Throbber wird nicht richtig angezeigt [SOLVED]

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Steffen
User
Beiträge: 3
Registriert: Donnerstag 29. Juni 2006, 12:27

Absicht:
Ich möchte in meinem Programm eine längere Datenübertragung ausführen (ca. 2min) und brauche deshalb einen Throbber, der während dieser Zeit angezeigt wird. Stellvertretend für die Datenübertragung habe ich im nachfolgenden Code wx.Sleep(10) verwendet, da auch meine Datenübertragung aus einer einzelnen Funktion besteht, die das Hauptprogramm stillstehen lässt.

Problem:
Der Throbber bleibt während der Datenübertragung weiß (wird nicht aktualisiert), obwohl dieser ja eigentlich in einem eigenen Thread laufen müsste!?

Kann mir jemand sagen, wo der Fehler liegt?

Code: Alles auswählen

import wx
import wx.lib.throbber as throb
import throbImages
#
if __name__=="__main__":
    def on_start_transfer(event):
        images = [throbImages.catalog[i].getBitmap()
                  for i in throbImages.index]
        my_throbber = throb.Throbber(throb_frame,-1,images)
        #
        throb_frame = wx.Frame(None,-1, size = (35,35), style = wx.SIMPLE_BORDER)
        throb_frame.Show()
        #
        my_throbber.Start()
        throb_frame.MakeModal(True)
        wx.Sleep(10) # Stands for transfer process.
        throb_frame.MakeModal(False)
    #
    app = wx.PySimpleApp()
    frame = wx.Frame(None)
    btn = wx.Button(frame,-1,'Start')
    #
    frame.Bind(wx.EVT_BUTTON, on_start_transfer, btn)
    #
    frame.Show()
    #
    app.MainLoop()
Zuletzt geändert von Steffen am Dienstag 4. Juli 2006, 08:01, insgesamt 1-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hi Steffen!

Unterbrich niemals durch ein "sleep()" den app.MainLoop.
Da ist nichts mit eigenem Thread und so. Wenn du den MainLoop unterbrichst, dann passiert gar nichts mehr.

Wenn sich während längerer Prozesse noch etwas rühren soll, oder Zwischenstände angezeigt werden sollen, dann **muss** man die Arbeit in einen eigenen Thread auslagern. Allerdings darf dieser ausgelagerte Arbeits-Thread nicht direkt mit dem Haupt-Thread (MainLoop) kommunizieren. Die einfachste Art, wie der arbeitende Thread doch noch mit dem Haupt-Thread kommunizieren kann, ist über die Funktion "wx.CallAfter()". Diese Funktion kümmert sich um die Trennung der Threads.

Hier ein Beispiel, das ich allerdings nur unter Windows getestet habe.

Code: Alles auswählen

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

import wx
import wx.lib.throbber
import throbImages # Muss aus dem Demo-Ordner in den Projektordner kopiert werden
import thread
import time


class MyFrame(wx.Frame):
    """
    Hauptformular
    """
    
    def __init__(self, parent = None, id = -1, title = "Throbber-Worker"):
        """
        Initialisiert den Frame
        """
        
        wx.Frame.__init__(self, parent, id, title)
        
        vbox = wx.BoxSizer(wx.VERTICAL)
        
        panel = wx.Panel(self)
        panel.SetSizer(vbox)
        
        images = [
            throbImages.catalog[i].getBitmap()
            for i in throbImages.index
            if i not in ['eclouds', 'logo']
        ]
        
        self.throb = wx.lib.throbber.Throbber(panel, -1, images)
        vbox.Add(self.throb, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5)
        
        self.btn_start = wx.Button(panel, -1, "Start")
        vbox.Add(self.btn_start, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5)
        self.btn_start.Bind(wx.EVT_BUTTON, self.OnStart)
        
        panel.Fit()
        self.SetVirtualSize(panel.GetSize())
        self.Fit()
        self.SetSizeHintsSz(self.GetSize())
        
        self.Center()
        self.Show()
    
    
    def my_worker(self):
        """
        Diese Funktion wird in einem eigenen Thread ablaufen
        """
        
        wx.CallAfter(self.throb.Start) # Aufruf über Thread-Grenze hinweg.
        wx.CallAfter(self.btn_start.Enable, False) # Aufruf über Thread-Grenze hinweg.
        
        time.sleep(10)
        
        wx.CallAfter(self.throb.Stop) # Aufruf über Thread-Grenze hinweg.
        wx.CallAfter(self.btn_start.Enable, True) # Aufruf über Thread-Grenze hinweg.


    def OnStart(self, event):
        """
        Startet den eigenständigen Thread
        """
        
        thread.start_new(self.my_worker, ())


if __name__ == "__main__":
    app = wx.PySimpleApp()
    myframe = MyFrame()
    app.MainLoop()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Steffen
User
Beiträge: 3
Registriert: Donnerstag 29. Juni 2006, 12:27

Danke für die schnelle Hilfe, so funktionierts jetzt.
@alle, die ein ähnliches Problem haben: die time.Sleep() Anweisung steht für die Funktion, die man ausführen will während der Throbber läuft.
Gruß,
Steffen
Antworten