簡體   English   中英

Python tkinter 文本修改回調

[英]Python tkinter text modified callback

在 python 2.7 中,每次在 Tkinter Text 小部件中更改某些內容時,我都會嘗試獲取回調。

該程序根據此處找到的代碼使用多個幀: 在 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, 'edit', 'modified', 0) _tkinter.TclError: bad option "edit": must be cget or configure

在回調示例代碼中,“編輯”工作正常。

編輯:這是工作代碼

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()

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

讓我們首先創建一個自定義文本小部件類,您將像使用任何其他文本小部件一樣使用它:

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. 最后它返回實際小部件命令的結果

您可以像使用任何其他 Text 小部件一樣使用此小部件,並且可以綁定到<<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()

我在我的代碼中集成了上面的<<TextModified >> 示例並且它運行得很好,除了它干擾了一些edit_modified()命令。

幸運的是,tkinter 文本窗口有一個記錄不佳的功能,它同樣好並且與edit_modified()獲取或設置命令完全兼容:預定義的<<Modified >> 標簽。 您甚至不必創建它,它開箱即用。

以下是我的代碼的相關部分:

刪除了“self”前綴,可能需要進行一些調整

將其放入您的文本小工具代碼中:

title = set_title(fname, numbr)

text.bind("<<Modified>>", lambda dummy: save_indicator(title))

確保這些功能可見:

def set_title(fname, numbr):  
    "Creates a window title showing the save indicator,"  
    "the file name and a window number"  
    fname = strip_path(fname)  
    if not fname:  
        fname = "(New Document)"  
    return "+ {} - Window no.{}".format(fname, numbr)  

def strip_path(fname):  
    return os.path.split(fname)[-1]

def save_indicator(title, event=None):  
    "Update the window title"  
    titre = toggle_star(title)  
    text.winfo_toplevel().title(title)  

def toggle_star(title):  
    "Change the first character of the title"  
    chr='+'; chr0='x'  
    if text.edit_modified():  
        title = chr0 + title[1:]  
    else:  
        title = chr + title[1:]  
    return title  

這是一個帶有預定義<<Modified >> 標簽的完整工作示例:

def toggle_star(title):  
    "Change the color of the star in the title bar"  
    chr='+'; chr0='x'  
    if text.edit_modified():  
            title = chr0 + title[1:]  
    else:  
            title = chr + title[1:]  
    return title  

def set_title(fname, winno):  
    "Put save indicator, file name and window number in the title"  
    if not fname:  
            fname = "(New Document)"  
    return "+ {} - Window no.{}".format(fname, winno)  

def mockSave(title, event=None):  
    title = toggle_star(title)  
    root.winfo_toplevel().title(title)  
    text.edit_modified(0)  

def read_ed_mod():  
    print("text.edit_modified()=", text.edit_modified())  

def onModification(title, event=None):  
    title = toggle_star(title)  
    root.winfo_toplevel().title(title)  

from tkinter import *  

fname = 'blabla.txt'  
winno = 1 ;  

root = Tk()  
label = Label(root, anchor="w")  
text = Text(root, width=40, height=4)  
label.pack(side="bottom", fill="x")  
text.pack(side="top", fill="both", expand=True)  
Button(root, text='Mock Save', command= lambda: mockSave(title)).pack(side=LEFT)  
Button(root, text='Read ed_mod', command= lambda: read_ed_mod()).pack(side=RIGHT)  
text.bind('<<Modified>>', lambda event: onModification(title))  

title = set_title(fname, winno)  
root.winfo_toplevel().title(title)  
text.edit_modified(0)  

root.mainloop()  

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM