2

我正在尝试为我的 tkinter 应用程序创建一个滑动菜单栏,但我遇到了一个我怀疑来自我没有正确锁定线程的问题。虽然我已经涉足 Python 有一段时间了,但我对 tkinter 和线程应用程序都比较陌生,我希望这里的某个人能够对我做错了什么有所了解。

我的代码如下:

class MenuBar():

def slide_out(self, event = None):

    def helper():
        while self.canvas.coords(self.bg)[0] > 100 and self.mouseIn:
            self.canvas.move(self.bg, -5, 0)
            sleep(0.01)

    self.mouseIn = True
    Thread(target = helper).start()

def slide_in(self, event):
    self.mouseIn = False
    self.canvas.coords(self.bg, 750, 0)

def __init__(self, canvas):
    self.canvas = canvas

    self.bgImg = ImageTk.PhotoImage(file = "Options Bar.png")
    self.bg = canvas.create_image(750, 0, anchor = "nw", image = self.bgImg, tags = ("menubar", "menubarbackground"))

    self.mouseIn = False

    self.canvas.tag_bind("menubar", "<Enter>", self.slide_out)
    self.canvas.tag_bind("menubar", "<Leave>", self.slide_in)

我在画布小部件中工作,因为它使样式化更简单。将鼠标悬停在菜单栏上时收到的错误如下:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python33\lib\threading.py", line 639, in _bootstrap_inner
    self.run()
  File "C:\Python33\lib\threading.py", line 596, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\alex\Desktop\Python - VN Work\readingscreen.py", line 12, in helper
    while self.canvas.coords(self.bg)[0] > 100 and self.mouseIn:
  File "C:\Python33\lib\tkinter\__init__.py", line 2264, in coords
    self.tk.call((self._w, 'coords') + args))]
  File "C:\Python33\lib\tkinter\__init__.py", line 2262, in <listcomp>
    return [getdouble(x) for x in
ValueError: could not convert string to float: 'None'

现在,当我在线程外调用它时,它所抱怨的 canvas 方法起作用了。还值得一提的是,如果我在 self.canvas.coords 调用之前在辅助函数中对 self.bg 进行任何引用,则不会发生错误。然而,添加一些无用的参考似乎很难解决。我担心如果我现在不解决这个问题,以后只会加剧其他问题。

我假设我需要锁定我的线程以便让它毫无问题地引用 self.bg 是正确的,或者我在这里缺少什么?我尝试向 MenuBar 类添加锁,获取它并在辅助方法中释放它,但无济于事。

有人知道吗?谢谢。

4

1 回答 1

1

Tkinter 不是线程安全的。除了创建小部件的线程之外,您不能从任何线程调用小部件上的方法。添加锁定将无济于事。

对于像滑入和滑出菜单这样简单的事情,您不需要线程。有几个通过利用事件循环使用 Tkinter 制作动画的示例。基本上,算法如下所示:

def animate(...):
    <move the widget one or two pixels>
    <if the widget needs to move some more>:
        self.canvas.after(100, animate)

它的作用很简单:它将您的小部件移动一两个像素,检查它是否完全可见,如果不是,它会重新安排自己在几毫秒内再次运行。您可以通过调整每次迭代移动多少像素以及动画每帧之间的毫秒数来调整动画的速度和平滑度。

于 2013-07-06T11:38:28.953 回答