2017-08-15 13 views
0

Je suis très nouveau sur wxPython et je ne suis pas familier avec le concept de thread. J'apprécierais beaucoup si quelqu'un pourrait fournir des sources d'information ou une suggestion à ma question.Comment terminer un thread en utilisant la condition renvoyée par wx.CallAfter()?

J'avais créé une interface graphique en utilisant wxpython pour permettre aux utilisateurs d'exécuter mon script avec des entrées. Comme une étape prend 20 minutes, je prévois de créer un dialogue de progression pour montrer aux utilisateurs la progression et leur permettre de l'abandonner. J'ai testé cela en utilisant l'exemple de code ci-dessous.

Cependant, je ne pouvais pas arrêter WorkThread même si je cliquais sur la boîte de dialogue Stop Button in Progress. J'ai essayé 1. instruction make if utilisant la valeur de retour de pb.sendMessage() 2. crée l'objet ProgressDialog lorsque WorkThread démarre et appelle ProgressDialog.abort mais aucun d'entre eux ne fonctionne. Je me demande s'il y a des erreurs conceptuelles mettant en œuvre un code comme celui-ci pour réaliser ce que je veux faire? Ou si cela peut fonctionner avec correction? Tout indice serait apprécié !!

class WorkThread(Thread): 

    def __init__(self): 
     """Init Worker Thread Class.""" 
     Thread.__init__(self) 
     self.start() # start the thread 

    def run(self): 

     for i in range(10): 
      time.sleep(1) 
      val = 100/10 
      wx.CallAfter(pub.sendMessage, "update", step=val) 
     print 'Finish Run' 

class ProgressDialog(wx.Dialog): 
    def __init__(self): 

     wx.Dialog.__init__(self, None) 
     self.abort = False 
     self.progress = 0 

     bSizer2 = wx.BoxSizer(wx.VERTICAL) 
     self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) 
     self.gauge.SetValue(0) 

     self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0) 
     bSizer2.Add(self.gauge, 0, 0, 5) 
     bSizer2.Add(self.m_button1, 0, 0, 5) 

     self.SetSizer(bSizer2) 
     self.Layout() 
     self.Centre(wx.BOTH) 

     ## Connect Events 

     self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel) 
     pub.subscribe(self.updateProgress, "update") 

    def updateProgress(self, step): 
     self.progress += step 

     if self.abort: 
      self.Update() 
      self.Close() 
     elif self.progress >= 100: 
      self.gauge.SetValue(self.progress) 
      self.Update() 
      self.Close() 
     else: 
      self.gauge.SetValue(self.progress) 

    def on_cancel(self, event): 
     """Cancels the conversion process""" 
     self.abort = True 
     print 'Click' 
     # pub.unsubscribe(self.if_abort, 'cancel') 

    def __del__(self): 
     pass 


######################################################################################## 

class MainFrame(wx.Frame): 
    # ---------------------------------------------------------------------- 
    def __init__(self,parent): 
     wx.Frame.__init__(self,parent) 
     # Add a panel so it looks the correct on all platforms 
     panel = wx.Panel(self, wx.ID_ANY) 
     self.btn = btn = wx.Button(panel, label="Start Thread") 
     btn.Bind(wx.EVT_BUTTON, self.onButton) 

     sizer = wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5) 
     panel.SetSizer(sizer) 

    # ---------------------------------------------------------------------- 
    def onButton(self, event): 
     btn = event.GetEventObject() 
     btn.Disable() 
     WorkThread() 
     self.dlg = ProgressDialog() 
     self.dlg.ShowModal() 
     btn.Enable() 

app = wx.App() 
frame = MainFrame(None) 
frame.Show(True) 
# start the applications 
app.MainLoop() 

Répondre

0

Vous avez besoin d'une méthode dans le thread pour l'arrêter.
Vous devriez également attendre que cela s'arrête.
Voici une option en utilisant votre code:

from threading import Thread 
import wx 
from wx.lib.pubsub import pub 
import time 
class WorkThread(Thread): 

    def __init__(self): 
     """Init Worker Thread Class.""" 
     Thread.__init__(self) 
     self.stop_work_thread = 0 
     self.start() # start the thread 

    def run(self): 
     for i in range(10): 
      if self.stop_work_thread == 1: 
       break 
      time.sleep(1) 
      val = 100/10 
      wx.CallAfter(pub.sendMessage, "update", step=val) 
     wx.CallAfter(pub.sendMessage, "finish") 
     return 

    def stop(self): 
     self.stop_work_thread = 1 

class ProgressDialog(wx.Dialog): 
    def __init__(self,parent): 
     wx.Dialog.__init__(self, parent) 
     self.parent = parent 
     self.abort = False 
     self.progress = 0 

     bSizer2 = wx.BoxSizer(wx.VERTICAL) 
     self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) 
     self.gauge.SetValue(0) 

     self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0) 
     bSizer2.Add(self.gauge, 0, 0, 5) 
     bSizer2.Add(self.m_button1, 0, 0, 5) 

     self.SetSizer(bSizer2) 
     self.Layout() 
     self.Centre(wx.BOTH) 

     ## Connect Events 

     self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel) 
     pub.subscribe(self.updateProgress, "update") 
     pub.subscribe(self.on_finish, "finish") 

    def updateProgress(self, step): 
     self.progress += step 
     self.gauge.SetValue(self.progress) 

    def on_cancel(self, event): 
     """Cancels the conversion process""" 
     self.parent.work.stop() 
     self.parent.work.join() 
     pub.unsubscribe(self.updateProgress, "update") 
     pub.unsubscribe(self.on_finish, "finish") 
     self.Destroy() 
     # pub.unsubscribe(self.if_abort, 'cancel') 

    def on_finish(self): 
     """conversion process finished""" 
     pub.unsubscribe(self.updateProgress, "update") 
     pub.unsubscribe(self.on_finish, "finish") 
     self.Close() 

    def __del__(self): 
     pass 


######################################################################################## 

class MainFrame(wx.Frame): 
    # ---------------------------------------------------------------------- 
    def __init__(self,parent): 
     wx.Frame.__init__(self,parent) 
     # Add a panel so it looks the correct on all platforms 
     self.panel = wx.Panel(self, wx.ID_ANY) 
     self.btn = btn = wx.Button(self.panel, label="Start Thread") 
     btn.Bind(wx.EVT_BUTTON, self.onButton) 

     sizer = wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5) 
     self.panel.SetSizer(sizer) 

    # ---------------------------------------------------------------------- 
    def onButton(self, event): 
     btn = event.GetEventObject() 
     btn.Disable() 
     self.panel.work = WorkThread() 
     self.dlg = ProgressDialog(self.panel) 
     self.dlg.ShowModal() 
     btn.Enable() 

app = wx.App() 
frame = MainFrame(None) 
frame.Show(True) 
# start the applications 
app.MainLoop() 
+0

Merci beaucoup! Cela fonctionne parfaitement! J'essaie d'utiliser sth similaire avec self.stop_work_thread en tant qu'indicateur pour arrêter la boucle for-work dans WorkThread mais cela n'a pas fonctionné à ce moment-là. Est-ce parce que je n'ai pas utilisé MainFrame comme parent de ProgressDialog donc ce n'est pas du tout lié? – paraeve

+0

Peut-être, mais il ne semblait pas y avoir un moyen d'arrêter le fil lui-même, juste la barre de progression. La routine 'on_cancel' arrête maintenant le thread et attend qu'il se termine (l'instruction' .join'). –

+0

Merci pour la réponse détaillée! – paraeve