2017-06-04 15 views
1

我想獲得使用Tkinter的應用程序的一些經驗。這個練習應用程序將在兩個面板圖中顯示一些數據,有一個按鈕可以創建新數據,並且可以選擇和突出顯示數據區域。這是最後一部分,我陷入困境。我有一個類(PointSelector),它將兩個軸(axarr)的列表作爲輸入,一個用於選擇哪個將被高亮顯示的整數(ax2select),圖形(圖),畫布(canv),要突出顯示的範圍數量(NumRanges)和數據(xdat和ydat)。如何使用tkinter和matplotlib從圖中選擇點而不關閉它

除非在mpl_connect行之後使用plt.show()命令,否則代碼不會等待click事件。但是,如果我不使用plt.show(),一個新的繪圖窗口出現(我不想)和matplotlib將阻塞,直到所有的窗口都關閉(我希望窗口繼續開放後大腿高亮完成)。

有沒有辦法來堵塞一旦mpl_disconnect被稱爲釋放matplotlib(也沒有第二個窗口出現即使用plt.plot())?或者有沒有辦法使用matplotlib的事件系統而不使用命令plt.show()?

import Tkinter as tk 
import numpy as np 
import matplotlib 
matplotlib.use('TkAgg') 

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 
import matplotlib.pyplot as plt 
import tkFileDialog 

LARGE_FONT = ("Verdana", 12) 


class PointSelector(): 
''' 
classdocs 
''' 
def __init__(self, axarr, ax2select, fig, canv, NumRanges, xdat, ydat): 
    ''' 
    Constructor 
    ''' 
    self.axarr = axarr 
    self.ax2select = ax2select 
    self.fig = fig 
    self.canv = canv 
    self.NumRanges = NumRanges 
    SelectedPoints = [] 
    self.Ranges = [] 
    self.xdat = xdat 
    self.ydat = ydat 
    self.Nselected = 0 
    self.SelectedL = False 
    self.SelectedR = False 
    self.LeftArr = [] 
    self.RightArr = [] 
    self.cid = canv.mpl_connect('button_press_event', self.onclick) 
#   plt.ion() 
#   plt.show() 
    canv.show() 




    print 'Done With ALL!' 


    def PlotRange(self,rng): 
     x = self.xdat[range(rng[0],rng[1]+1)] 
     y = self.ydat[range(rng[0],rng[1]+1)] 
     self.axarr[self.ax2select].plot(x,y,color='r') 

     self.canv.draw() 

    def PlotPoint(self,x,y): 
     self.axarr[self.ax2select].scatter(x,y,color='r') 
     self.canv.draw() 

    def onclick(self, event): 
     print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % 
     (event.button, event.x, event.y, event.xdata, event.ydata)) 

     if event.inaxes == self.axarr[self.ax2select]: 

      xval = np.argmin(np.abs(event.xdata-self.xdat)) 
      if self.SelectedL: 
       self.RightArr.append(xval) 
       self.SelectedR = True 
       self.PlotPoint(self.xdat[xval], self.ydat[xval]) 
      else: 
       self.LeftArr.append(xval) 
       self.SelectedL = True 
       self.PlotPoint(self.xdat[xval], self.ydat[xval]) 

      if self.SelectedL and self.SelectedR: 
       self.SelectedL = False 
       self.SelectedR = False 
       self.Nselected += 1 
       self.PlotRange([self.LeftArr[-1], self.RightArr[-1]]) 
       if self.Nselected == self.NumRanges: 
        for i in range(len(self.LeftArr)): 
         self.Ranges.append([self.LeftArr[i], self.RightArr[i]]) 
        self.canv.mpl_disconnect(self.cid) 
        print 'Done With Selection' 


     else: 
      print 'Outside Window' 

class Driver(tk.Tk): 
''' 
classdocs 
''' 
    def __init__(self, *args, **kwargs): 
    ''' 
    Constructor 
    ''' 
     tk.Tk.__init__(self, *args, **kwargs) 
     container = tk.Frame(self) 
     container.pack(side="top", fill="both",expand=True) 
     container.grid_rowconfigure(0, weight=1) 
     container.grid_columnconfigure(0, weight=1) 
     self.frames = dict() 
     F = StartPage 
     frame = F(container, self) 
     self.frames[F] = frame 
     frame.grid(row=0,column=0,sticky="nsew") 
     self.show_frame(StartPage) 
    def show_frame(self, cont): 
     frame = self.frames[cont] 
     frame.tkraise() 

class StartPage(tk.Frame): 
''' 
classdocs 
''' 


    def __init__(self, parent, controller): 
    ''' 
    Constructor 
    ''' 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 
     label = tk.Label(self,text="Start Page", font=LARGE_FONT) 
     label.pack(pady=10,padx=10) 

     quitButton = tk.Button(self, text="Quit",command=self._quit) 
     quitButton.pack(side="bottom") 



     NewDataButton = tk.Button(self, text="Get New Data",command=self.GetNewData) 
     NewDataButton.pack(side="top") 


     menu = tk.Menu(parent) 
     controller.config(menu=menu) 

     submenuFile = tk.Menu(menu) 
     menu.add_cascade(label="File",menu=submenuFile) 
     submenuFile.add_command(label='Open File', command=self.onOpen) 
     submenuFile.add_command(label='Load Configuration File', command=self.onOpen) 
     submenuFile.add_separator() 

     submenuFile.add_command(label='Exit', command=self._quit) 




     submenuContinuum = tk.Menu(menu) 
     menu.add_cascade(label="Continuum",menu=submenuContinuum) 



     canvas_width = 100 
     canvas_height = 100 
     canv = tk.Canvas(self,width=canvas_width,height=canvas_height) 
     canv.pack() 

     self.x = np.linspace(0.0,4.0*np.pi,100) 

     self.fig = plt.figure(figsize=(6,6)) 

     self.axarr = [] 
     self.axarr.append(plt.subplot(211)) 
     self.axarr.append(plt.subplot(212)) 


     self.canv = FigureCanvasTkAgg(self.fig,master=self) 
     self.canv.show() 
     self.canv.get_tk_widget().pack() 
     self.canv._tkcanvas.pack(side=tk.TOP,fill=tk.BOTH,expand=1) 
     self.GetNewData() 

     self.txt=tk.Text(self) 
     submenuContinuum.add_command(label='Select Continuum', 
           command=lambda:PointSelector(self.axarr, 0, self.fig, self.canv, 2, 
                  self.x, self.y)) 
     submenuContinuum.add_command(label='Remove Continuum', command=donothing) 

    def GetNewData(self): 
     rnum = (np.random.rand(1)) 
     print rnum 
     self.y = np.sin(self.x)*(np.random.rand(1)*4.0)*(self.x)**rnum 
     self.dy = np.gradient(self.y, self.x[1]-self.x[0]) 
     self.DrawData() 
    def DrawData(self): 
     self.axarr[0].clear() 
     self.axarr[1].clear() 
     self.axarr[0].plot(self.x,self.y) 
     self.axarr[1].plot(self.x,self.dy) 

     self.canv.draw() 

    def onOpen(self): 

     ftypes = [('Python files', '*.py'), ('All files', '*')] 
     dlg = tkFileDialog.Open(self, filetypes = ftypes) 
     fl = dlg.show() 

     if fl != '': 
      text = self.readFile(fl) 
      self.txt.insert('end', text) 

    def readFile(self, filename): 

     f = open(filename, "r") 
     text = f.read() 
     return text 

    def _quit(self): 
     self.controller.quit() 
     self.controller.destroy() 

def donothing(): 
    print "nothing" 


if __name__ == '__main__': 
    app = Driver() 
    app.mainloop() 

回答

0

首先,它是正確的,你不希望使用plt.show()plt.ion()在你的GUI,因爲他們將與GUI窗口乾擾。
你可能也想擺脫canv.show()PointSelector的初始化方法(僅僅因爲我不知道它應該在那裏做什麼,而__init__不是在程序中需要重繪東西的方法)。

所以假設點選擇是,否則罰款,實際的問題來自於它是一個匿名函數實例化的線

.add_command(label='...', command=lambda:PointSelector(...)) 

。這裏的問題是,你現在有沒有在你的程序,它會丟失,一旦其__init__結束後使用該實例的方式。

與此一起出現問題,你鬆散對matplotlib回調的引用。
的matplotlib event handling tutorial狀態:

畫布只保留到回調弱引用。因此,如果回調是類實例的方法,則需要保留對該實例的引用。否則,實例將被垃圾回收,回調將消失。

因此,爲了能夠在程序中,使用PointSelector你需要將其分配到一個類變量,例如self.selector = PointSelector(..)。這可以在菜單中稱爲命令的方法內完成。然後

一個可能的解決辦法是這樣的:

class StartPage(tk.Frame): 

    #... 

    def __init__(self, parent, controller): 
     submenuContinuum.add_command(label='Select Continuum', 
           command=self.registerSelector) 
     submenuContinuum.add_command(label='Remove Continuum', command=donothing) 

    def registerSelector(self): 
     self.selector = PointSelector(self.axarr, 0, self.fig, 
             self.canv, 2, self.x, self.y) 
+0

謝謝,這個固定。 – BSM

+0

如果這樣可以解決問題,則可能[接受](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)吧,這樣的問題不會留下未解。 – ImportanceOfBeingErnest

相關問題