在tkinter中,有没有办法更改重叠画布的绘制堆栈顺序?

2024-04-29 01:54:59 发布

您现在位置:Python中文网/ 问答频道 /正文

问题

使用Python的tkinter,我试图通过扩展Canvas widget来创建自定义按钮和其他小部件。当用户与自定义画布小部件交互时,如何更改这些小部件被绘制在顶部?你知道吗

lift()适用于常规的tkinter按钮和其他小部件,但是当我尝试使用它来提升画布时会出现错误,因为Canvas has its own lift() method。Canvas的lift()不推荐用于Canvas,而支持tag\u raise()。但是,tag_raise()文档说它“不适用于窗口项”,这符合我的经验,并指导我改用lift()。我的大脑一直在追逐这个看似循环的文档,直到它引发了自己的StackOverflow异常,这让我不得不问你。你知道吗

代码说明

下面是一些运行的基本代码,说明了我的问题。我已经包括了button3,这是一个可以按预期提升()的常规按钮。但是,如果单击custom\u按钮1,click\u处理程序将引发一个异常。你知道吗

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

这适用于按钮3,但对于自定义按钮1,引发的异常是:

_tkinter.TclError: wrong # args: should be ".!example.!canvas.!mycustombutton2 raise tagOrId ?aboveThis?"

在这样的背景下,这个例外是有意义的帆布升降机()和Canvas.tag\u升起()通常用于按标记或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“您不能在小部件上绘制其他画布项目。”在这项工作中,所有自定义按钮都将是画布项目,因此如果我正确阅读了本文,就不能在其他小部件上绘制。)更不用说实现它所需的额外代码了。也就是说,我目前还没有更好的办法来实现这一点。你知道吗

提前感谢您的帮助!你知道吗


Tags: 代码self部件tkinter画布createcustomwindow
1条回答
网友
1楼 · 发布于 2024-04-29 01:54:59

不幸的是,您在tkinter实现中偶然发现了一个bug。你可以用几种方法来解决这个问题。您可以创建一个方法来执行tkinterlift方法所执行的操作,也可以直接在tkinterMisc类中调用该方法。你知道吗

因为您正在创建自己的类,所以可以重写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()

相关问题 更多 >