首页 > 解决方案 > tkinter 中的动画

问题描述

我想为我通过 Tkinter 中的 Canvas 创建的自定义按钮添加一些动画。但是,我无法正确管理事件,而且我的动画经常无法正常工作。

这是一个代码:

from tkinter import *
import threading
import time
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(1)


class AACloseButton(Frame):
    def __init__(self, _x=170, _y=0, fg="black", **kw):
        super().__init__(borderwidth=0, height=10, width=10, bg='white', **kw)
        self.cnv = Canvas(self, highlightthickness=0, relief="flat", height=32, width=201, bg="white")
        self.cnv.pack(side=RIGHT)
        d1, d2 = 100, 31
        self.check1, self.check2 = False, False
        # self.left = False
        self.cover = self.cnv.create_rectangle(_x, _y, _x + d1, _y + d2, tags=["p098", "io9"])
        self.text = self.cnv.create_text(_x + (d1 / 2) + 15, _y + (d2 / 2), fill=fg,
                                         text="Close", tags=["p097", "io9"])
        self.rect3 = self.cnv.create_rectangle(_x + 1, _y + 1, _x + d2, _y + d2, tags=["p092", "io9"], fill="red",
                                               width=0)
        self.l1 = self.cnv.create_line(_x + 2, _y + 2, _x + d2 - 2, _y + d2 - 2, fill="white", tags=["io9", "p094"])
        self.l2 = self.cnv.create_line(_x + d2 - 2, _y + 2, _x + 2, _y + d2 - 2, fill="white", tags=["io9", "p093"])

        self.cnv.bind("<Leave>", self._aout)
        self.cnv.bind("<Enter>", self._ain)

    def _ain(self, event):
        print("Inserted")
        try:
            del self.r2
        except AttributeError:
            pass

        def worker():
            try:
                xchg = self.r3.is_alive()
            except AttributeError:
                xchg = False
            self.check1 = True
            if not xchg:
                for i in range(30):
                    self.cnv.move("io9", -2, 0)
                    time.sleep(0.0001)
                    if self.check2:
                        self.check2 = False
                        self.check1 = False

                        self.cnv.move("io9", (i + 1) * 2, 0)
                        break
                self.check1 = False

        self.r2 = threading.Thread(target=worker, daemon=True)
        self.r2.start()

    def _aout(self, event):
        print("Left")
        try:
            del self.r3
        except AttributeError:
            pass

        def worker():
            self.check2 = True
            if not self.r2.is_alive():
                for i in range(30):
                    self.cnv.move("io9", 2, 0)
                    time.sleep(0.0001)
                    if self.check1:
                        self.check1 = False
                        self.check2 = False

                        self.cnv.move("io9", -(i + 1) * 2, 0)
                        break
                self.check2 = False

        self.r3 = threading.Thread(target=worker, daemon=True)
        self.r3.start()


if __name__ == '__main__':

    root = Tk()
    root.geometry("200x200")
    root.tk.call('tk', 'scaling', 2)
    q = AACloseButton()
    q.pack(pady=10)
    root.mainloop()

这是带有.after(), 而不是线程的代码:

from tkinter import *
import ctypes

ctypes.windll.shcore.SetProcessDpiAwareness(1)


class AACloseButton(Frame):
    def __init__(self, _x=170, _y=0, fg="black", **kw):
        super().__init__(borderwidth=0, height=10, width=10, bg='white', **kw)
        self.cnv = Canvas(self, highlightthickness=0, relief="flat", height=32, width=201, bg="white")
        self.cnv.pack(side=RIGHT)
        d1, d2 = 100, 31
        self.co1, self.co2 = 0, 0
        self.check1, self.check2 = False, False
        # self.left = False
        self.cover = self.cnv.create_rectangle(_x, _y, _x + d1, _y + d2, tags=["p098", "io9"])
        self.text = self.cnv.create_text(_x + (d1 / 2) + 15, _y + (d2 / 2), fill=fg,
                                         text="Close", tags=["p097", "io9"])
        self.rect3 = self.cnv.create_rectangle(_x + 1, _y + 1, _x + d2, _y + d2, tags=["p092", "io9"], fill="red",
                                               width=0)
        self.l1 = self.cnv.create_line(_x + 2, _y + 2, _x + d2 - 2, _y + d2 - 2, fill="white", tags=["io9", "p094"],
                                       width=2)
        self.l2 = self.cnv.create_line(_x + d2 - 2, _y + 2, _x + 2, _y + d2 - 2, fill="white", tags=["io9", "p093"],
                                       width=2)

        self.cnv.bind("<Leave>", self._aout)
        self.cnv.bind("<Enter>", self._ain)

    def _ain(self, event=None):
        self.co1 += 1
        self.check2 = True
        self.cnv.move("io9", -2, 0)
        if self.check1:
            self.check1 = False
            self.cnv.move("io9", (self.co1 + 1) * 2, 0)
            self.co1 = 0
        elif self.co1 == 29:
            self.check1 = False
            self.check2 = False
            self.co1 = 0
        else:
            self.after(10, self._ain)

    def _aout(self, event=None):
        self.co2 += 1
        self.check1 = True
        self.cnv.move("io9", 2, 0)
        if self.check2:
            self.check2 = False
            self.cnv.move("io9", -(self.co2 + 1) * 2, 0)
            self.co2 = 0
        elif self.co2 == 29:
            self.check1 = False
            self.check2 = False
            self.co2 = 0
        else:
            self.after(10, self._aout)


if __name__ == '__main__':
    root = Tk()
    root.geometry("200x200")
    root.tk.call('tk', 'scaling', 2)
    q = AACloseButton()
    q.pack(pady=10)
    root.mainloop()

目前,该按钮是可用的,但存在一些错误:当您尝试canvas在鼠标上按下按钮离开时,“离开”事件会触发两次(一次是指针离开画布,一次是在释放按钮时)。此外,有时,当您快速移动鼠标时,按钮可能会错位。

请你解释一下我做错了什么?

.after()正如@TheLizzard 建议的那样,我已经用 重新制作了我的程序。它工作得更好;但是,未修复按下按钮的错误。

标签: pythonpython-3.xtkintertkinter-canvas

解决方案


尝试使用 TkVideo。它使用 Tkinter 标签小部件播放视频。

它是另一个库,可以使用pip install tkVideo.

来自https://pypi.org/project/tkVideo/的示例:

from tkinter import *
from tkvideo import tkvideo

root = Tk()
my_label = Label(root)
my_label.pack()
player = tkvideo.tkvideo("C:\\path\\to\\video.mp4", my_label, loop = 1, size = (1280,720))
player.play()

root.mainloop()

您可以制作视频并使用 TkVideo 播放。

欲了解更多信息,请访问https://pypi.org/project/tkVideo/


推荐阅读