在tkinter中,是否可以更改重叠画布的绘制堆栈顺序?

时间:2019-04-07 06:44:32

标签: python tkinter tkinter-canvas

问题

使用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“您不能在窗口小部件上绘制其他画布项目。”在这种解决方法中,所有自定义按钮都是画布项目,因此,如果我正确阅读此内容, (不要在其他小部件之上绘制。)更不用说实现它所需要的额外代码。就是说,我目前对如何实现这一目标没有更好的了解。

提前感谢您的帮助!

1 个答案:

答案 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()