wxPython: wx.ProgressDialog mit Threading und Trennung

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Beim Erstellen des Worker-Threads wird eine Funktion übergeben, die vom Thread laufend aufgerufen wird. Diese Funktion ruft über wx.CallAfter eine andere Funktion auf, die sich darum kümmert, dass der ProgressDialog einen neuen Wert bekommt. Außerdem wird auch abgefragt, ob der Benutzer den "Abbruch"-Button gedrückt hat. Wenn Ja, dann wird der Thread abgebrochen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

# wx.ProgressDialog mit Threading.
# Die Trennung zwischen Worker-Thread und GUI findet hier über eine Funktion statt

import wx
import threading
import time

wx.SetDefaultPyEncoding("iso-8859-15")


class MyWorkerThread(threading.Thread):
    """
    Dieser Thread ruft in Intervallen die übergebene Funktion "statusfunction" auf.
    Damit kann ein Status an die GUI übergeben werden.
    """
    
    def __init__(self, statusfunction = None):
        threading.Thread.__init__(self)
        self.canceled = threading.Event()
        self.statusfunction = statusfunction
    
    
    def run(self):
        """
        Hier wird gearbeitet
        """
        
        for i in xrange(1, 101):
            # Abbruch, wenn notwendig
            if self.canceled.isSet():
                print "Der Thread wurde abgebrochen"
                break
            print i
            
            # Statusfunktion aufrufen
            if self.statusfunction:
                self.statusfunction(i)
            
            # Ein wenig Arbeit simulieren
            time.sleep(0.1)
    
    
    def cancel(self):
        """
        Abbruchbedingung setzen
        """
        
        self.canceled.set()
    


class MyFrame(wx.Frame):
    
    def __init__(
        self, parent = None, title = "Example", size = wx.Size(250, 150)
    ):
        wx.Frame.__init__(self, parent, -1, title, size = size)
        
        panel = wx.Panel(self)
        
        vbox_main = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox_main)
        
        vbox_main.AddStretchSpacer()
        
        btn_start = wx.Button(panel, label = u"Start")
        vbox_main.Add(btn_start, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
        btn_start.Bind(wx.EVT_BUTTON, self.start_thread)
        
        vbox_main.AddStretchSpacer()
    
    
    def start_thread(self, event = None):
        """
        Startet den neuen Thread
        """
        
        self.progressdialog = wx.ProgressDialog(
            title = u"Demo", message = u"Huiiiiii... Jetzt geht's loooos...", parent = self, 
            style = wx.PD_AUTO_HIDE | wx.PD_APP_MODAL | wx.PD_CAN_ABORT
        )
        self.progressdialog.Show()
        
        # Beim erstellen des neuen Threads wird gleich die Funktion mitgegeben,
        # die bei Statusänderungen aufgerufen werden soll.
        self.worker = MyWorkerThread(self.worker_statusfunc)
        self.worker.start()
    
    
    def worker_statusfunc(self, value):
        """
        Diese Funktion wird vom Worker-Thread aus aufgerufen
        
        Es wird ``wx.CallAfter`` verwendet um einen Trennung zwischen Thread und
        GUI zu verwirklichen.
        """
        
        wx.CallAfter(self.progressdialog_update, value)
    
    
    def progressdialog_update(self, value):
        (thread_continue, thread_skip) = self.progressdialog.Update(value)
        
        # Wenn die Rückgabe (thread_continue) nicht mehr True ist, dann
        # Thread abbrechen
        if not thread_continue:
            # Thread abbrechen
            self.worker.cancel()
            
            # Kurz warten um dem Thread noch ein wenig Zeit zum Beenden zu lassen.
            time.sleep(0.3)
            
            # ProgressDialog zerstören
            self.progressdialog.Destroy()


def main():
    """Testing"""
    app = wx.PySimpleApp()
    f = MyFrame()
    f.Center()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Ich weiss nicht ob man hier Posten soll / darf, wenn nicht darf dieser Post gerne kommentarlos geloescht werden, ich weiss dann bescheid:)

Frage:

Koennte man statt

Code: Alles auswählen

# Kurz warten um dem Thread noch ein wenig Zeit zum Beenden zu lassen.
            time.sleep(0.3) 
nicht lieber sowas wie

Code: Alles auswählen

thread.join()
benutzen ?

Weil wenn der Thread umfangreiche Aufraeumaktionen hat, sich aufhaengt oder er vom Betriebssystem-Scheduler in den folgenden 0.3 Sekunden gar keine Rechenzeit um aufraeumen und beenden bekommt koennte es ja ein Problem geben oder ?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

maxip hat geschrieben:

Code: Alles auswählen

thread.join()
Hallo maxip!

``thread.join`` ist eine gute Idee. Daran kann man auch ein sinnvolles Timeout übergeben, falls der Thread sich, aus irgendeinem Grund, mal nicht beenden lässt.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten