python - 鼠标事件中的 tkinter wait_variable

标签 python tkinter

我正在尝试对 GUI 进行一些实时更新,但变量似乎在事件发生后更新,这意味着它会更新之前按下的按钮。我知道它与 wait_variable() 函数有关,但文档很模糊,而且这里关于它的另一篇文章似乎对我没有帮助。相关代码如下:

编辑:工作示例

from tkinter import *
import json
import os

class GUI:
    def __init__(self,master):
        self.master = master
        self.frame = Frame(master)
        master.title("Catalogue")
        master.geometry("500x300")
        self.categories = ["Top","Bottom","Dress","Outerwear"]
        self.setup_filters()

    def setup_filters(self):
        self.filter_categs_vars = []
        self.filter_checks = []
        for i in range(len(self.categories)):
            self.filter_categs_vars.append(IntVar())
            self.filter_checks.append(Checkbutton(root,variable=self.filter_categs_vars[i],text=self.categories[i]))
            self.filter_checks[i].grid(row=7+i,column=0,sticky=W)
            self.filter_checks[i].select()
            self.filter_checks[i].bind("<ButtonRelease-1>", self.filter_categ)

    def filter_categ(self, event):
        for i in range(len(self.filter_categs_vars)):
            #self.filter_checks[i].wait_variable(self.filter_categs_vars[i])
            print(self.filter_categs_vars[i].get()) #Debug print

#START PROGRAM
global catalogue 
root = Tk()
GUI(root)
root.mainloop()

最佳答案

分析

我相信的是a Minimal, Complete, and Verifiable example针对上述问题:

import tkinter as tk


def callback(event):
    #checkbutton.wait_variable(var)
    checkbutton['text'] = var.get()

if __name__ == '__main__':
    root = tk.Tk()
    var = tk.BooleanVar()
    checkbutton = tk.Checkbutton(root, text="Check", variable=var,
                                        onvalue=True, offvalue=False)
    checkbutton.pack()
    checkbutton.bind("<ButtonRelease-1>", callback)

    root.mainloop()

发生这种情况只是因为虚拟事件,例如设置 Checkbutton 的事件的值基于其状态(选中/未选中),在附加 bind 的事件之后处理已处理(请参阅提供的最后一个 MCVE)。

有关更多信息,请参阅:https://stackoverflow.com/a/3513906/7032856

<小时/>

对于您提供的代码,这意味着 Checkbutton的值不会改变,直到 bind事件句柄filter_categ方法完毕。但是filter_categ不会继续前进,除非 Checkbutton的值已更改。

这会使您的程序陷入本地事件循环,等待 'break' ,一个'break'仅当循环完成时才出现。感觉就像一个悖论。

<小时/>

查看以下示例:

import tkinter as tk
import random


def modify_var(event):
    var.set(random.choice((True, False)))

def callback(event):
    checkbutton.wait_variable(var)
    checkbutton['text'] = var.get()

if __name__ == '__main__':
    root = tk.Tk()
    var = tk.BooleanVar()
    checkbutton = tk.Checkbutton(root, text="Check", variable=var,
                                        onvalue=True, offvalue=False)
    checkbutton.pack(fill='both', expand=True)
    checkbutton.bind("<ButtonRelease-1>", callback)

    root.bind_all("<Escape>", modify_var)

    root.mainloop()

它具有与您的代码相同的矛盾行为,但唯一的异常(exception)是,当 Escape 被命中时,变量 wait_variable等待,var ,被修改,因此本地事件循环被破坏。

<小时/>

解决方案

通过使用 command option in Checkbutton

替换:

self.filter_checks[i].bind("<ButtonRelease-1>", self.filter_categ)

与:

self.filter_checks[i]['command'] = self.filter_categ

这是迄今为止最简单的。另外,您可以将方法定义覆盖为:

def filter_categ(self):
    ...

除非稍后其他事件会使用它。

其 MCVE:

# By using command option in Checkbutton MCVE
import tkinter as tk


def callback():
    checkbutton['text'] = var.get()

if __name__ == '__main__':
    root = tk.Tk()
    var = tk.BooleanVar()
    checkbutton = tk.Checkbutton(root, text="Check", variable=var,
                                        onvalue=True, offvalue=False)

    checkbutton['command'] = callback

    checkbutton.pack(fill='both', expand=True)
    root.mainloop()
<小时/>

通过使用 Tkinter Variable Class and, trace_add

替换:

self.filter_checks[i].bind("<ButtonRelease-1>", self.filter_categ)

与:

self.filter_categs_vars[i].trace_add('write', self.filter_categ)

上面的行,trace_add将调用其回调,self.filter_categ有 3 个参数,无论何时附加到该变量,您的方法也需要接受这些参数,self.filter_categs_vars[i] ,被修改。替换:

def filter_categ(self, event):

与:

def filter_categ(self, *args):

其 MCVE:

# By using Tkinter Variable Class and, trace_add
import tkinter as tk


def callback(*args):
    checkbutton['text'] = var.get()

if __name__ == '__main__':
    root = tk.Tk()
    var = tk.BooleanVar()
    checkbutton = tk.Checkbutton(root, text="Check", variable=var,
                                        onvalue=True, offvalue=False)
    
    var.trace_add('write', callback)

    checkbutton.pack(fill='both', expand=True)
    root.mainloop()
<小时/>

通过改变事件句柄序列的顺序

self.filter_checks[i].bind("<ButtonRelease-1>", self.filter_categ) # this line is not modified
self.filter_checks[i].bindtags((self.filter_checks[i].bindtags()[1:] + self.filter_checks[i].bindtags()[:1]))

这使得 "<ButtonRelease-1>"事件是最新处理的,如Checkbutton的变量值会在self.filter_categ之前改变已执行。

其 MCVE:

# By shifting the order of event handle sequence MCVE
import tkinter as tk


def callback(event):
    checkbutton['text'] = var.get()

if __name__ == '__main__':
    root = tk.Tk()
    var = tk.BooleanVar()
    checkbutton = tk.Checkbutton(root, text="Check", variable=var,
                                        onvalue=True, offvalue=False)
    checkbutton.pack(fill='both', expand=True)
    checkbutton.bind("<ButtonRelease-1>", callback)

    # comment the line below out to see the difference
    checkbutton.bindtags((checkbutton.bindtags()[1:] + checkbutton.bindtags()[:1]))

    root.mainloop()

关于python - 鼠标事件中的 tkinter wait_variable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48373128/

相关文章:

python - sqlalchemy connection.execute() 上的 UnicodeDecodeError 用于选择查询

python - 如何在多个子图上设置相同的轴值?

python - 使用 cx_freeze 为 tkinter 接口(interface)创建 .exe 文件

python - 在Tkinter中绘制波形图(Python)

python - pack() 和 grid() tkinter 的区别

python - 打开第二个 python 文件 (tkinter)

python - 安装适用于 Python 3.1 的 oursql 时出错

Python日期字符串测试

python - 类型错误 : Image data cannot be converted to float

python - 如何使用 pyserial 将文件逐行写入 com0com?