【问题标题】:How do I Quit a Multi-Threaded GUI with wxpython?如何使用 wxpython 退出多线程 GUI?
【发布时间】:2023-03-10 08:15:01
【问题描述】:

很简单的问题。如何让这个程序在“run()”函数完成 5 秒后退出?

# Import Libraries
import requests, os, sys, zipfile, shutil, subprocess, wx, urllib, time
from threading import *

# Define variables
url = "Insert any Dropbox .zip file link here"
r = requests.get(url, stream = True)
myEVT_PROGRESS = wx.NewEventType() # Custom Event Type
EVT_PROGRESS = wx.PyEventBinder(myEVT_PROGRESS, 1) # bind specific events to event handlers

# Button definitions
ID_START = wx.NewId()

# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()

# Downloads new file
def Download():
    urllib.request.urlretrieve(url, 'File.zip')

# Extracts new file
def Extract():
    zip_ref = zipfile.ZipFile("File.zip", 'r')
    zip_ref.extractall("Folder")
    zip_ref.close()

# Deletes the .zip file but leave the folder
def Clean():
    os.remove("File.zip")

class ProgressEvent(wx.PyCommandEvent):
    """Event to signal that a status or progress changed"""
    def __init__(self, etype, eid, status=None, progress=None):
        """Creates the event object"""
        wx.PyCommandEvent.__init__(self, etype, eid)
        self._status = status       # field to update label
        self._progress = progress   # field to update progress bar

    def GetValue(self):
        """Returns the value from the event.
        @return: the tuple of status and progress
        """
        return (self._status, self._progress)

# Thread class that executes processing
class WorkerThread(Thread):
    """Worker Thread Class."""
    def __init__(self, notify_window):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self._notify_window = notify_window
        self.setDaemon(1)
        # This starts the thread running on creation.
        self.start()

    # This is what runs on a separate thread when you click the download button
    def run(self):
        # This is the code executing in the new thread.
        self.sendEvent('Checking for old files...', 00)
        self.sendEvent('Checking for old files...', 100)
        time.sleep(.5)
        if os.path.exists("Folder"):
            self.sendEvent('Removing old files...', 200)
            subprocess.check_call(('attrib -R ' + 'Folder' + '\\* /S').split())
            shutil.rmtree('Folder')
            time.sleep(.3)
            self.sendEvent('Removed old files!', 300)
        else:
            time.sleep(.3)
            self.sendEvent('No old files found.', 300)
            time.sleep(.3)
            pass
        self.sendEvent('Downloading Package...', 400)
        Download()
        self.sendEvent('Downloading complete!', 600)
        time.sleep(.3)
        self.sendEvent('Extracting...', 650)
        Extract()
        self.sendEvent('Extraction complete!', 900)
        time.sleep(.3)
        self.sendEvent('Cleaning up...', 950)
        Clean()
        time.sleep(.3)
        self.sendEvent('Cleaning complete!', 1000)
        time.sleep(.3)
        self.sendEvent('Done!',
                       1000)
        time.sleep(5)

######### QUIT PROGRAM HERE #########

    def sendEvent(self, status=None, progress=None):
        # Send event to main frame, first param (str) is for label, second (int) for the progress bar
        evt = ProgressEvent(myEVT_PROGRESS, -1, status, progress)
        wx.PostEvent(self._notify_window, evt)

# GUI Frame class that spins off the worker thread
class MainFrame(wx.Frame):
    """Class MainFrame."""    
    def __init__(self, parent, id):
        """Create the MainFrame."""
        wx.Frame.__init__(self, parent, id, 'RFMP GUInstaller', 
                          style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER
                          ^ wx.MAXIMIZE_BOX)
        self.SetSize(400, 350)
        wx.Button(self, ID_START, 'Download', size=(300,50), pos=(42,250))
        self.Bind(wx.EVT_BUTTON, self.OnStart, id=ID_START)
        self.status = wx.StaticText(self, -1, '', pos=(7,200), style=wx.NO_BORDER)
        self.status.SetBackgroundColour((255,255,0)) # set text back color
        self.gauge = wx.Gauge(self, range = 1000, size = (370, 30), pos=(7,217),
                              style =  wx.GA_HORIZONTAL)

        # And indicate we don't have a worker thread yet
        self.worker = None
        self.Bind(EVT_PROGRESS, self.OnResult) # Bind the custom event to a function

    def OnStart(self, event):
        # Trigger the worker thread unless it's already busy
        if not self.worker:
            self.status.SetLabel('')
            self.worker = WorkerThread(self)

    def OnResult(self, event):
        """Our handler for our custom progress event."""
        status, progress = event.GetValue()
        self.status.SetLabel(status)
        if progress:
            self.gauge.SetValue(progress)

class MainApp(wx.App):
    """Class Main App."""
    def OnInit(self):
        """Init Main App."""
        self.frame = MainFrame(None, -1)
        self.frame.Show(True)
        self.SetTopWindow(self.frame)
        return True

# Main Loop
if __name__ == '__main__':
    app = MainApp(0)
    app.MainLoop()

显然,我需要添加更多细节,因为我的程序中有很多代码,所以我在这里做了一个不必要的长句子,以使我的帖子看起来更具描述性,以满足需要我制作的堆栈溢出网站的要求我的代码更具描述性,否则它不会让我发布我需要帮助修复的很长的代码,因为我昨天才开始编写 GUI python。

【问题讨论】:

    标签: python python-3.x multithreading user-interface wxpython


    【解决方案1】:

    在您的Run 函数中,只需发送一个带有“终止”状态和类似-1 的进度值的信号。

    self.sendEvent('Terminate...', -1)
    

    你的 OnResult 函数,然后变成这样:

    def OnResult(self, event):
        """Our handler for our custom progress event."""
        status, progress = event.GetValue()
        self.status.SetLabel(status)
        if progress >= 0:
            self.gauge.SetValue(progress)
        else:
            # The worker is done
            self.worker = None
            time.sleep(5)
            self.Close()
            self.Destroy()
    

    【讨论】:

      【解决方案2】:

      在 run() 中你可以添加:

      import time
      import sys
      start = time.time()
      PERIOD_OF_TIME = 5    # 5 seconds
      ...
      your code
      ...
      if time.time() > start + PERIOD_OF_TIME:
          sys.exit(0)    # 5 seconds up, quit program
      

      【讨论】:

      • 在 run() 中,您正在执行 time.sleep() 总共超过 5 秒,因此最后的条件将始终解析为 true。但这听起来与您提出的问题不同。
      • 显然这没有经过测试。 sys.exit() 不会正确关闭我的 GUI 或当前运行的线程。然而,我自己找到了解决方案,我将在完成这个项目后发布。
      猜你喜欢
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      • 1970-01-01
      相关资源
      最近更新 更多