python - 从 TKinter 小部件检索/取回命令回调函数

标签 python tkinter callback

我(出于一些复杂的设置原因)试图检索实际的 command来自 tkinter 小部件的回调函数,例如为按钮设置回调 b

import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='btn', command=lambda:print('foo'))
两个都
b['command']
b.cget('command')
我认为两者都相当于
b.tk.call(b._w, 'cget', '-command')
只会返回类似 "2277504761920<lambda\>" 的字符串而不是实际的命令功能。有没有办法获得实际的回调函数?

最佳答案

看着 tkinter.__init__.py :

class BaseWidget:
    ...
    def _register(self, func, subst=None, needcleanup=1):
        """Return a newly created Tcl function. If this
        function is called, the Python function FUNC will
        be executed. An optional function SUBST can
        be given which will be executed before FUNC."""
        f = CallWrapper(func, subst, self).__call__
        name = repr(id(f))
        try:
            func = func.__func__
        except AttributeError:
            pass
        try:
            name = name + func.__name__
        except AttributeError:
            pass
        self.tk.createcommand(name, f)
        if needcleanup:
            if self._tclCommands is None:
                self._tclCommands = []
            self._tclCommands.append(name)
        return name

class CallWrapper:
    """Internal class. Stores function to call when some user
    defined Tcl function is called e.g. after an event occurred."""
    def __init__(self, func, subst, widget):
        """Store FUNC, SUBST and WIDGET as members."""
        self.func = func
        self.subst = subst
        self.widget = widget
    def __call__(self, *args):
        """Apply first function SUBST to arguments, than FUNC."""
        try:
            if self.subst:
                args = self.subst(*args)
            return self.func(*args)
        except SystemExit:
            raise
        except:
            self.widget._report_exception()
我们得到 tkinter 将函数包装在 CallWrapper 中类(class)。这意味着如果我们得到所有的 CallWrapper我们可以恢复对象的功能。使用@hussic 的建议猴子修补 CallWrapper使用更易于使用的类,我们可以轻松获得所有 CallWrapper对象。
这是我用@hussic 的建议实现的解决方案:
import tkinter as tk

tk.call_wappers = [] # A list of all of the `MyCallWrapper` objects

class MyCallWrapper:
    __slots__ = ("func", "subst", "__call__")

    def __init__(self, func, subst, widget):
        # We aren't going to use `widget` because that can take space
        # and we have a memory leak problem
        self.func = func
        self.subst = subst
        # These are the 2 lines I added:
        # First one appends this object to the list defined up there
        # the second one uses lambda because python can be tricky if you
        # use `id(<object>.<function>)`.
        tk.call_wappers.append(self)
        self.__call__ = lambda *args: self.call(*args)

    def call(self, *args):
        """Apply first function SUBST to arguments, than FUNC."""
        try:
            if self.subst:
                args = self.subst(*args)
            return self.func(*args)
        except SystemExit:
            raise
        except:
            if tk._default_root is None:
                raise
            else:
                tk._default_root._report_exception()

tk.CallWrapper = MyCallWrapper # Monkey patch tkinter

# If we are going to monkey patch `tk.CallWrapper` why not also `tk.getcommand`?
def getcommand(name):
    for call_wapper in tk.call_wappers:
        candidate_name = repr(id(call_wapper.__call__))
        if name.startswith(candidate_name):
            return call_wapper.func
    return None

tk.getcommand = getcommand


# This is the testing code:
def myfunction():
    print("Hi")

root = tk.Tk()

button = tk.Button(root, text="Click me", command=myfunction)
button.pack()

commandname = button.cget("command")
# This is how we are going to get the function into our variable:
myfunction_from_button = tk.getcommand(commandname)
print(myfunction_from_button)

root.mainloop()
正如@hussic 在评论中所说,存在列表( tk.call_wappers )仅被附加到的问题。如果您有 .after,问题就会很明显。 tkinter 每次循环 .after被称为一个对象将被添加到列表中。要解决此问题,您可能需要使用 tk.call_wappers.clear() 手动清除列表。 .我把它改成使用 __slots__功能以确保它不会占用太多空间,但这并不能解决问题。

关于python - 从 TKinter 小部件检索/取回命令回调函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66668898/

相关文章:

javascript - 如何在 JetBrains IDE 中为闭包指定 "this"值的类型?

python - 有没有一种方法可以将图像保存在 OpenCV 执行期间创建的子文件夹中

python - 如何在 tkinter 的 ttk.Treeview 中取消选择行

python - 如何在回调函数中获取换行符(%s 或 .format)

python - 用 pandas 查找树中叶节点的所有祖先

python - 如何制作一个连接到 sqlite3 数据库的可执行 python tkinter 文件

node.js - Nodejs 5.4 中 => 符号是什么

python - 从 DynamoDB 获取项目时出现 "The provided key element does not match the schema"错误

用于作业调度的 Python 库,ssh

python - 获取 "actual"字符串的 Unicode 字符长度