tkinter:绑定事件中奇怪的操作顺序,如何更改?

tkinter:绑定事件中奇怪的操作顺序,如何更改?,tkinter,focus,Tkinter,Focus,这是相关的,但不是完全相同的事情 我有一个输入字段(tk.Entry)和一个复选框字段(tk.Checkbutton)。我绑定到条目的FocusOut,复选框被赋予一个命令=在选中时运行。他们都被解雇了,但是 在许多情况下,用户输入文本,并且知道我处理FocusOut是提交文本更改的一种方式,所以他不必费心点击Return。他只是去点击相关的复选框,因为他知道文本更改当然会在复选框更改状态之前提交。这很重要;文本更改和选中标记状态都会在发生时发送到服务器,这将根据文本的大小决定选中标记状态是否合

这是相关的,但不是完全相同的事情

我有一个输入字段(tk.Entry)和一个复选框字段(tk.Checkbutton)。我绑定到条目的FocusOut,复选框被赋予一个命令=在选中时运行。他们都被解雇了,但是

在许多情况下,用户输入文本,并且知道我处理FocusOut是提交文本更改的一种方式,所以他不必费心点击Return。他只是去点击相关的复选框,因为他知道文本更改当然会在复选框更改状态之前提交。这很重要;文本更改和选中标记状态都会在发生时发送到服务器,这将根据文本的大小决定选中标记状态是否合法

问题是“当它们发生时”。无论出于何种原因,tkinter决定先触发复选标记更改,然后才开始在文本字段上触发焦点。我在ubuntu上,但我(和我的用户)已经习惯了Windows SDK应用程序,而这是不会发生的。结果是,当文本(据所知)处于不良状态时,服务器看到复选框被设置,错误路径被采用,邪恶的事情发生,用户咬牙切齿,我受到牙医账单的威胁,等等

这对我来说毫无意义。复选框必须接收焦点才能处理单击;为了获得焦点,widget应该放弃它。但特金特显然不同意

我尝试在checkmark的命令处理程序中将焦点设置为checkmark,但显然为时已晚。tkinter不会在那个时候停下来叫FocusOut。理论上,我可以让checkmark命令处理程序首先到达文本框并发送该内容,但由于复杂的原因,checkmark的代码甚至不知道文本字段的存在,因此更改该字段将非常困难。我可能会让checkmark命令队列在50ms后执行一个“after”操作来发送checkmark状态,但是我现在正在与快速手指的用户竞争

在我看来,这是一个tkinter bug-checkbutton在单击时接收焦点,处理空间作为切换的方式,它就像其他任何可以获得焦点的东西一样。在我看来,tkinter以错误的顺序报告focus属于2个widgits,这似乎是在简单地断言focus属于2个widgits。但是关于tkinter不会改变的猜测,以及用户在编辑每个文本字段后不会停止并点击Return的特定知识,是否有一些聪明的解决方法

根据要求,输入代码。对不起,长度太长了,但我想保留所有的装订,以防万一。按照显示的方向操作,结果将显示在标题栏(和标准输出)中

“”“请参见第130行以启用黑客绕过
Python 3.6.7 linux mint
"""
将tkinter作为tk导入
从tkinter导入ttk
从tkinter导入字体
myFont=None
零宽度=10
tkinterHandle=无
firstThingsFirst=错误
#基础设施,忽略
课程内容:
定义初始化(自我、isA、信息):
self.typeIs=isA
#基础设施,忽略
class Cmd:
定义初始化(自我、姓名、关于、文本):
自我介绍
self.name=名称
self.text=文本
#保存一个小部件,即特定小部件类型的基类
类别WidgetHolder:
定义初始化(self、name、about):
self.widgit=None
self.name=名称
自我介绍
self.parentW=self.about.widgit
def canTakeFocus(自我):
试试看:#显然不是每件事都有“状态”。
如果self.widgit['state']=“已禁用”:
返回错误
除了:#叹气
通过
返回isinstance(self.widgit,tk.Entry)或isinstance(self.widgit,ttk.Button)\
或isinstance(self.widgit,ttk.Combobox)或isinstance(self.widgit,tk.Checkbutton)\
或isinstance(self.widgit、ScrolledText)
def设置(自身,t):
自我应用(t)
def doEnable(自我,b):
通过
def SETERRORCOOR(自身):
通过
#保存条目
类别SingleLineInputOnPage(WidgetHolder):
定义初始化(self,c):
WidgetHolder.\uuuuu init\uuuuuu(self,c.name,c.about)
self.txt=tk.StringVar()
vcmd=(self.parentW.register(self.onValidate),“%P”)
#钥匙,不是鼠标什么的?
self.widgit=tk.Entry(self.parentW,validate=“key”,
validatecommand=vcmd,textvariable=self.txt,font=myFont,width=0)
self.savedText=c.text
self.apply(c.text)
self.widgit.bind(“,(lambda事件:self.timeToSend()))
self.widgit.bind(“,(lambda事件:self.doNot()))
self.widgit.bind(“,(lambda事件:self.doNot()))
self.widgit.bind(“,(lambda事件:self.click(事件)))
self.widgit.bind(“,(lambda事件:self.save()))
self.widgit.bind(“,(lambda事件:self.timeToSendMaybe()))
self.widgit.bind(“,(lambda事件:self.gotEsc()))
self.widgit.bind(“,(lambda事件:self.timeToSend()))
self.widgit.bind(“,(lambda事件:self.selectAll()))
def gotEsc(自我):
通过
def doNot(自我):
打印(“重新处理问题不涉及制表符!”)
def保存(自我):
self.savedText=self.txt.get()
def TIMETOSENDEMABLE(自我):
self.timeToSend()
def单击(自身,事件):
通过
def SETERRORCOOR(自身):
self.widgit.config(background=“#ff5000”)
def selectAll(自我):
self.widgit.select\u范围(0,'end')
返回“中断”
def timeToSend(自):
self.savedText=self.txt.get()
全球第一
firstThingsFirst=True
打印(“理想情况下,这首先发生。输入文本将立即发送:”,self.savedText)
返回“中断”
def应用(自身,t):
self.savedText=t
self.txt.set(t)
def onValidate(自我,P):
返回真值
#保留一个复选框
类别CheckBoxOnPage(WidgetHolder):
定义初始化(自我,
"""See line 130 to enable the hackAround
Python 3.6.7 linux mint
"""

import tkinter as tk
from tkinter import ttk
from tkinter import font

myFont = None
zerowidth = 10
tkinterHandle = None
firstThingsFirst = False

#infrastructure, ignore
class About:
    def __init__(self, isA, info):
        self.typeIs = isA

#infrastructure, ignore
class Cmd:
    def __init__(self, name, about, text):
        self.about = about
        self.name = name
        self.text = text

#holds a widget, base class of specific widget types
class WidgetHolder: 
    def __init__(self, name, about):
        self.widgit = None
        self.name = name
        self.about = about
        self.parentW = self.about.widgit 

    def canTakeFocus(self):
        try: #apparently not everything has a 'state'.
            if self.widgit['state'] == "disabled":
                return False
        except: #sigh
            pass
        return isinstance(self.widgit, tk.Entry) or isinstance(self.widgit, ttk.Button)\
              or isinstance(self.widgit, ttk.Combobox) or isinstance(self.widgit, tk.Checkbutton)\
              or isinstance(self.widgit, ScrolledText)

    def set(self, t):
        self.apply(t)

    def doEnable(self, b):
        pass

    def setErrorColor(self):
        pass

#Holds an Entry
class SingleLineInputOnAPage(WidgetHolder):
    def __init__(self, c):
        WidgetHolder.__init__(self, c.name, c.about)
        self.txt = tk.StringVar()
        vcmd = (self.parentW.register(self.onValidate), '%P')

        #key, not mouse or something?
        self.widgit = tk.Entry(self.parentW,   validate="key",
                          validatecommand=vcmd, textvariable=self.txt, font=myFont, width=0)
        self.savedText = c.text
        self.apply(c.text)
        self.widgit.bind("<Return>", (lambda event: self.timeToSend()))
        self.widgit.bind("<Key-ISO_Left_Tab>", (lambda event: self.doNot()))
        self.widgit.bind("<Tab>", (lambda event: self.doNot()))
        self.widgit.bind("<Button>", (lambda event: self.click(event)))
        self.widgit.bind("<FocusIn>", (lambda event: self.save()))
        self.widgit.bind("<FocusOut>", (lambda event: self.timeToSendMaybe()))
        self.widgit.bind("<Escape>", (lambda event: self.gotEsc()))
        self.widgit.bind("<Control-s>", (lambda event: self.timeToSend()))
        self.widgit.bind("<Control-a>", (lambda event: self.selectAll()))

    def gotEsc(self):
        pass
    def doNot(self):
        print("Reproduing the problem does not involve tab!")

    def save(self):
        self.savedText = self.txt.get()

    def timeToSendMaybe(self):
        self.timeToSend()

    def click(self, event):
        pass

    def setErrorColor(self):
        self.widgit.config(background="#ff5000")

    def selectAll(self):
        self.widgit.select_range(0, 'end')
        return 'break'

    def timeToSend(self):
        self.savedText = self.txt.get()
        global firstThingsFirst
        firstThingsFirst = True
        print("Ideally this happens first. Entry text would be sent now:", self.savedText)
        return 'break'

    def apply(self, t):
        self.savedText = t
        self.txt.set(t)

    def onValidate(self, P):
      return True

#Holds a checkbox
class CheckboxOnAPage(WidgetHolder):
    def __init__(self, c, cmd):
        WidgetHolder.__init__(self, c.name, c.about)
        if cmd == None:
            cmd = self.fire
        self.value = tk.IntVar()
        self.widgit = tk.Checkbutton(self.parentW, text="", variable=self.value, command=self.fire)
        self.apply(c.text)
        #self.widgit.bind("<Key-ISO_Left_Tab>", (lambda event: self.timeToSendMaybeAndPrev()))
        #self.widgit.bind("<Tab>", (lambda event: self.timeToSendMaybeAndNext()))

    def apply(self, t):
        pass

    def fire(self):
        self.widgit.focus()
        v = "X"[0:self.value.get()]

        #to get things in a better order, set to True
        if False:
          global tkinterHandle #clumsy, is there a way to get  self.widgit from the tk instance? 
          tkinterHandle.after(30, lambda sendthis=v: self.push(sendthis))
        else:
          self.push(v)

    def push(self, v):
        print("Checkmark ", v, " would be sent now. Ideally this does not happen first")
        global firstThingsFirst
        if firstThingsFirst:
          tkinterHandle.title("In order")
        else:
          tkinterHandle.title("Out of order!")


class CharSheet(tk.Tk):
    def __init__(self, argThing, *args, **kwargs):
        global myFont
        global zerowidth
        global tkinterHandle 

        tk.Tk.__init__(self, *args, **kwargs)
        self.geometry("360x80")
        self.title("Out of order?")

        myFont = tk.font.Font(family="Courier", size=10)
        zerowidth=myFont.measure("0")
        tkinterHandle = self
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)

        abt2 = About("C", "")
        abt2.widgit = container
        cmd2 = Cmd("b", abt2, "give me focus; then click checkbox")
        w2 = SingleLineInputOnAPage(cmd2)
        w2.widgit.pack()

        abt = About("C", "")
        abt.widgit = container
        cmd = Cmd("a", abt, "")
        w1 = CheckboxOnAPage(cmd, None)
        w1.widgit.pack()

CharSheet(None).mainloop()
import tkinter as tk
from tkinter import ttk

class Example():
    def __init__(self):
        self.root = tk.Tk()
        self.entry = tk.Entry(self.root)
        self.cb = tk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")
        self.text = tk.Text(self.root, height=8, width=80, background="bisque")
        self.vsb = tk.Scrollbar(self.root, command=self.text.yview)
        self.text.configure(yscrollcommand=self.vsb.set)

        self.entry.pack(side="top", fill="x")
        self.cb.pack(side="top", anchor="w")
        self.vsb.pack(side="right", fill="y")
        self.text.pack(side="bottom", fill="both", expand=True)

        self.entry.insert(0, "click here, then click on the checkbutton")

        self.entry.bind("<FocusOut>", self.handle_focus_out)
        self.entry.focus_set()

    def handle_focus_out(self, event):
        self.text.insert("end", "received entry <FocusOut>\n")
        self.text.see("end")

    def handle_checkbutton(self):
        self.cb.focus_set()
        self.text.insert("end", "received checkbutton command\n")
        self.text.see("end")

e = Example()
tk.mainloop()
def handle_checkbutton(self):
    self.cb.focus_set()
    self.root.update()
    self.text.insert("end", "received checkbutton command\n")
    self.text.see("end")
def handle_checkbutton(self):
    self.text.insert("end", "received checkbutton command\n")
    self.text.see("end")
self.cb = ttk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")
def set_cb_focus(self, event):
    self.text.insert("end", "setting focus via class binding\n")
    self.text.see("end")
    event.widget.focus_set()
self.root.bind_class("Checkbutton", "<ButtonPress-1>", self.set_cb_focus)
self.cb = tk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")