英文:
Why is the generated tkinter button-1 event not recognized?
问题
我有2个画布矩形,它们都绑定了Button-1事件。有时候,我希望当一个矩形接收到这个事件时,另一个矩形也能接收到这个事件。因此,我需要通过widget方法"event_generate"来复制Button-1事件。
我的示例代码相当简化,因此你只能按一次左鼠标按钮。当你在红色矩形上按下左鼠标按钮时,会识别此事件,这通过显示消息"button1 red
这是我的代码:
import tkinter as tk
class rectangle():
def __init__(self, factor, color):
self.canvas_id = canvas.create_rectangle(factor * 10, factor * 10,
(factor + 1) * 10, (factor + 1) * 10, fill=color)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.color = color
def button1(self, event):
print("button1", self.color, event.x, event.y)
def get_canvas_id(self):
return self.canvas_id
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0] + coords[2]) / 2, (coords[1] + coords[3]) / 2
def create_event_for_rectangle(not_clicked):
print("Create event for other rectangle")
canvas.tag_unbind(red.get_canvas_id(), "<Button-1>", func_id_red)
canvas.tag_unbind(green.get_canvas_id(), "<Button-1>", func_id_green)
not_clicked_center_x, not_clicked_center_y = not_clicked.get_center()
canvas.event_generate("<Button-1>",
x=not_clicked_center_x, y=not_clicked_center_y)
canvas.create_rectangle(not_clicked_center_x - 1, not_clicked_center_y - 1,
not_clicked_center_x + 1, not_clicked_center_y + 1)
root = tk.Tk()
canvas = tk.Canvas(width=100, height=100)
canvas.grid()
red = rectangle(1, "red")
green = rectangle(4, "green")
func_id_red = canvas.tag_bind(red.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(green), add="+multi")
func_id_green = canvas.tag_bind(green.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(red), add="+multi")
root.mainloop()
我做错了什么?
英文:
I have 2 canvas rectangles, which have a binding for Button-1. Sometimes I want that when one rectangle gets this event, then the other rectangle should also get this event. So I have to duplicate the Button-1 event by the widget method "event_generate".
My example code is kind of minimal, so you can only press once the left mouse button. When you press the left mouse button over the red rectangle, this event is recognized, which is proved by the message "button1 red <x-coord> <y-coord>" showing up. Because I have added a second binding to each of the rectangles, also the method "create_event_for_rectangle" is started, which is proved by the message "Create event for other rectangle" showing up. This method also generates a new Button-1 event at the green rectangle. The coordinates of the generated event seem to be correct, as additionally a small rectangle is created at the calculated coordinates. This generated event at the the green rectangle now should create the message "button1 green 45 45", but nothing happens.
This is my code:
import tkinter as tk
class rectangle():
def __init__(self, factor, color):
self.canvas_id=canvas.create_rectangle(factor*10,factor*10,
(factor+1)*10,(factor+1)*10,fill=color)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.color = color
def button1(self, event):
print("button1", self.color, event.x, event.y)
def get_canvas_id(self):
return self.canvas_id
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0]+coords[2])/2, (coords[1]+coords[3])/2
def create_event_for_rectangle(not_clicked):
print("Create event for other rectangle")
canvas.tag_unbind(red.get_canvas_id() , "<Button-1>", func_id_red)
canvas.tag_unbind(green.get_canvas_id(), "<Button-1>", func_id_green)
not_clicked_center_x, not_clicked_center_y = not_clicked.get_center()
canvas.event_generate("<Button-1>",
x=not_clicked_center_x, y=not_clicked_center_y)
canvas.create_rectangle(not_clicked_center_x-1, not_clicked_center_y-1,
not_clicked_center_x+1, not_clicked_center_y+1)
root = tk.Tk()
canvas = tk.Canvas(width=100, height=100)
canvas.grid()
red = rectangle(1, "red" )
green = rectangle(4, "green")
func_id_red = canvas.tag_bind(red.get_canvas_id() , "<Button-1>",
lambda event: create_event_for_rectangle(green), add="+multi")
func_id_green = canvas.tag_bind(green.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(red ), add="+multi")
root.mainloop()
What am I doing wrong?
答案1
得分: 0
这是你要翻译的代码部分:
That's because the ```tag_unbind()``` or ```unbind()``` clears all the existing handlers. The backend Tcl/Tk library does not provide a command for unbinding, and [the current implementation](https://github.com/python/cpython/blob/v3.11.3/Lib/tkinter/__init__.py#L1450) of the ```unbind()``` binds a dummy empty script. (There is [an old open issue](https://github.com/python/cpython/issues/75666) on this problem. What about pinging the issue?)
Changing event handlers and synthesizing events make the program unnecessarily complex. In your case, it's much better to use the ```after_idle()``` and closures, like the following example.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(width=100, height=100)
canvas.grid()
class rectangle():
def __init(self, factor, color):
self.canvas_id = canvas.create_rectangle(...)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.other = None
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0]+coords[2])/2, (coords[1]+coords[3])/2
def button1(self, event=None):
print("button1", self.color, event)
other = self.other
if other and event:
x, y = other.get_center()
canvas.create_rectangle(x-1, y-1, x+1, y+1)
print("Schedule the button1() of other rectangle")
root.after_idle(other.button1)
red = rectangle(1, "red" )
green = rectangle(4, "green")
red.other = green
green.other = red
root.mainloop()
如果你需要进一步的翻译或有其他问题,请告诉我。
英文:
That's because the tag_unbind()
or unbind()
clears all the existing handlers. The backend Tcl/Tk library does not provide a command for unbinding, and the current implementation of the unbind()
binds a dummy empty script. (There is an old open issue on this problem. What about pinging the issue?)
Changing event handlers and synthesizing events make the program unnecessarily complex. In your case, it's much better to use the after_idle()
and closures, like the following example.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(width=100, height=100)
canvas.grid()
class rectangle():
def __init__(self, factor, color):
self.canvas_id = canvas.create_rectangle(...)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.other = None
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0]+coords[2])/2, (coords[1]+coords[3])/2
def button1(self, event=None):
print("button1", self.color, event)
other = self.other
if other and event:
x, y = other.get_center()
canvas.create_rectangle(x-1, y-1, x+1, y+1)
print("Schedule the button1() of other rectangle")
root.after_idle(other.button1)
red = rectangle(1, "red" )
green = rectangle(4, "green")
red.other = green
green.other = red
root.mainloop()
答案2
得分: 0
我在https://github.com/python/cpython/issues/75666找到了一个解决方案,并更新了我的代码。这个补丁重载了tag_unbind
方法。新的tag_unbind
首先读取绑定的函数id,然后移除要移除的函数,然后重新绑定。现在它按预期工作。
import tkinter as tk
class CanvasPatched(tk.Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def tag_unbind(self, tagOrId, sequence, funcid=None):
funcs_string = self.tk.call(self._w, 'bind', tagOrId, sequence, None).split('\n')
bound = '\n'.join([f for f in funcs_string if not f.startswith('if {"[' + funcid)])
self.tk.call(self._w, 'bind', tagOrId, sequence, bound)
if funcid:
self.deletecommand(funcid)
class Rectangle():
def __init__(self, factor, color):
self.canvas_id = canvas.create_rectangle(factor * 10, factor * 10,
(factor + 1) * 10, (factor + 1) * 10, fill=color)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.color = color
def button1(self, event):
print("button1", self.color, event.x, event.y)
def get_canvas_id(self):
return self.canvas_id
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0] + coords[2]) / 2, (coords[1] + coords[3]) / 2
def create_event_for_rectangle(not_clicked):
print("Create event for other rectangle")
canvas.tag_unbind(red.get_canvas_id(), "<Button-1>", func_id_red)
canvas.tag_unbind(green.get_canvas_id(), "<Button-1>", func_id_green)
not_clicked_center_x, not_clicked_center_y = not_clicked.get_center()
canvas.event_generate("<Button-1>", x=not_clicked_center_x, y=not_clicked_center_y, state=3)
canvas.create_rectangle(not_clicked_center_x - 1, not_clicked_center_y - 1,
not_clicked_center_x + 1, not_clicked_center_y + 1)
root = tk.Tk()
canvas = CanvasPatched(width=100, height=100)
canvas.grid()
red = Rectangle(1, "red")
green = Rectangle(4, "green")
func_id_red = canvas.tag_bind(red.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(green), add="+multi")
func_id_green = canvas.tag_bind(green.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(red), add="+multi")
root.mainloop()
英文:
I found a solution at https://github.com/python/cpython/issues/75666 and updated my code. The patch overloads the method tag_unbind. The new tag_unbind reads first the bound function ids, removes the function which shall be removed, and binds then again. Now it works as expected.
import tkinter as tk
class CanvasPatched(tk.Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def tag_unbind(self, tagOrId, sequence, funcid=None):
funcs_string = self.tk.call(self._w, 'bind', tagOrId, sequence, None).split('\n')
bound = '\n'.join([f for f in funcs_string if not f.startswith('if {"[' + funcid)])
self.tk.call(self._w, 'bind', tagOrId, sequence, bound)
if funcid:
self.deletecommand(funcid)
class rectangle():
def __init__(self, factor, color):
self.canvas_id=canvas.create_rectangle(factor*10,factor*10,
(factor+1)*10,(factor+1)*10,fill=color)
canvas.tag_bind(self.canvas_id, "<Button-1>", self.button1)
self.color = color
def button1(self, event):
print("button1", self.color, event.x, event.y)
def get_canvas_id(self):
return self.canvas_id
def get_center(self):
coords = canvas.coords(self.canvas_id)
return (coords[0]+coords[2])/2, (coords[1]+coords[3])/2
def create_event_for_rectangle(not_clicked):
print("Create event for other rectangle")
canvas.tag_unbind(red.get_canvas_id() , "<Button-1>", func_id_red)
canvas.tag_unbind(green.get_canvas_id(), "<Button-1>", func_id_green)
not_clicked_center_x, not_clicked_center_y = not_clicked.get_center()
canvas.event_generate("<Button-1>", x=not_clicked_center_x, y=not_clicked_center_y, state=3)
canvas.create_rectangle(not_clicked_center_x-1, not_clicked_center_y-1,
not_clicked_center_x+1, not_clicked_center_y+1)
root = tk.Tk()
canvas = CanvasPatched(width=100, height=100)
canvas.grid()
red = rectangle(1, "red" )
green = rectangle(4, "green")
func_id_red = canvas.tag_bind(red.get_canvas_id() , "<Button-1>",
lambda event: create_event_for_rectangle(green), add="+multi")
func_id_green = canvas.tag_bind(green.get_canvas_id(), "<Button-1>",
lambda event: create_event_for_rectangle(red ), add="+multi")
root.mainloop()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论