Как мне остановить поток, когда он закончил работать, и перезапустить его, когда нажата кнопка? - PullRequest
0 голосов
/ 27 сентября 2018

У меня есть установщик, который я создаю для игры, и на данный момент есть две кнопки.Один загружает игру, а другой запускает игру, если обнаруживает исполняемый файл.Я многопоточный обе кнопки, чтобы мой GUI не зависал, когда я нажимаю любую кнопку.Проблема в том, что если я нажму одну из кнопок, другая не будет работать до перезапуска приложения.Мне нужен какой-то способ закрытия потока после завершения процесса, чтобы открыть поток для работы другой кнопки.

Вот что у меня есть:

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

# Define global variables
url = "{ENTER DROPBOX URL HERE}" # The url to the file we are downloading
myEVT_PROGRESS = wx.NewEventType() # Custom Event Type
EVT_PROGRESS = wx.PyEventBinder(myEVT_PROGRESS, 1) # Bind specific events to event handlers
ID_START = wx.NewId()# Button definitions
EVT_RESULT_ID = wx.NewId()# Define notification event for thread completion

# Version Check
def VersionCheck():
    try:
        CurrentVersion = os.listdir("./RFMB6_WINDOWS/")[0] # Checks the version currently downloaded
        VersionCheck = requests.get('https://pastebin.com/raw/yc30uwAh') # Checks the newest version
        NewestVersion = VersionCheck.text # Converts VersionCheck to a string

        if CurrentVersion == NewestVersion:
            message = 'It looks like you have the newest version already.\n Are you sure you want to download?'
            wx.MessageBox(message=message, caption='RFMP GUIntaller | Complete!', style=wx.OK | wx.ICON_INFORMATION)

        else:
            print('\n\nThere is an update available, would you like to install it?')
            pass
    except:
        print("It looks like you don't have RFMP installed yet. Let me fix that for you.")

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

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

# Deletes the .zip file but leave the folder
def Clean():
    os.remove("RFMP.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 DLThread(Thread):
    """Worker Thread Class."""
    def __init__(self, notify_window):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self._notify_window = notify_window
        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("RFMB6_WINDOWS"):
            self.sendEvent('Removing old files...', 200)
            subprocess.check_call(('attrib -R ' + 'RFMB6_WINDOWS' + '\\* /S').split())
            shutil.rmtree('RFMB6_WINDOWS')
            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(.5)
        done = ("Installation the RFMP Private Alpha has been completed!")
        wx.MessageBox(message=done, caption='RFMP GUIntaller | Complete!', style=wx.OK | wx.ICON_INFORMATION)
        self._notify_window.worker = None

    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)

class StartAppThread(Thread):
    """Worker Thread Class."""
    def __init__(self, notify_window):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self._notify_window = notify_window
        # 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):
        try:
            subprocess.run('RFMB6_WINDOWS/RFMB6_WINDOWS/RFMB6.exe')
        except:
            error = ("Failed to locate RFMB6.exe. Please don't move any game files after downloading.")
            wx.MessageBox(message=error, caption='RFMP GUIntaller | Error!',
            style=wx.OK | wx.ICON_ERROR)
        self._notify_window.worker = None

# 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)
        self.Centre()

        DLStart = wx.Button(self.bitmap1, ID_START, 'Download RFMP', size=(175,50), pos=(50,260))
        DLStart.Bind(wx.EVT_BUTTON, self.OnButton_DLStart)
        AppStart = wx.Button(self.bitmap1, ID_START, 'Start RFMP', size=(175,50), pos=(50,160))
        AppStart.Bind(wx.EVT_BUTTON, self.OnButton_AppStart)
        self.status = wx.StaticText(self.bitmap1, -1, '', pos=(10,215), style=wx.NO_BORDER)
        self.status.SetBackgroundColour((255,255,0)) # set text back color
        self.gauge = wx.Gauge(self.bitmap1, range = 1000, size = (375, 30), pos=(10,230),
                              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 OnButton_DLStart(self, event):
        # Trigger the worker thread unless it's already busy
        VersionCheck()
        if not self.worker:
            self.worker = DLThread(self)

    def OnButton_AppStart(self, event):
        if not self.worker:
            self.worker = StartAppThread(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()

1 Ответ

0 голосов
/ 28 сентября 2018

Ваша проблема вызвана тем, что self.worker имеет значение.
Вам необходимо сбросить self.worker.
Ниже я настроил ваш код, чтобы сделать это, и при этом я переименовал notify_window до parent, просто потому, что это делает происходящее более очевидным и соответствует стандартам Python.Я уверен, что есть много других способов достижения этого, в данном случае это просто упрощенный способ достижения этого.

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

class DLThread(Thread):
    """Worker Thread Class."""
    def __init__(self, parent):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.parent = parent
        self.stop_download = 0
        self.setDaemon(1)
        self.start()

    def run(self):
        # This is the code executing in the new thread.
        '''
        This is what runs on a separate thread when you click the download button
        '''
        x = 0
        while self.stop_download == 0:
            time.sleep(0.5)
            x +=1
            if x > 20:
                self.stop_download = 1
            print ("Downloading App", x)
        print("Download finished")
        self.parent.worker = None

    def stop(self):
        self.stop_download = 1
        print ("Download Cancelled")

class StartAppThread(Thread):
    """Worker Thread Class."""
    def __init__(self, parent):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.parent = parent
        self.stop_app_thread = 0
        self.setDaemon(1)
        self.start()

    def run(self):
        # This is the code executing in the new thread.
        '''
        This is what runs on a separate thread when you click the Start App button.
        '''
        x= 0
        while self.stop_app_thread == 0:
            print ("Game in progress",str(x))
            time.sleep(0.5)
            x +=1
        print ("Game finished")
        self.parent.worker = None

    def stop(self):
        self.stop_app_thread = 1

# GUI Frame class that spins off the worker thread
class MainFrame(wx.Frame):
    """Class MainFrame."""
    #Main Window
    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)
        #self.bitmap1 = wx.StaticBitmap(self)
        self.bitmap1 = wx.Panel(self)
        self.Centre()

        # Variables
        myEVT_PROGRESS = wx.NewEventType() # Custom Event Type
        EVT_PROGRESS = wx.PyEventBinder(myEVT_PROGRESS, 1) # Bind specific events to event handlers
        ID_START = wx.NewId()# Button definitions
        EVT_RESULT_ID = wx.NewId()# Define notification event for thread completion

        # Download button
        DLStart = wx.Button(self.bitmap1, ID_START, 'Download', size=(175,50), pos=(50,260))
        DLStart.Bind(wx.EVT_BUTTON, self.OnButton_DLStart)

        # App Start button
        AppStart = wx.Button(self.bitmap1, ID_START, 'Start App', size=(75,50), pos=(50,160))
        AppStart.Bind(wx.EVT_BUTTON, self.OnButton_AppStart)

        # App Stop button
        AppStop = wx.Button(self.bitmap1, ID_START, 'Stop', size=(75,50), pos=(150,160))
        AppStop.Bind(wx.EVT_BUTTON, self.OnButton_AppStop)

        # Progress bar
        self.gauge = wx.Gauge(self.bitmap1, range = 1000, size = (375, 30), pos=(10,230), 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 OnButton_DLStart(self, event):
        # Trigger the worker thread unless it's already busy
        if not self.worker:
            self.worker = DLThread(self)

    def OnButton_AppStart(self, event):
        if not self.worker:
            self.worker = StartAppThread(self)

    def OnButton_AppStop(self, event):
        if self.worker:
           self.worker.stop()
        print ("App Stop command")

    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()
...