【问题标题】:In tkinter, is there a way to change the draw stack order of overlapping canvases?在 tkinter 中,有没有办法改变重叠画布的绘制堆栈顺序?
【发布时间】: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


【解决方案1】:

不幸的是,您偶然发现了 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()

【讨论】:

  • 非常感谢!你给我的两个选项都对我有用。有一个我应该比另一个更喜欢吗?我认为 Misc.tkraise() 是首选,因为它不依赖于私有变量 _w,但我对 Python 足够陌生,我不确定。
  • @SansWit:我个人会使用第二种方法。
  • 只花了一点时间就实现了第二种方法:“直接调用底层的tk解释器”,它完美地工作了。 Oakley 先生是 python tkinter 用户的天赐之物 :) 顺便说一句,我想了解更多关于 self._w 的作用。我见过它经常使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-28
  • 2014-11-07
  • 1970-01-01
相关资源
最近更新 更多