【发布时间】:2019-04-07 06:44:32
【问题描述】:
问题
使用 Python 的 tkinter,我正在尝试通过扩展 Canvas widget 创建自定义按钮和其他小部件。当用户与它们交互时,如何更改在顶部绘制的自定义画布小部件?
lift() 适用于常规 tkinter 按钮和其他小部件,但当我尝试使用它来提升 Canvas 时会引发错误,因为Canvas has its own lift() method。 Canvas 的 lift() 已被 Canvas 弃用,取而代之的是 tag_raise()。但是,tag_raise() 文档说它“不适用于窗口项目”,这符合我的经验,并指示我改用 lift()。我的大脑一直在追逐这个看似循环的文档,直到它引发了自己的 StackOverflow 异常,这让我想问你。
代码说明
这是一些运行并说明我的问题的基本代码。我已经包含了 button3,一个可以按预期提升()的常规按钮。但是,如果我单击 custom_button1,则 click_handler 会引发异常。
from tkinter import Button, Canvas, Frame, Tk
from tkinter.constants import NW
class Example(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.canvas = Canvas(self, width=200, height=200, background="black")
self.canvas.grid(row=0, column=0, sticky="nsew")
self.button3 = Button(self.canvas, text="button3")
self.custom_button1 = MyCustomButton(self.canvas)
self.custom_button2 = MyCustomButton(self.canvas)
self.canvas.create_window(20, 20, anchor=NW, window=self.button3)
self.canvas.create_window(40, 40, anchor=NW, window=self.custom_button1)
self.canvas.create_window(34, 34, anchor=NW, window=self.custom_button2)
self.button3.bind("<Button-1>", self.click_handler)
self.custom_button1.bind("<Button-1>", self.click_handler)
self.custom_button2.bind("<Button-1>", self.click_handler)
def click_handler(self,event):
event.widget.lift() #raises exception if event.widget is a MyCustomButton
#note that Canvas.lift() is deprecated, but documentation
#says Canvas.tag_raise() doesn't work with window items
class MyCustomButton(Canvas):
def __init__(self, master):
super().__init__(master, width=40, height=25, background='blue')
if __name__ == "__main__":
root = Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
这适用于 button3,但对于 custom_button1,引发的异常是:
_tkinter.TclError: wrong # args: should be ".!example.!canvas.!mycustombutton2 raise tagOrId ?aboveThis?"
在 Canvas.lift() 和 Canvas.tag_raise() 通常用于通过标签或 id 影响画布上的项目而不是画布本身的上下文中,该异常是有意义的。我只是不知道如何更改画布本身的堆栈顺序,因此我可以将其用作自定义小部件。
我考虑过的解决方法
我可以通过仅处理 all 绘图和 all 鼠标事件的 一个 画布来管理画布上的一堆自定义小部件所有小部件。我仍然可以有小部件的类,但不是从 Canvas 继承,而是接受 Canvas 参数。所以添加看起来像下面的代码,我必须编写类似的代码来提升、移动、确定是否将点击事件应用于此按钮、更改活动状态等等。
def add_to_canvas(self, canvas, offset_x=0, offset_y=0):
self.button_border = canvas.create_rectangle(
offset_x + 0, offset_y + 0,
offset_x + 40, offset_y + 25
)
#create additional button features
不过,这种解决方法似乎与 tkinter 中已建立的编码范例背道而驰。此外,我相信这种方法会阻止我在其他窗口对象上方绘制这些自定义按钮。 (根据create_window() documentation“您不能在小部件上绘制其他画布项。”在此解决方法中,所有自定义按钮都是画布项,因此,如果我正确阅读此内容,则不可能绘制在其他小部件之上。)更不用说实现所需的额外代码了。也就是说,我目前对如何实现这一点没有更好的想法。
提前感谢您的帮助!
【问题讨论】:
-
使用
if isinstance(event.widget, Canvas):可以识别您是否点击了Canvas或其他小部件(即Button)并使用不同的方法 -
@furas 是的,但我不确定用什么不同的方法代替 lift()。谢天谢地,Bryan Oakley 的回答解决了我的问题。
标签: python tkinter tkinter-canvas