2016-11-15 64 views
0

在python 2.7中,我試圖每次在Tkinter Text小部件中更改某個對象時獲得一個回調。Python tkinter文本修改回調

該方案採用基於代碼在這裏找到了多個幀:Switch between two frames in tkinter

回調部分從下面的例子中採取:http://code.activestate.com/recipes/464635-call-a-callback-when-a-tkintertext-is-modified/

兩個代碼正常工作分開,但結合這兩個對我來說很難。 這是我儘可能使用裸骨代碼的嘗試。

import Tkinter as tk 

class Texter(tk.Tk): 

    def __init__(self, *args, **kwargs): 
     tk.Tk.__init__(self, *args, **kwargs) 

     container = tk.Frame(self) 
     container.pack() 

     self.frames = {} 

     for F in (ConnectPage, EditorPage): 
      frame = F(container, self) 
      self.frames[F] = frame 
      frame.grid(row=0, column=0, sticky="nsew") 

     page_name = EditorPage.__name__ 
     self.frames[page_name] = frame 
     self.show_frame(ConnectPage) 


    def show_frame(self, cont): 
     frame = self.frames[cont] 
     frame.tkraise() 

    def get_page(self, page_name): 
     return self.frames[page_name] 


class ConnectPage(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 

     button1 = tk.Button(self, text="SecondPage", 
          command=lambda: controller.show_frame(EditorPage)) 
     button1.grid(row=2, column=3, padx=15) 


class EditorPage(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 

     self.text = tk.Text(self, height=25, width=80) 
     self.text.grid(column=0, row=0, sticky="nw") 

     button2 = tk.Button(self, text="FirstPage", 
          command=lambda: controller.show_frame(ConnectPage)) 
     button2.grid(row=2, column=3, padx=15) 

     self.clearModifiedFlag() 
     self.bind_all('<<Modified>>', self._beenModified) 

    def _beenModified(self, event=None): 
     if self._resetting_modified_flag: return 

     self.clearModifiedFlag() 
     print("Hello!") 
     #self.beenModified(event) 

    def clearModifiedFlag(self): 
     self._resetting_modified_flag = True 

     try: 
      self.tk.call(self._w, 'edit', 'modified', 0) 

     finally: 
      self._resetting_modified_flag = False 


if __name__ == '__main__': 
    gui = Texter() 
    gui.mainloop() 

我試着從回調的例子中只考慮必要的部分。 代碼確實做了一個回調(如果self.tk.call(self._w,'edit','modified',0)行被註釋掉了)當文本被修改時,但重置修改的標誌不起作用,所以只有第一次修改被註冊。

此刻,我得到以下錯誤:
線67,在clearModifiedFlag self.tk.call(self._w, '編輯', '修改',0) _tkinter.TclError:壞的選擇「編輯「:必須cget或配置

在回調示例代碼」編輯「工作正常。

編輯:這是工作的代碼

import Tkinter as tk 

class Texter(tk.Tk): 

    def __init__(self, *args, **kwargs): 
     tk.Tk.__init__(self, *args, **kwargs) 

     container = tk.Frame(self) 
     container.pack() 

     self.frames = {} 

     for F in (ConnectPage, EditorPage): 
      frame = F(container, self) 
      self.frames[F] = frame 
      frame.grid(row=0, column=0, sticky="nsew") 

     page_name = EditorPage.__name__ 
     self.frames[page_name] = frame 
     self.show_frame(ConnectPage) 


    def show_frame(self, cont): 
     frame = self.frames[cont] 
     frame.tkraise() 

    def get_page(self, page_name): 
     return self.frames[page_name] 


class ConnectPage(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 

     button1 = tk.Button(self, text="SecondPage", 
          command=lambda: controller.show_frame(EditorPage)) 
     button1.grid(row=2, column=3, padx=15) 


class EditorPage(tk.Frame): 

    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 

     self.text = CustomText(self, height=25, width=80) 
     self.text.grid(column=0, row=0, sticky="nw") 
     self.text.bind("<<TextModified>>", self.onModification) 

     button2 = tk.Button(self, text="FirstPage", 
          command=lambda: controller.show_frame(ConnectPage)) 
     button2.grid(row=2, column=3, padx=15) 

    def onModification(self, event): 
     print("Yellow!") 


class CustomText(tk.Text): 
    def __init__(self, *args, **kwargs): 
     """A text widget that report on internal widget commands""" 
     tk.Text.__init__(self, *args, **kwargs) 

     # create a proxy for the underlying widget 
     self._orig = self._w + "_orig" 
     self.tk.call("rename", self._w, self._orig) 
     self.tk.createcommand(self._w, self._proxy) 

    def _proxy(self, command, *args): 
     cmd = (self._orig, command) + args 
     result = self.tk.call(cmd) 

     if command in ("insert", "delete", "replace"): 
      self.event_generate("<<TextModified>>") 

     return result 

if __name__ == '__main__': 
    gui = Texter() 
    gui.mainloop() 

回答

1

我提出一個簡單的方法。您可以爲小部件設置一個代理,並且在該代理中,您可以檢測何時插入或刪除了任何內容。您可以使用該信息生成一個虛擬事件,該事件可以像任何其他事件一樣綁定。

首先讓我們來創建一個自定義文本組件類,你會使用像任何其他文本組件:

import Tkinter as tk 

class CustomText(tk.Text): 
    def __init__(self, *args, **kwargs): 
     """A text widget that report on internal widget commands""" 
     tk.Text.__init__(self, *args, **kwargs) 

     # create a proxy for the underlying widget 
     self._orig = self._w + "_orig" 
     self.tk.call("rename", self._w, self._orig) 
     self.tk.createcommand(self._w, self._proxy) 

    def _proxy(self, command, *args): 
     cmd = (self._orig, command) + args 
     result = self.tk.call(cmd) 

     if command in ("insert", "delete", "replace"): 
      self.event_generate("<<TextModified>>") 

     return result 

本例中的代理做了三兩件事:

  1. 首先,它調用實際的小部件命令,傳入所有收到的參數。
  2. 下它會產生一個事件對於每次插入,每刪除
  3. 然後再生成一個虛擬的事件
  4. 最後返回實際組件命令的結果

您可以準確地使用這個小工具像任何其他文本小部件一樣,還可以綁定到<<TextModified>>

例如,如果你想顯示文本組件中的字符數,你可以做這樣的事情:

root = tk.Tk() 
label = tk.Label(root, anchor="w") 
text = CustomText(root, width=40, height=4) 

label.pack(side="bottom", fill="x") 
text.pack(side="top", fill="both", expand=True) 

def onModification(event): 
    chars = len(event.widget.get("1.0", "end-1c")) 
    label.configure(text="%s chars" % chars) 

text.bind("<<TextModified>>", onModification) 

root.mainloop() 
+0

謝謝你的答案布萊恩,就像一個魅力! :) 你還可以告訴這種技術如何被調用(如果這有一個名稱)。例如像多幀方法使用MVC架構。 另一個問題,請您解釋_self._w_是什麼,它似乎很重要,但無法找到關於它的信息。 – additive

+0

@additive:'self._w'只是小部件的內部名稱。代碼可以使用'str(self)'代替。我不確定整個技術是否有名稱,儘管它使用代理模式作爲實現的一部分。 –