Search code examples
pythonmatplotlibtkinterginput

Using matplotlib's ginput function with tkinter GUI


I have built a simple matplotlib plotting GUI in python using tkinter. Now I would like to to ginput to obtain coordinates from the plot. Unfortunately I keep receiving the following errors:

AttributeError: 'FigureCanvasTkAgg' object has no attribute 'manager'
Figure.show works only for figures managed by pyplot, normally created by pyplot.figure().

It seems that ginput doesn't work well with tkinter, I can get this code to work just fine if I don't use tkinter and simply produce a plot window from the command line.

I know there is an alternative way to do this sort of thing with event picking functions from matplotlib but I can't seem to translate the examples I've seen towards this functionality. Can anyone walk me through this?

import numpy as np
from matplotlib.pyplot import *
from Tkinter import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg


class App:

    def __init__(self, master):

        global frame
        frame = Frame(master)
        frame.pack(side=TOP)   

        self.quitbutton = Button(frame, text="QUIT", fg="red", command=frame.quit)
        self.quitbutton.grid(row=4,column=0,sticky=W)       

        self.hi_there = Button(frame, text="LOAD",command=self.loadfile)
        self.hi_there.grid(row=0,column=0,sticky=W)   

        self.cutbutton = Button(frame, text="CUT", fg="purple",command=self.cut)
        self.cutbutton.grid(row=1,column=0,sticky=W)        

        global canvas, ax, f

        f = Figure(figsize=(20,4))
        ax = f.add_subplot(111)
        ax.set_xlabel('Time(s)',fontsize=20)
        ax.set_ylabel('Current(nA)',fontsize=20)
        canvas = FigureCanvasTkAgg(f, master=root)
        canvas.show()
        canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1)

        toolbar1 = NavigationToolbar2TkAgg( canvas, root )
        toolbar1.update()
        f.tight_layout()


    def loadfile(self):
        ax.clear()
        self.data=rand(1000)
        self.t=np.arange(0,len(self.data))       
        ##############################################plot data
        self.baseline=np.median(self.data)  
        self.var=2*std(self.data)
        ax.plot(self.t,self.data,'b')

        ax.set_xlabel('Time(s)',fontsize=20)
        ax.set_ylabel('Current(nA)',fontsize=20)
        ax.axhline(linewidth=2, y=self.baseline, color='g')
        ax.set_xlabel('Time(s)',fontsize=20)
        ax.set_ylabel('Current(nA)',fontsize=20)
        canvas.draw()

    def cut(self): 
        pts=np.array(f.ginput(2))
        pts=pts[:,0]
        print pts
        self.data=np.delete(self.data,pts)

        ax.clear()
        self.baseline=np.median(self.data)    
        self.t=np.arange(0,len(self.data))
        ax.plot(self.t,self.data,'b')
        ax.axhline(linewidth=2, y=self.baseline, color='g')
        canvas.draw()




root = Tk()

app = App(root)

root.mainloop()
root.destroy()

EDIT: it seems that the problem is in the distinction between Figure and figure

ginput seems to work with figure() but not Figure(). And tkinter only works with Figure...

is there a workaround for this?


Solution

  • Once you are inside python/Tkinter .mainloop(), base your Tkinter GUI input on mouse-click Event, rather than on MATLAB/pyplot .ginput()

    No serious control-system ( and the Tkinter .mainloop() is a pretty serious one ) likes to handover it's control-reins to any other, competing ( == blocking ) sub-system.

    Tested to work with:

    class App( Frame ):                                          # The user interface:
    
        def __init__( self, master = None ):
    
            Frame.__init__( self, master )
            self.fig        = Figure( ( 6, 6 ), dpi = 100 )    
            canvas          = FigureCanvasTkAgg( self.fig, master = self )
            # ---------------------------------------------------# cover Tk.root
            self.bind(                     "<Button-1>",         # Tk Event type
                                           self.showXY_handler   #    handler 2 call
                                           )
            # ----------------------------------------------------------------------
            canvas.get_tk_widget().grid(   row        = 0,       # this adds a plot
                                           column     = 0,       # on Tk.root .grid()
                                           columnspan = 4        # geometry manager
                                           )
            # ---------------------------------------------------cover graph area
            canvas.get_tk_widget().bind(   "<Button-1>",
                                           self.showXY_handler
                                           )
            # -----------------------------------------------------------
    
    
         #FINALLY THE INTERFACE TO A DEMO-HANDLER METHOD:
         def showXY_handler( self,  aHandledEVENT ):
             print aHandledEVENT.x, aHandledEVENT.y
    

    You may also rather use Class instance variables rather than global-s

    # global canvas, ax, f                  # rather to be avoided use of global-s
    self.canvas =                           # instance variables, Class-wide visible  
    self.ax     =
    self.f      =