如何在线程正常工作后停止它,并在按下按钮时重新启动它?

时间:2018-09-26 21:06:22

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

我有一个正在为游戏创建的安装程序,到目前为止有两个按钮。一个下载游戏,如果检测到可执行文件,则启动游戏。我对两个按钮都进行了多线程处理,因此单击任何一个按钮时,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 :(得分:1)

您的问题是由//creating custom annotation import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.METHOD,ElementType.FIELD}) @Documented @Constraint(validatedBy = Mobile_EmailValidation.class) public @interface CustomAnnotation { String message() /*default "Invalid phone number"*/; int min(); Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // for validation class public class Mobile_EmailValidation implements ConstraintValidator<CustomAnnotation, String> { static{ System.out.println("hi this is static block"); } @Override public void initialize(CustomAnnotation constraintAnnotation) { System.out.println("hi this annotation works"); } @Override public boolean isValid(String contactField, ConstraintValidatorContext ctx) { System.out.println("hi this annotation works"); if (contactField == null) return true; boolean isValid; if (contactField.length()<10) { isValid = false; } else { isValid = true; }} //this is my entity class @Entity @Table(name = "user") public class User extends BaseDomain { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "name", nullable = true, length = 40) private String name; @CustomAnnotation(message = "invalid messages",min=4) @Column(name = "email_id", unique = true, nullable = false, length = 100) private String emailId; 有一个值引起的。
您需要重置self.worker
在下面,我已经调整了您的代码以执行此操作,在此过程中,我将self.worker重命名为notify_window,这仅仅是因为它使进行的操作更加明显并符合python标准。我确信还有很多其他方法可以实现这一目标,在这种情况下,这仅仅是一种简单的实现方法。

parent