Wartemeldung - Thread

Plattformunabhängige GUIs mit wxWidgets.
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Wartemeldung - Thread

Beitragvon JR » Montag 6. November 2006, 18:05

Hallo Pythoner!

In meiner Applikation kann ich einen Dialog rufen, in dessen Init eine MySQL-Abfrage übers Internet gestartet wird. Und hier liegt mein Problem. Die Abfrage dauert etwa 2 bis 4 Sekunden. Dementsprechend poppt der Dialog auch erst nach einer kurzen Warte zeit auf.

Ich würde währenddessen gerne eine asynchrone Meldung wie "Daten werden geladen..." bringen.

Was muss ich um meinen Funktionsaufruf, welcher mir die abgefragten Daten zurückgibt herumbauen, damit das klappt?

Nach dem Motto:

Öffne Wartemeldung
Rufe Funktion die Daten ruft
Schließe Wartemeldung

Geht das mit wx.Thread() ?

Viele Grüße
JR
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Re: Wartemeldung - Thread

Beitragvon gerold » Montag 6. November 2006, 19:41

JR hat geschrieben:Öffne Wartemeldung
Rufe Funktion die Daten ruft
Schließe Wartemeldung

Hi JR!

So könnte es ohne Threads funktionieren:

Code: Alles auswählen

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

import wx
import time

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


class MyDialog(wx.Dialog):
   
    def __init__(self, parent = None, id = -1, title = "Hallo"):
        wx.Dialog.__init__(self, parent, id, title)
       
        statuslabel = wx.StaticText(self, -1)
        self.statuslabel = statuslabel
       
        wx.CallAfter(self.ich_tu_was)


    def ich_tu_was(self):
        statuslabel = self.statuslabel
        for i in range(1, 11):
            statuslabel.SetLabel(str(i))
            wx.YieldIfNeeded()
            time.sleep(0.5)


def main():
    app = wx.PySimpleApp()
    diag = MyDialog()
    diag.ShowModal()


if __name__ == "__main__":
    main()


Und so funktioniert es mit Thread:

Code: Alles auswählen

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

import wx
import time
from thread import start_new_thread

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


class MyDialog(wx.Dialog):
   
    def __init__(self, parent = None, id = -1, title = "Hallo"):
        wx.Dialog.__init__(self, parent, id, title)
       
        statuslabel = wx.StaticText(self, -1)
        self.statuslabel = statuslabel
       
        start_new_thread(self.ich_tu_was, ())


    def ich_tu_was(self):
        statuslabel = self.statuslabel
        for i in range(1, 11):
            wx.CallAfter(statuslabel.SetLabel, str(i))
            wx.YieldIfNeeded()
            time.sleep(0.5)


def main():
    app = wx.PySimpleApp()
    diag = MyDialog()
    diag.ShowModal()


if __name__ == "__main__":
    main()


Es gibt mehrere Möglichkeiten. Du könntest, unter anderem, auch eine Progressbar, einen ProgressDialog oder einen Throbber anzeigen/einblenden.

lg
Gerold
:-)

Edit: Code verbessert.
Zuletzt geändert von gerold am Freitag 5. Januar 2007, 19:03, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Beitragvon JR » Montag 6. November 2006, 19:58

Hi Gerold!

Zunächst einmal vielen Dank für die Codebeispiele.

Habe sie mir eben durchgelesen und weiß nicht, ob das so klappen kann.

Im Prinzip habe ich in der Funktion ich_tu_was einfach nur einen MySQL-Abfragestring den ich über die MySQL-Schnittstelle absende.
Diese Abfrage dauert lange.
Das heißt, die MySQL-Abfrage und die Funktion ich_tu_was müssten gleichzeitig laufen und wenn die Abfrage zu Ende ist, müsste die Schleife abgebrochen werden.

Ich versuchs mal
Grüße
Jamil
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Montag 6. November 2006, 20:53

JR hat geschrieben:und wenn die Abfrage zu Ende ist, müsste die Schleife abgebrochen werden

Hi JR!

Hier ist noch ein Beispiel. Allerdings spiele ich mich darin mit Threads und das ist für den Anfang nicht immer einfach.

Das Wichtigste ist, dass du nichts was die grafische Oberfläche ändert oder ändern kann, in einem Thread machst. Wenn sich etwas an der GUI ändern soll, dann übergebe den Auftrag an den Hauptthread, in dem das Programm selber läuft.

Das geht am Einfachsten mit ``wx.CallAfter()``.

Code: Alles auswählen

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

import wx
import time
from thread import start_new_thread


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


class MyDialog(wx.Dialog):
   
    def __init__(self, parent = None, id = -1, title = "Hallo"):
       
        # Dialogfenster initialisieren
        wx.Dialog.__init__(self, parent, id, title)
       
        # Abbruchvariable vorbereiten
        self.cancel = False
       
        # Einfaches Label für Statusanzeige
        statuslabel = wx.StaticText(self, -1, "Gleich tut er etwas...")
        self.statuslabel = statuslabel
       
        # Neuen Thread starten. In diesem Thread wird gearbeitet und alles
        # was irgendwie an den Hauptthread übergeben wird, wird über ``wx.CallAfter``
        # geleitet.
        # Alles was irdengetwas mit der GUI zu tun hat, hat im Hauptthread zu bleiben.
        # Deshalb wird alles, was die Anzeige verändert, per ``wx.CallAfter`` an
        # den Hauptthread weitergeleitet und nicht diret in einem anderen Thread
        # geändert.
        start_new_thread(self.ich_tu_was, ())
   
   
    def show_progressdiag(self):
       
        # ProgressDialog anzeigen
        diag = wx.ProgressDialog(
            "Arbeitsmeldung", "Jetzt tu ich etwas...", 100, self
        )
       
        # In einer Schleife die Progressbar bewegen.
        while True:
            time.sleep(0.1)
            diag.UpdatePulse()
            # Wenn die Abbruchvariable gesetzt wurde --> Fertig
            if self.cancel:
                diag.Destroy()
                return
   

    def ich_tu_was(self):
        statuslabel = self.statuslabel
       
        # Label setzen (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(statuslabel.SetLabel , "Jetzt tu ich etwas...")
        wx.CallAfter(wx.YieldIfNeeded)
       
        # Abbruchvariable auf False setzen
        self.cancel = False
       
        # Den Progressdialog im Hauptthread starten.
        # (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(self.show_progressdiag)
       
        # Hier wird gearbeitet
        time.sleep(5) # Hier könntest du die SQL-Abfrage durchführen
       
        # Abbruchvariable setzen, damit der PrograssDialog endet
        self.cancel = True
       
        # Label setzen (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(statuslabel.SetLabel , "Fertig!")
        wx.CallAfter(wx.YieldIfNeeded)


def main():
    app = wx.PySimpleApp()
    diag = MyDialog()
    diag.ShowModal()


if __name__ == "__main__":
    main()

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs

Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Beitragvon JR » Montag 6. November 2006, 21:01

Hi Gerold!

Deine Mühen sind wirklich toll.
Also ich habe wohl genau den klassischen Fehler gemacht und im Thread meinen "Bitte warten..." Dialog gestartet. Ging auch halbwegs schief :-)

Baue jetzt deine letzte Variante ein und melde mich dann bestimmt glücklich zurück...

"Bitte warten..."

Gruß
Jamil
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Beitragvon JR » Montag 6. November 2006, 22:58

Hi Gerold,

also es ist doch etwas komplizierter.
Erst einmal habe ich gemerkt, wieviel Performance es schluckt, wenn man mehrere MySQL-Abfragen durch eine Schleife hat.
Konnte eine Schleife mit vielen Abfragen durch eine sinnvollere Abfrage ersetzen und benötige an dieser Stelle keine "Bitte warten..." Meldung mehr.

Dennoch gibt es andere Stellen, wo die Abarbeitung solch eine Meldung parallel bringen sollte, damit meine Anwender nicht denken, dass das Programm abgeschmiert ist.
Ich muss mich mit dem Threading mehr auseinandersetzen. Mein Problem ist, dass ich versucht habe, über den Thread ganze Methoden (inkl. Rückgabewert und GUI-Steuerung) auszulösen. Das ging aber alles nicht so, wie ich es wollte. Entweder kam nur ein weißer Dialog oder meine Stelle, wo ich das Abbruchkriterium für die Progressschleife setze, wurde nicht durchlaufen etc.

Also ich werde es einfach mal die Tage versuchen, hinzubekommen.

Vielen Dank jedenfalls nochmals für deine rasche Reaktion.

Viele Grüße
Jamil
JR
User
Beiträge: 286
Registriert: Montag 20. Februar 2006, 16:43
Wohnort: Berlin

Beitragvon JR » Donnerstag 7. Dezember 2006, 18:27

Hallo!

Keine Frage, sondern jetzt im Folgenden der Code dessen, was ich gesucht habe.
Ist im Prinzip komplett die obige Lösung von Gerold, wobei in den Zeilen 82-85

irgendetwas passiert, was halt länger dauern kann.

Code: Alles auswählen

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

import wx
import time
from thread import start_new_thread


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


class MyDialog(wx.Dialog):
   
    def __init__(self, parent = None, id = -1, title = "Hallo"):
       
        # Dialogfenster initialisieren
        wx.Dialog.__init__(self, parent, id, title)
       
        # Abbruchvariable vorbereiten
        self.cancel = False
       
        # Einfaches Label für Statusanzeige
        statuslabel = wx.StaticText(self, -1, "Gleich tut er etwas...")
        self.statuslabel = statuslabel
       
        # Neuen Thread starten. In diesem Thread wird gearbeitet und alles
        # was irgendwie an den Hauptthread übergeben wird, wird über ``wx.CallAfter``
        # geleitet.
        # Alles was irdengetwas mit der GUI zu tun hat, hat im Hauptthread zu bleiben.
        # Deshalb wird alles, was die Anzeige verändert, per ``wx.CallAfter`` an
        # den Hauptthread weitergeleitet und nicht diret in einem anderen Thread
        # geändert.
        start_new_thread(self.ich_tu_was, ())
   
   
    def show_progressdiag(self):
       
        # ProgressDialog anzeigen
        diag = wx.ProgressDialog(title="Arbeitsmeldung",
                                 message="Jetzt tu ich etwas...",
                                 maximum=100, parent=self,
                                 style=wx.PD_APP_MODAL|
                                 wx.PD_CAN_ABORT|
                                 wx.PD_SMOOTH^
                                 wx.PD_AUTO_HIDE)
        self.diag = diag
        # In einer Schleife die Progressbar bewegen.
        i = 0
        while True:
            time.sleep(0.5)
            if i * 10 >= 100:
                i=0
           
            try:             
                self.diag.Update(i * 10)
            except:
                pass
            i += 1

            # Wenn die Abbruchvariable gesetzt wurde --> Fertig
            if self.cancel:
                diag.Destroy()
                return
             
   

    def ich_tu_was(self):
        statuslabel = self.statuslabel
       
        # Label setzen (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(statuslabel.SetLabel , "Jetzt tu ich etwas...")
        wx.CallAfter(wx.YieldIfNeeded)
       
        # Abbruchvariable auf False setzen
        self.cancel = False
       
        # Den Progressdialog im Hauptthread starten.
        # (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(self.show_progressdiag)
       
        # Hier wird gearbeitet
        for i in range(10):
            time.sleep(1) # Hier könntest du die SQL-Abfrage durchführen
            print i
       
        # Abbruchvariable setzen, damit der PrograssDialog endet
        self.cancel = True
       
        # Label setzen (über Threadgrenzen hinweg -- deshalb mit CallAfter)
        wx.CallAfter(statuslabel.SetLabel , "Fertig!")
        wx.CallAfter(wx.YieldIfNeeded)


def main():
    app = wx.PySimpleApp()
    diag = MyDialog()
    diag.ShowModal()


if __name__ == "__main__":
    main()


Viele Grüße
Jamil

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder