Search code examples
matplotlibdynamicpyqtlines

change matplotlib data in gui


I've developed an gui with python pyqt. There I have a matplotlib figure with x,y-Data and vlines that needs to change dynamically with a QSlider.

Right now I change the data just with deleting everything and plot again but this is not effective

This is how I do it:

 def update_verticalLines(self, Data, xData, valueSlider1, valueSlider2, PlotNr, width_wg):
    if PlotNr == 2:
        self.axes.cla()
        self.axes.plot(xData, Data, color='b', linewidth=2)             
        self.axes.vlines(valueSlider1,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
        self.axes.vlines(valueSlider2,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
        self.axes.text(1,0.8*max(Data),str(np.round(width_wg,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
        self.axes.text(1,0.6*max(Data),"Pos1: "+str(round(valueSlider1,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
        self.axes.text(1,0.4*max(Data),"Pos2: "+str(round(valueSlider2,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
        self.axes.grid(True)
        self.draw()

"vlines" are LineCollections in matplotlib. I searched in the documentation but could not find any hint to a function like 'set_xdata' How can I change the x value of vertical lines when they are already drawn and embedded into FigureCanvas?

I have the same problem with changing the x and y data. When trying the known functions of matplotlib like 'set_data', I get an error that AxisSubPlot does not have this attribute.

In the following is my code for the FigureCanvas Class. The def update_verticalLines should only contain commands for changing the x coord of the vlines and not complete redraw.

class MplCanvas(FigureCanvas):    

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)   
        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        
class DynamicMplCanvas(MplCanvas):    

    def __init__(self, *args, **kwargs):
        
        MplCanvas.__init__(self, *args, **kwargs)
        

    def update_figure(self, Data, xData, Path, PlotNr):
        
        if PlotNr==0:
            self.axes.cla()
            self.style1(self.axes)
            self.axes.grid(True)  
            self.axes.plot(xData, Data, color='r', linewidth=2)  
            self.axes.set_xlim(0,max(xData))  
            self.axes.set_ylim(1.1*min(Data),1.1*max(Data))              
            self.draw()
            
        if PlotNr==1:
            self.axes.cla()
            self.style2(self.axes)
            self.axes.grid(True)
            self.axes.plot(xData, Data, color='b', linewidth=2) 
            self.axes.set_xlim(0,max(xData))  
            self.axes.set_ylim(1.1*min(Data),1.1*max(Data)) 
            self.vLine1 = self.axes.axvline(color='r')#vlines(len(Data)/4,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)         
            self.vLine2 = self.axes.vlines(len(Data)/2,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
            #self.vLine1.set_xdata(0)
            self.draw() 
       
            
    def update_verticalLines(self, Data, xData, valueSlider1, valueSlider2, PlotNr, width_wg):
        if PlotNr == 2:
            self.axes.cla()
            self.axes.plot(xData, Data, color='b', linewidth=2) 
            self.axes.set_ydata(xData)            
            self.axes.vlines(valueSlider1,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
            self.axes.vlines(valueSlider2,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
            self.axes.text(1,0.8*max(Data),str(np.round(width_wg,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
            self.axes.text(1,0.6*max(Data),"Pos1: "+str(round(valueSlider1,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
            self.axes.text(1,0.4*max(Data),"Pos2: "+str(round(valueSlider2,2))+u"µm", fontsize=16, bbox=dict(facecolor='m', alpha=0.5))
            self.axes.grid(True)
            self.draw()
            #test=self.axes
            #self.vLine1 = plt.vlines(0,min(Data),max(Data),color='r',linewidth=1.5, zorder = 4)
            #help(test)
            #print test.get_axes()
            #print test.properties()
            #test.set_axes()
            #return test
            self.style2(self.axes)
            self.axes.relim()
            self.axes.autoscale_view(True,True,True)
            self.draw()  
        if PlotNr == 1:
            self.axes.cla()
            self.axes.plot(xData, Data, color='r', linewidth=2)             
            self.axes.vlines(valueSlider1,min(Data),max(Data),color='b',linewidth=1.5, zorder = 4)
            self.axes.vlines(valueSlider2,min(Data),max(Data),color='b',linewidth=1.5, zorder = 4)
            self.axes.grid(True)
            self.style1(self.axes)
            self.axes.relim()
            self.axes.autoscale_view(True,True,True)
            self.draw()  

Thanks


Solution

  • When you create the vlines, save a reference to them, e.g.

    self.my_vlines = self.axes.vlines(...)
    

    so that when you want to change them, you can just remove and replace them, e.g.

    self.my_vlines.remove()
    self.my_vlines = self.axes.vlines(...)
    
    # Redraw vline
    self.axes.draw_artist(self.my_vlines)
    # Add newly-rendered lines to drawing backend
    self.update()
    # Flush GUI events for figure
    self.flush_events()
    

    By the way, in the future you should try your best to pare down your code sample to just the essential parts. Having a lot of unnecessary sample code makes it hard to understand your question. :)