首页 > 编程问答 >Python tkinter 窗口不断关闭,我不知道为什么

Python tkinter 窗口不断关闭,我不知道为什么

时间:2024-07-24 03:55:10浏览次数:12  
标签:python tkinter python-multithreading

我正在尝试制作一个有趣的小程序,其中每小时左右就会有一只毛茸茸的动物走过屏幕。我有一个主窗口,它启动一个循环,每小时左右播放一次动画/声音,但是在口袋妖怪第一次完成行走后,整个程序就会结束。我认为这可能与我设置 tkinter 窗口的方式有关,但我无法弄清楚。

我认为在这里包含整个代码是不合适的,但我是新的我不知道我在做什么,所以请耐心等待。

from tkinter import *
from PIL import Image
from random import randint
from pydub import AudioSegment
from pydub.playback import play
import threading
import time

class MyFrame(Frame):
    def __init__(self, root):
        Frame.__init__(self, root)
        self.status = StringVar(self, 'Not Walking')
        self.walking = False
        self.showAnimation = None
        self.createWidgets()

    def createWidgets(self):
        self.mainLabel = Label(self, text= "<>FURRET WALK<>")
        self.statusLabel = Label(self, textvariable= self.status)
        self.startButton = Button(self, text="START", command= self.starter)
        self.endButton = Button(self, text="END", command= self.endWalk)

        self.mainLabel.pack(expand=True)
        self.statusLabel.pack(expand=True)
        self.startButton.pack(expand=True)
        self.endButton.pack(expand=True)

    def playSong(self):
        num = randint(1,6)
        path = ""
        match num:
            case 1:
                path = "1-17. Accumula Town.wav"
            case 2:
                path = "1-32. The Dreamyard.wav"
            case 3:
                path = "1-51. Castelia City.wav"
            case 4:
                path = "1-61. Bicycle.wav"
            case 5:
                path = "2-02. Driftveil City.wav"
            case 6:
                path = "2-08. Mistralton City.wav"
        song = AudioSegment.from_wav(path)
        audio = song[:43*1000]
        play(audio)

    def animation(self, extraWindow, count, imageObject, gifLabel, frames):
        newImage = imageObject[count]

        try:
            gifLabel.configure(image=newImage)
        except:
            pass
        count += 1
        if count == frames:
            count = 0
        try:
            self.showAnimation = extraWindow.after(20, lambda: self.animation(extraWindow, count, imageObject, gifLabel, frames))
        except:
            pass

    def moveWindow(self, extraWindow,moveX,moveY,startX,startY,dirX,dirY,width,yCumulitive,avgY):
        extraWindow.geometry(f"128x72+{moveX+startX}+{moveY+startY}")
        if dirY:
            yCumulitive += avgY
            if yCumulitive > 1:
                yCumulitive = 0
                moveY += 1
        else:
            yCumulitive += avgY
            if yCumulitive > 1:
                yCumulitive = 0
                moveY -= 1
        if dirX:
            moveX += 2
            if moveX > width+128:
                extraWindow.destroy()
        else:
            moveX -= 2
            if moveX < (width+128)*-1:
                extraWindow.destroy()
        time.sleep(0.05)
        try:
            extraWindow.after(150, self.moveWindow(extraWindow,moveX,moveY,startX,startY,dirX,dirY,width,yCumulitive,avgY))
        except:
            pass

    def createWindow(self):
        extraWindow = Toplevel()

        count = 0
        moveX = 0
        moveY = 0
        width = extraWindow.winfo_screenwidth()
        height = extraWindow.winfo_screenheight()
        dirX = True
        dirY = True
        startX = randint(-128, -127)
        startY = randint(0, height)
        if startX == -127:
            dirX = False
            startX = width
        yTarget = startY
        if randint(1,3) > 1:
            yTarget = (float(randint(0, width))*0.25)*2
        if randint(1,2) == 2:
            dirY = False
        if (startY - yTarget) < 0:
            dirY = True
        if (startY + yTarget) > height:
            dirY = False
        tics = width/2
        yDistance = yTarget - startY
        avgY = yDistance/tics

        extraWindow.geometry(f"128x72+{startX}+{startY}")
        extraWindow.wm_attributes("-topmost", True)
        extraWindow.overrideredirect(True)
        extraWindow.attributes("-transparentcolor", "white")

        if dirX:
            gifImage = "furret.gif"
        else:
            gifImage = "furret-right-to-left.gif"
        
        openImage = Image.open(gifImage)

        frames = openImage.n_frames

        imageObject = [PhotoImage(file=gifImage, format=f"gif -index {i}") for i in range(frames)]

        gifLabel = Label(extraWindow, image="")
        gifLabel.place(x=0,y=0,width=128,height=72)

        t1 = threading.Thread(target=self.playSong)
        t1.start()

        self.animation(extraWindow, count, imageObject, gifLabel, frames)
        self.moveWindow(extraWindow,moveX,moveY,startX,startY,dirX,dirY,width,0,avgY)

    def startWalk(self):
        self.walking = True
        self.status.set("Walking!")
        while(self.walking):
            self.createWindow()
            add = randint(1800, 3600)
            time.sleep(3600)

    def starter(self):
        ts = threading.Thread(target=self.startWalk, args=())
        ts.start()
        

    def endWalk(self):
        self.walking = False
        self.status.set("Not Walking")


def main():
    root = Tk()
    root.title("Furret Walk!")
    root.geometry("300x100")
    frame = MyFrame(root)
    frame.pack()
    root.mainloop()

main()

我尝试更改应该保持程序运行的 while 循环的位置,但它只会破坏一切。

基本上, main创建一个小窗口,其中填充了 createWidgets() 函数生成的小部件。我按下开始按钮,它运行 starter(),它为 startWalk() 函数创建一个线程。 startWalk() 应该继续播放,直到我按下结束按钮,这会触发 endWalk() ,从而将 while 循环条件变为 false。但如上所述,程序在毛茸茸第一次走过屏幕后结束。

在乱搞之后,我认为这与我设置的线程更相关,就像 t1 线程(它的存在是为了播放音乐)一样,播放完歌曲,程序结束。我不确定这有什么关系。


问题不在于 Tkinter 窗口不断关闭,而在于你的 startWalk 函数在第一次调用 createWindow 后结束,从而过早地停止了漫步过程。这是因为 while self.walking 循环在 createWindow 返回后立即检查 self.walking 的值,此时 createWindow 及其所有子进程(动画、移动、音乐)可能仍在运行。

你需要修改代码以使 startWalk 函数在 createWindow 创建的动画完成之前不会结束。

与其试图管理循环和睡眠时间,更简洁的解决方案是使用 after 方法来安排窗口的下一个“步骤”。以下是如何重构代码以实现此目的:

import tkinter as tk
from PIL import Image, ImageTk
from random import randint
from pydub import AudioSegment
from pydub.playback import play
import threading

class MyFrame(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.status = tk.StringVar(self, 'Not Walking')
        self.walking = False
        self.createWidgets()

    def createWidgets(self):
        self.mainLabel = tk.Label(self, text="&lt;&gt;FURRET WALK&lt;&gt;")
        self.statusLabel = tk.Label(self, textvariable=self.status)
        self.startButton = tk.Button(self, text="START", command=self.startWalk)
        self.endButton = tk.Button(self, text="END", command=self.endWalk)

        self.mainLabel.pack(expand=True)
        self.statusLabel.pack(expand=True)
        self.startButton.pack(expand=True)
        self.endButton.pack(expand=True)

    def playSong(self):
        # ... (song playing logic remains the same)

    def createWindow(self):
        extraWindow = tk.Toplevel()
        extraWindow.overrideredirect(True)
        extraWindow.attributes("-transparentcolor", "white")
        extraWindow.wm_attributes("-topmost", True)

        width = extraWindow.winfo_screenwidth()
        height = extraWindow.winfo_screenheight()

        # ... (rest of your createWindow logic for positioning, etc.)

        self.animate_and_move(extraWindow, width, height, 0, moveX, moveY, dirX, dirY, 0, avgY)

    def animate_and_move(self, window, screen_width, screen_height, frame_count, x, y, dir_x, dir_y, y_cumulative, avg_y):
        if not self.walking or not window.winfo_exists():
            return  # Stop if walking is stopped or window closed

        # ... (Your animation and movement logic here)

        # Schedule the next frame after a delay
        window.after(
            150, 
            self.animate_and_move, 
            window, 
            screen_width, 
            screen_height, 
            frame_count + 1, 
            x, 
            y, 
            dir_x, 
            dir_y, 
            y_cumulative, 
            avg_y
        )

    def startWalk(self):
        if not self.walking:
            self.walking = True
            self.status.set("Walking!")
            self.createWindow()

    def endWalk(self):
        self.walking = False
        self.status.set("Not Walking")

def main():
    root = tk.Tk()
    root.title("Furret Walk!")
    root.geometry("300x100")
    frame = MyFrame(root)
    frame.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

在这个改进的版本中:

  1. 我们将动画和移动逻辑合并到一个函数 animate_and_move 中。
  2. animate_and_move 使用 window.after 方法安排自身在 150 毫秒后再次调用,从而有效地创建了一个动画循环。
  3. createWindow 函数现在只初始化窗口和动画,然后 animate_and_move 接管。
  4. startWalk 函数现在只设置 self.walking 标志并调用一次 createWindow
  5. animate_and_move 函数在每次调用开始时都会检查 self.walking 标志。如果标志为 False(表示 endWalk 被调用),则函数返回,从而停止动画。

这种方法可以让你更简洁地控制动画,并且不会依赖于可能不准确的 time.sleep 调用。

标签:python,tkinter,python-multithreading
From: 78785210

相关文章

  • 用于自动访问 MongoDB Atlas CLI 的 Python 脚本
    我想编写一个Python脚本,以便普通用户可以访问他的数据库并从他的终端执行CRUD操作。我正在查看官方文档,但我有点迷失。有人可以给我指点基本教程来开始吗?当然,以下是如何构建Python脚本来访问MongoDBAtlasCLI的基本教程:先决条件:MongoDBAtlas......
  • Python实现简单学生登陆系统
     代码:importhashlibclassStudent:def__init__(self,username,password):#初始化学生对象,存储用户名和加密后的密码self.username=usernameself.password=hashlib.sha256(password.encode()).hexdigest()defcheck_passwo......
  • python猜数字游戏
    好的,下面是一个简单的小游戏代码示例,使用Python编写。这个游戏是一个猜数字游戏,玩家需要猜一个1到100之间的随机数。importrandomdefguess_number_game():print("欢迎来到猜数字游戏!")print("我已经想好了一个1到100之间的数字,请你猜一猜。")secret_num......
  • 【Python正则表达式】:文本解析与模式匹配
    文章目录1.正则表达式2.re模块3.修饰符3.元字符3-1字符匹配元字符3-2重复次数限定元字符3-3字符集合匹配元字符3-4分组元字符3-5边界匹配元字符3-6字符类别匹配元字符4.技巧4-1贪婪与非贪婪5.案例1.正则表达式正则表达式面向什么样的问题?1、判断一个字......
  • 仅需增加2行代码,Python量化策略速度提升20+倍!
    今天分享一个Python量化策略加速的小技巧,不用修改原有代码,只需在原有代码里新增2行,策略执行速度便可能提高20+倍,正文开始~现如今,无论是入门量化投资,还是做数据分析、机器学习和深度学习,Python成为了首选编程语言,直观的原因就是容易上手和资源丰富,但Python有个根深蒂固的标签,......
  • Python基础-Anaconda,Spyder,数据类型
    1、Python与Anaconda在想使用Python之前需先安装Python,以及PythonIDE和Python的库,而用Anaconda就可以一键安装。Anaconda包含了Python,常用的python库以及IDE,还具有强大的环境和python包的管理能力。PythonIDE(IntegratedDevelopmentEnvironment,集成开发环境)是一个为开发......
  • python实现图像特征提取算法2
    python实现广义Hough变换算法、Hough变换算法1.广义Hough变换算法详解算法步骤Python实现详细解释优缺点2.Hough变换算法详解算法步骤Python实现详细解释优缺点实现广义Hough变换算法(GeneralizedHoughTransform)可以用于检测任意形状的......
  • 使用Python自动备份MC服务器存档至轻量COS
    在存档意外炸档时,备份就显得尤为重要。本文以Minecraft1.18.2为例,教你如何使用轻量搭建一个MC服务器,并定时将存档备份入轻量COS,节省本地空间。前期准备一台安装了Linux原版系统的轻量(因为目前轻量COS仅支持挂载Linux系统镜像,不支持容器或自定义镜像)一个和轻量大区相同的轻......
  • python中的数据类型-适合新手-比较完善(写了好久……)
    作者的话        首先,我先申明,以下思路仅为个人理解,如有不同,望指导,谢谢。        数据类型它是什么,有什么用,怎么用就是它的全部内容,知识框架串联起来之后就是这三部分内容,没有什么特别的东西就只是单纯的可以培养这个习惯,之后就可以很轻松的学习使用之后串联......
  • 090、Python 写Excel文件及一些操作(使用xlwt库)
    要写Excel文件,我们需要使用第三方库。xlwt库是一个常用的写Excel的第三方库,它同时支持.xls和.xlsx。要使用第三方库,首选需安装:pipinstallxlwtxlutils安装完成后,我们就可以引入库来进行相关操作了。使用xrwt库写Excel文本,可以按以下步骤操作:1、第一步:创建工作簿(Wor......