Skip to main content
 首页 » 编程设计

python之在 MATPLOTLIB 中绘制多个子图时条形图损坏

2025年05月04日8kerrycode

我创建了一个类,允许用户将多个图表添加到 MATPLOTLIB 窗口。这些可以是折线图或条形图。它还具有以下功能:当图表已添加到窗口(从 rowID 标识)而不是绘制新图时,它将替换旧图中的数据。即它允许更新(动画)

这对于折线图非常有效,但在绘制多个条形图时我会出错。该类看起来像:

  import math 
 
  class TFrmPlot(): 
 
    def __init__(self, point_lists, deleteCallback, plotType, rowID):                     
            import matplotlib  
            matplotlib.interactive( True ) 
            matplotlib.use( 'WXAgg' )   
 
            import matplotlib.pyplot as plt 
            self.plt = plt 
            self.fig = plt.figure()       
            self.fig.canvas.mpl_connect('close_event', self.on_close)     
 
            import matplotlib.axes as ax     
            self.ax = ax 
 
            self.deleteCallback = deleteCallback 
            self.chartArray = []               
            self.addChart(point_lists, plotType, rowID) 
 
        def close(self):     
            self.plt.close('all') 
            #self.fig.close() 
 
        def replaceChartDataIfChartExists(self, point_lists, rowID): 
            if rowID==0: 
                pass 
            for chart in self.chartArray: 
                for plot in chart.plots: 
                    if plot.rowID == rowID: 
                        plot.points = point_lists                                
                        if plot.plotType=="Point":                          
                            plot.plotItem.set_data(point_lists[0],point_lists[1])                           
                            chart.subPlot.draw_artist(plot.plotItem)                         
                            self.fig.canvas.blit(chart.subPlot.bbox)  
                        else:                       
                            for rect, h in zip(plot.plotItem, point_lists[1]): 
                                rect.set_height(h)    
                        chart.subPlot.relim()                      
                        chart.subPlot.autoscale_view(True,True,True)                                                        
                        self.plt.draw() 
                        return True 
            return False     
 
        def addChart(self, point_lists, plotType, rowID): 
            self.chartArray.append(TChart(rowID,plotType,point_lists)) 
            self._drawAll()  
 
        def addPlot(self, point_lists, plotType, rowID):            
            chartNum = len(self.chartArray) 
            self.chartArray[chartNum-1].plots.append(TPlot(rowID,plotType,point_lists))   
            self._drawAll() 
 
        def on_close(self, event): 
            self.deleteCallback() 
 
        def _drawAll(self):      
            self.plt.clf() 
            numSubPlots = len(self.chartArray) 
            numCols = self._noCols(numSubPlots) 
            IndexConverter = TIndexConverter(numCols) 
            subPlot = None 
            for chartIndex in range(0,numSubPlots): 
                if numSubPlots==1:                 
                    subPlot = self.fig.add_subplot(1,1,1) 
                elif numSubPlots==2:                              
                    subPlot = self.fig.add_subplot(1,2,chartIndex+1) 
                else: 
                    subPlot = self.fig.add_subplot(2,numCols,IndexConverter._getSubPlotIndex(chartIndex)) 
                subPlot.relim()            
                subPlot.autoscale_view(True,True,True) 
                self.chartArray[chartIndex].subPlot = subPlot 
                self._drawSubs(self.chartArray[chartIndex])                    
            self.plt.show()  
 
        def _drawSubs(self, chart): 
            for plot in chart.plots: 
                if plot.plotType=="Point":           
                    chart.subPlot.plot(plot.points[0],plot.points[1]) 
                    plot.plotItem = chart.subPlot.lines[len(chart.subPlot.lines)-1] 
                else: 
                    kwargs = {"alpha":0.5} 
                    plot.plotItem = chart.subPlot.bar(plot.points[0],plot.points[1], width=self._calculateleastDiff(plot.points[0]), **kwargs)      
 
        def _noCols(self, numSubPlots): 
            return math.ceil(float(numSubPlots)/2.0)   
 
        def _calculateleastDiff(self, xValues): 
            xValues2 = sorted(xValues) 
            leastDiff = None 
            lastValue = None 
            for value in xValues2: 
                if lastValue is not None:  
                    diff = value-lastValue             
                    if leastDiff is None or diff < leastDiff: 
                        leastDiff = diff 
                lastValue = value  
            return leastDiff 

总结起来有点长:

addChart——基本上是添加一个新的子图

addPlot -- 向现有的子图添加新的线或条

replaceChartDataIfChartExists -- 如果 ID 已经存在则刷新数据

我使用的虚拟数据只是连续绘制正梯度和负梯度线。然而,我的地 block 可能会进入一个/一些或所有条形图被破坏的状态。它看起来几乎像 x/y 轴已经旋转,各个条形图不是从 x 轴开始的。问题是间歇性的;有时我会按预期获得几个情节。一旦情节被破坏,所有 future 的更新都会被破坏。

按要求,剩余代码:

class TIndexConverter():     
    def __init__(self, numCols):                                       
        self.evenCounter = 0 
        self.oddCounter = numCols 
 
    def _getSubPlotIndex(self, arrayIndex): 
        if arrayIndex%2==0: 
            self.evenCounter += 1 
            return self.evenCounter 
        else:         
            self.oddCounter += 1 
            return self.oddCounter 
 
 
class TChart(): 
    def __init__(self, rowID, plotType, point_lists): 
        self.subPlot = None 
        self.plots = [TPlot(rowID, plotType, point_lists)] 
 
class TPlot(): 
    def __init__(self, rowID, plotType, point_lists):                      
        self.plotItem = None         
        self.plotType = plotType   
        self.rowID = rowID            
        self.points = point_lists 

一些客户端代码:

def _updateData(self, state, data):  
    if self.plot is not None: 
        if not self.plot.replaceChartDataIfChartExists(data, state.comm.rowID): 
            if self.createNewChart == True: 
                self.plot.addChart(data, state.setting.plotType, state.comm.rowID)     
            else: 
                self.plot.addPlot(data, state.setting.plotType, state.comm.rowID) 

请您参考如下方法:

这可能相关也可能不相关,但您可以将 _calculateleastDiff 替换为以下内容。

def _calculateleastDiff(self, xValues): 
    return np.min(np.diff(sorted(xValues))) 

这段代码的功能方式过于复杂。我怀疑您可以去掉 TChart 和 TPlot 类。我会保留一个数据列表列表(所以 [ [subplot1_data1,subplot1_data2],[subplot2_data1],[...]])一个 axes 对象列表,以及一个列表,用于跟踪您想要的情节类型。

此外,尽量不要使用 matplotlib 中已经使用的名称,这会使您的代码更难阅读。