ProgressDialog beenden

Plattformunabhängige GUIs mit wxWidgets.
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Mittwoch 7. Mai 2008, 18:30

Hi,

ich versuche mich gerade am ProgressDialog mit folgendem Code:

Code: Alles auswählen

import wx
from threading import Thread

class Worker(Thread):
    def __init__(self, progressDialog):
        Thread.__init__(self)
        self.progressDialog = progressDialog
    def run(self):
        for i in range(0, 100):
            for j in range(0, 100000):
                pass
            self.progressDialog.Update(i+1, str(i+1))
        self.progressDialog.Destroy(True)

app = wx.PySimpleApp()
progressDialog = wx.ProgressDialog("Progress", "Message", 100, None, wx.PD_APP_MODAL)
worker = Worker(progressDialog)
worker.start()
app.MainLoop()
Der Balken baut sich wunschgemaess auf, aber am Ende kann man nicht auf X klicken um den Dialog zu beenden.

Was ist die einfachste Moeglichkeit, den Dialog entweder automatisch destroyen zu lassen oder dem Benutzer die Moeglichkeit zu geben, das fenster zu schliessen ?
Im Moment hangt sich das Fenster am Ende kompett auf :(

vielen Dank

ciao Daniel
lunar

Mittwoch 7. Mai 2008, 19:26

Du greifst aus einem anderen Thread auf GUI-Elemente zu, die sich im Hauptthread befinden. Kein Toolkit, welches ich kenne, hat Thread-sichere GUI-Widgets. Ergo bringt jeder Zugriff auf solche Widgets, der **nicht** aus dem Hauptthread erfolgt, die Nachrichtenschleife durcheinander, was sich bei dir wahrscheinlich im Einfrieren des Fensters äußert.

Merke: Zugriffe auf ein GUI-Elemente sollten **immer** aus dem Thread erfolgen, in dem diese GUI-Elemente erzeugt wurden.

Lösungsansätze bietet die wxPython Dokumentation.
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Mittwoch 7. Mai 2008, 21:05

Wenn ich da so mache, dass nur der Erzeuger Thread darauf zugreift, funktioniert das aber auch nicht...

Code: Alles auswählen

import wx
from threading import Thread

class Worker(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.progressDialog = wx.ProgressDialog("Progress", "Message", 100, None, wx.PD_APP_MODAL)
    def run(self):
        for i in range(0, 100):
            for j in range(0, 100000):
                pass
            self.progressDialog.Update(i+1, str(i+1))
        self.progressDialog.Close(True)
        self.progressDialog.Destroy(True)

app = wx.PySimpleApp()
worker = Worker()
worker.start()
app.MainLoop()
BlackJack

Mittwoch 7. Mai 2008, 21:34

Der Hauptthread enthält die `MainLoop` und auch das Widget wird dort erstellt, aber die `run()` läuft in einem anderen Thread. Also wieder Zugriff auf die GUI von einem anderen Thread als dem, in dem die Hauptschleife des GUI-Toolkits läuft -> Chaos.
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Mittwoch 7. Mai 2008, 21:56

Hm, wie ist dass dann denn zu loesen ?

so gehts:

Code: Alles auswählen

import wx

app = wx.PySimpleApp()
progressDialog = wx.ProgressDialog("Progress", "Message", 100, None, wx.PD_APP_MODAL)
for i in range(0, 100):
    for j in range(0, 100000):
        pass
    progressDialog.Update(i+1, str(i+1))
app.MainLoop()
so nicht mehr

Code: Alles auswählen

import wx
from threading import Thread

class Main():
    def __init__(self):
        self.progressDialog = wx.ProgressDialog("Progress", "Message", 100, None, wx.PD_APP_MODAL)
        self.worker = Worker(self)
        self.worker.start()
    def Update(self, value):
        self.progressDialog.Update(value, str(value))


class Worker(Thread):
    def __init__(self, listener):
        Thread.__init__(self)
        self.listener = listener
    def run(self):
        for i in range(0, 100):
            for j in range(0, 100000):
                pass
            self.listener.Update(i+1)
        print "finished"

app = wx.PySimpleApp()
main = Main()
app.MainLoop()

wie mach ich dass dann denn mit threads ?

danke
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Mittwoch 7. Mai 2008, 23:23

Wie? Wo? Was? ProgressDialog nur mit Threads-handlebar?

Ich flehe Euch an .... schaut Euch den Beispiel-Code zum ProgressDialog aus den "wx Python Docs Demos and Tools" an.

(Downloadbar in der Version 2.8.7.1 hier -> http://downloads.sourceforge.net/wxpyth ... .8.7.1.exe).

Code: Alles auswählen

wx.ProgressDialog(...,  style = wx.PD_CAN_ABORT, ...
... lautet das Zauberwort. Ohne Threads, ohne Events und ohne eventuell sogar Kills :shock:

>>Masaru<<
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Donnerstag 8. Mai 2008, 09:00

Ich benoetige aber zwingend Threads.

Und wx.PD_CAN_ABORT hilft auch nicht weiter...
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Donnerstag 8. Mai 2008, 10:11

Zuviele Bugs in ProgressDialog

werd wohl selber was schreiben muessen...
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Donnerstag 8. Mai 2008, 10:29

So gehts:

Code: Alles auswählen

import wx
from threading import Thread

class ProgressFrame(wx.Frame):
    def __init__(self, parent, message, heading):
        wx.Frame.__init__(self, parent, wx.ID_ANY, heading, size=(400, 200))
        self.EnableCloseButton(False)
        self.panel = wx.Panel(self, wx.ID_ANY )
        self.messageText = wx.StaticText(self.panel, wx.ID_ANY, message, pos=(50, 50))
        self.gauge = wx.Gauge(self.panel, wx.ID_ANY, 100, pos=(50, 100), size=(300, 30))
        self.Show(True)
    def update(self, value):
        self.gauge.SetValue(value)
    def OnFinish(self):
        self.EnableCloseButton(True)

class Worker(Thread):
    def __init__(self, pd):
        Thread.__init__(self)
        self.pd = pd
    def run(self):
        
        for i in range(0, 99):
            for j in range(0, 1000000):
                pass
            self.pd.update(i)
            print str(i)
        print "finished"
        self.pd.OnFinish()

app = wx.PySimpleApp()
progressFrame = ProgressFrame(None, "Inserting...", "Progress")
worker = Worker(progressFrame)
worker.start()
app.MainLoop()
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Donnerstag 8. Mai 2008, 11:00

Du hast Dir nicht die Demo genau genug angeschaut ... :?

Ich habe Dir hier mal einen abbrechbaren Dialog gebaut + die Update-Funktionalität in einen Thread noch geschoben (was man sich aber auch schenken könnte *seufz*.

Code: Alles auswählen

import threading
import time
import wx


class MainFrame(wx.Frame):
    def __init__(self, parent, ID, title, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):

        size = wx.Size(150,150)
        wx.Frame.__init__(self, parent, ID, title, pos, size, style)
        panel = wx.Panel(self, -1)

        button = wx.Button(panel, 1003, "Start")
        button.SetPosition((15, 15))
        self.Bind(wx.EVT_BUTTON, self.__start_a_progress, button)

    def __start_a_progress(self, event):
        def do_update(_progress):
            pbar_max = 100
            pbar_curr = 0
            keep_going = True
            while keep_going and pbar_curr < pbar_max:
                (keep_going, skip) = _progress.Update(pbar_curr)
                wx.MilliSleep(10)
                pbar_curr += 1
            _progress.Destroy()

        progress = wx.ProgressDialog(
                "Progress",
                "Message",
                maximum=100,
                parent=self,
                style=wx.PD_CAN_ABORT|wx.PD_APP_MODAL)

        pthread = threading.Thread(target=do_update, args=[progress])
        pthread.start()


class TestApp(wx.App):
    def OnInit(self):
        win = MainFrame(None, -1, "wxProgressDialog Tester")
        win.Show(True)
        self.SetTopWindow(win)
        return True

if __name__ == '__main__':
    app = TestApp()
    app.MainLoop()
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Donnerstag 8. Mai 2008, 11:18

Dein Programm laeuft bis sich der Button auf Close aendert und danach ist es nicht mehr responsive.

Habe es 1:1 kopiert und laufen lassen

Python 2.5, wxPython aktuelle Version und Windows XP SP2
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Donnerstag 8. Mai 2008, 11:18

Ah ... Fehler behoben, kopiers nochmal bitte.

Die "Close" Funktionalität darf bei dem Dialog nicht erreicht werden, sofern die Update-Schleife in einem Thread ausgeführt wird.

Bevor der Button also auf "Close" (wie gesagt: NUR im Thread-Szenario) wechselt (nach 99%) wird im Beispiel nun der Dialog `destroy`t.

Generell solltest Du Dir nochmal die "ProgressBar.Update" Beschreibung anschauen. Es werden die Werte "keep_going" und "skip" nämlich zurückgeliefert, die für die eigene Update-Steuerung von enormer Wichtigkeit sind sofern man "Cancel" nutzen möchte ;).
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Freitag 9. Mai 2008, 14:38

Ich verstehe es einfach nicht, warum funktioniert denn dieser zum obigen Postigng analoger Code nicht ?

Code: Alles auswählen

from threading import Thread
import wx

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Just a simple ProgressDialog", size=(400, 300))
        self.Show(True)
    def startWork(self):

        def runWork(_pd):
            keep_going = True
            for i in range(0, 100):
                for j in range(0, 100):
                    print i, j
                    pass
                (keep_going, skip) = _pd.Update(i)
                wx.MilliSleep(10)
                if not keep_going:
                    print "cancelled!"
                    break
            print "finished"
            _pd.Destroy()

        progress = wx.ProgressDialog(
                "Progress",
                "Message",
                maximum=100,
                parent=self,
                style=wx.PD_CAN_ABORT|wx.PD_APP_MODAL)
        worker = Thread(target=runWork, args=[progress])
        worker.start()


app = wx.PySimpleApp()
mainFrame = MainFrame()
mainFrame.startWork()
app.MainLoop()
Abbrechen funktioniert, laeuft der ProgressDialog aber bis zum ende, haengt sich das programm auf :(
sechsrad
User
Beiträge: 173
Registriert: Montag 31. März 2008, 17:09

Freitag 9. Mai 2008, 16:04

Abbrechen funktioniert, laeuft der ProgressDialog aber bis zum ende, haengt sich das programm auf

spielst du "hangmann"
Mephisto
User
Beiträge: 28
Registriert: Mittwoch 17. Januar 2007, 15:52

Freitag 9. Mai 2008, 16:55

Kann den nicht mal jemand abschalten? :(
Antworten