Python Tkinter文本修改后的回调 [英] Python tkinter text modified callback

查看:147
本文介绍了Python Tkinter文本修改后的回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在python 2.7中,每次Tkinter Text小部件中发生更改时,我都试图获取回调.

In python 2.7, I am trying to get a callback every time something is changed in the Tkinter Text widget.

程序基于以下代码使用多个帧:在tkinter中的两个帧之间切换

The program uses multiple frames based on code found here: Switch between two frames in tkinter

回调部分摘自以下示例: http://code.activestate.com/recipes/464635-call-a-callback-when-a-tkintertext-is-modified/

The callback part is taken from the following example: http://code.activestate.com/recipes/464635-call-a-callback-when-a-tkintertext-is-modified/

两种代码都可以单独很好地工作,但是将这两种代码结合起来对我来说很困难. 这是我尝试使用尽可能简单的代码.

Both codes work fine separately, but combining those two is difficult for me. Here is my attempt with as bare bones code as possible.

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)行被注释掉),但会重置已修改的文本标记不起作用,因此只有第一次修改被注册.

I tried taking only the necessary parts from the callback example. The code does do a callback (if self.tk.call(self._w, 'edit', 'modified', 0) line is commented out) when the text is modified, but resetting the modified flag does not work, so only the first modification is registered.

此刻,我收到以下错误:
第67行,在clearModifiedFlag中 self.tk.call(self._w,'edit','modified',0) _tkinter.TclError:错误的编辑"选项:必须是cget或配置

At the moment I get the following error:
line 67, in clearModifiedFlag self.tk.call(self._w, 'edit', 'modified', 0) _tkinter.TclError: bad option "edit": must be cget or configure

在回调示例代码中,"edit"工作正常.

In the callback example code "edit" works fine.

编辑:这是有效代码

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

推荐答案

我建议使用一种更简单的方法.您可以为窗口小部件设置代理,并在该代理内可以检测到何时插入或删除了任何内容.您可以使用这些信息来生成虚拟事件,该事件可以像其他任何事件一样绑定.

I suggest a simpler approach. You can set up a proxy for the widget, and within that proxy you can detect whenever anything was inserted or deleted. You can use that information to generate a virtual event, which can be bound to like any other event.

首先创建一个自定义文本窗口小部件类,您将像使用其他任何文本窗口小部件一样使用该类:

Let's start by creating a custom text widget class, which you will use like any other text widget:

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

此示例中的代理执行三件事:

The proxy in this example does three things:

  1. 首先,它调用实际的小部件命令,并传递它收到的所有参数.
  2. 接下来,它会为每个插入和删除操作生成一个事件
  3. 然后它会生成一个虚拟事件
  4. 最后,它返回实际小部件命令的结果

您可以像使用任何其他文本"小部件一样完全使用此小部件,并具有可以绑定到<<TextModified>>的附加好处.

You can use this widget exactly like any other Text widget, with the added benefit that you can bind to <<TextModified>>.

例如,如果您想在文本小部件中显示字符数,则可以执行以下操作:

For example, if you wanted to display the number of characters in the text widget you could do something like this:

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

这篇关于Python Tkinter文本修改后的回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆