使用Python的tkinter,我尝试通过扩展Canvas widget来创建自定义按钮和其他小部件。当用户与之交互时,如何更改在其上绘制的自定义画布小部件?
lift()适用于常规tkinter Button和其他小部件,但是由于Canvas has its own lift() method,当我尝试使用它举起Canvas时会引发错误。 Canvas不推荐使用Canvas的lift(),而推荐使用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 鼠标事件的一个画布,就可以在画布上管理一堆自定义小部件。 all 小部件。我仍然可以为窗口小部件提供类,但是它们将继承Canvas参数,而不是从Canvas继承。因此添加看起来像下面的代码,我必须编写类似的代码来抬起,移动,确定是否将click事件应用于此按钮,更改活动状态等等。
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“您不能在窗口小部件上绘制其他画布项目。”在这种解决方法中,所有自定义按钮都是画布项目,因此,如果我正确阅读此内容, (不要在其他小部件之上绘制。)更不用说实现它所需要的额外代码。就是说,我目前对如何实现这一目标没有更好的了解。
提前感谢您的帮助!
答案 0 :(得分:3)
不幸的是,您偶然发现了tkinter实现中的一个错误。您可以通过两种方法解决此问题。您可以创建一个执行tkinter lift
方法的方法,也可以直接在tkinter Misc
类中调用该方法。
由于您正在创建自己的类,因此可以覆盖lift
方法以使用这两种方法。
这是使用现有功能进行操作的方式。确保从tkinter导入Misc
:
from tkinter import Misc
...
class MyCustomButton(Canvas):
...
def lift(self, aboveThis=None):
Misc.tkraise(self)
这是您直接调用基础tk解释器的方式:
class MyCustomButton(Canvas):
...
def lift(self, aboveThis=None):
self.tk.call('raise', self._w, aboveThis)
这样,您可以通过调用lift
方法将一个按钮移到另一个按钮上:
def click_handler(self,event):
event.widget.lift()