使用Tkinter模块的GUI“按钮”管理脚本执行

0 投票
2 回答
1482 浏览
提问于 2025-04-18 18:06

有一个这样的脚本:

import sys, Tkinter

def myScript():
    ...

    ...


def runScript():
    while 1:
        myScript()

我想用Tkinter模块中的“按钮”来管理它。

if __name__ == '__main__':
    win = Frame ()
    win.pack ()
    Label(win, text='Choose following action', font=("Helvetica", 16), width=70, height=20).pack(side=TOP)
    Button(win, text='Start script', width=20, height=3, command=runScript).pack(side=LEFT)
    Button(win, text='Stop script', width=20, height=3, command=sys.exit).pack(side=LEFT)
    Button(win, text='Quit', width=15, height=2, command=win.quit).pack(side=RIGHT)
    mainloop()

当我点击“开始脚本”按钮时,我的脚本成功启动并运行(是个无限循环),但是当我想用“停止脚本”按钮来停止它时,我做不到,因为主窗口和按钮都变得不可用(显示“没有响应”)。

我需要做什么改变才能正确使用这两个按钮呢?

2 个回答

-2

使用这个:

import sys
from Tkinter import *
import tkMessageBox as tkmsg

win = None

def myScript():
    pass

def runScript():
    global win
    while 1:
        win.update()
        pass

def btnStop_Click():
    tkmsg.showinfo("Stopped", "Stopped")
    sys.exit

if __name__ == '__main__':
    global win
    win = Frame ()
    win.pack ()
    Label(win, text='Choose following action', font=("Helvetica", 16), width=70, height=20).pack(side=TOP)
    Button(win, text='Start script', width=20, height=3, command=runScript).pack(side=LEFT)
    Button(win, text='Stop script', width=20, height=3, command=btnStop_Click).pack(side=LEFT)
    Button(win, text='Quit', width=15, height=2, command=win.quit).pack(side=RIGHT)
    mainloop()
2

问题在于,脚本的执行被认为是被阻塞的,所以它一直在运行时,控制权不会返回到图形界面(GUI),这样就无法继续处理任何外部命令来停止它。要解决这个问题,你需要使用线程。最好的方法是让你的脚本方法继承自 threading.Thread,并重写 .run() 方法来执行你的脚本。这样做的代码看起来是这样的:

import threading

class MyScript(threading.Thread):
    def __init__(self):
        super(MyScript, self).__init__()
        self.__stop = threading.Event()

    def stop(self):
        self.__stop.set()

    def stopped(self):
        return self.__stop.isSet()

    def run(self):
        while not self.stopped():
            # Put your script execution here
            print "running"

接下来,你可以设置一个全局变量或类变量来跟踪当前是否有线程在运行(如果你希望用户能够运行多个脚本实例,可能需要以不同的方式处理这个问题),以及启动和停止线程的方法。我建议使用类变量,因为你的应用本身就是一个类,但这取决于你。

script_thread = None

def startScript():
    global script_thread
    # If we don't already have a running thread, start a new one
    if not script_thread:
        script_thread = MyScript()
        script_thread.start()

def stopScript():
    global script_thread
    # If we have one running, stop it
    if script_thread:
        script_thread.stop()
        script_thread = None

然后,你可以将这些方法绑定到你的按钮上。我不太确定你的应用结构是怎样的(看起来你是从 Tkinter 导入了所有内容,或者是对 Tkinter.Tk() 实例进行了子类化)。不过,为了实现你所提议的功能,你需要使用线程来避免阻塞的情况。

撰写回答