Seite 1 von 1
Threads - Zwischenergebnisse an GUI übergeben
Verfasst: Dienstag 9. Oktober 2007, 22:01
von tomate
Moin!
Ich habe eine Funktion in einer Klasse, die Berechnungen durchführt und am Ende das Ergebnis über return an eine GUI Klasse zurückgibt.
Als Erweiterung würde ich gerne schon Zwischenergebnisse ausgeben und vielleicht auch den Fortschritt der Berechnung anzeigen.
Dazu lasse ich die Berechnung in einem eigenen Thread laufen.
Muss ich mit wx.CallAfter() arbeiten, um mir schon während der Laufzeit Zwischenergebnisse liefern zu lassen?
Danke
Verfasst: Donnerstag 11. Oktober 2007, 19:42
von tomate
Könnte mir vielleicht jemand erklären, wie ich wx.CallAfter() kann?
Bisher rufe ich eine Funktion ganz normal auf und lasse mir die Ergebnisse zurückgeben:
Wie würde ich sowas mit einem Thread umsetzen?
Re: Threads - Zwischenergebnisse an GUI übergeben
Verfasst: Donnerstag 11. Oktober 2007, 20:31
von ProgChild
tomate hat geschrieben:Dazu lasse ich die Berechnung in einem eigenen Thread laufen.
Eine bessere lösung wäre es, einfach die GUI upzudaten, wärend deiner Berechnung. Dann musst du dich nicht mit Thread-Synchronisation herumschlagen.
http://www.wxwidgets.org/manuals/2.6/wx ... ppdispatch
Re: Threads - Zwischenergebnisse an GUI übergeben
Verfasst: Donnerstag 11. Oktober 2007, 20:59
von gerold
tomate hat geschrieben:Muss ich mit wx.CallAfter() arbeiten, um mir schon während der Laufzeit Zwischenergebnisse liefern zu lassen?
Hallo tomate!
Ja! Aber dazu brauchst du etwas, was du von deiner arbeitenden Funktion aus an die GUI zurück geben kannst.
Suche hier im Forum nach "wx.CallAfter" und gehe die Suchergebnisse von unten nach oben durch. Nimm dir die Zeit. Dort findest du Anleitungen, Hintergründe und Beispiele.
Wenn du in deiner arbeitenden Funktion eine Schleife durchläufst, so dass du immer wieder zwischendurch die GUI am Laufen halten kannst (mit wx.YieldIfNeeded), dann musst du nicht unbedingt auf einen zusätzlichen Thread ausweichen. Aber ein Thread ist nicht schlecht, dann bleibt die GUI auf jeden Fall flüssig und bedienbar.
mfg
Gerold

Verfasst: Samstag 13. Oktober 2007, 10:04
von tomate
Danke für Eure Hilfe.
Ich denke, ich werde es mit Threads versuchen, damit die GUI bedienbar bleibt.
Normalerweise sollte man ja möglichst GUI und Logik trennen oder?
Ich habe im Moment mein Programm auf zwei Dateien aufgeteilt. Eine für die Logik, eine für die GUI.
Sehe ich es richtig, dass ich wenn ich mit wx.CallAfter arbeite, automatisch auch GUI-Elemente in meiner Logik-Datei einbauen muss?
Mein Problem ist, dass ich in meiner GUI-Datei Klassen aus der Logik-Datei importiere. Aber wenn ich Threads nutzen will, müsste ich aus der Logik-Datei eigentlich auch Zugriff auf die GUI haben oder? Wie mache ich das?
Um mal auf ein
Beispielvon dir zurückzukommen:
Code: Alles auswählen
class MyWorker(threading.Thread):
def __init__(self, finished_function = None):
threading.Thread.__init__(self)
self.canceled = threading.Event()
self.finished_function = finished_function
def run(self):
for i in xrange(10):
if self.canceled.isSet():
print "Gestoppt..."
break
print "Ich arbeite..."
time.sleep(1)
if self.finished_function:
self.finished_function()
def stop(self):
self.canceled.set()
Könnte ich auch in jedem Schleifendurchlauf etwas an die GUI zurückgeben? Also z.b. "SchleifeX"
Verfasst: Samstag 13. Oktober 2007, 12:49
von gerold
Hallo tomate!
Ich habe jetzt keinen Bock auf erklären:
example_lib.py:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
import time
def my_worker(start = 0, stop = 10, step = 1, status_func = None, finished_func = None):
"""
Arbeitende Funktion.
:param start: Gibt die Zahl an, ab der gezählt werden soll.
:param stop: Gibt die Zahl an, bis zu der gezählt werden soll.
:param step: Schrittwert
:param status_func: Wenn angegeben, dann wird eine Funktion erwartet, an die
man als Parameter den Zwischenstand übergeben kann.
Z.B. so: ``status_func("Neuer Zwischenstand")``
:param finished_func: Wird ausgeführt, wenn der Worker mit seiner Arbeit fertig ist.
Es wird eine ausführbare Funktion oder None erwartet.
"""
if not callable(status_func):
status_func = None
for i in range(start, stop, step):
if status_func:
status_func("Zwischenstand: %i" % i)
time.sleep(0.6)
if status_func:
status_func("Fertig")
if callable(finished_func):
finished_func()
def main():
# Testen
def status_func(message):
print message
def finished_func():
print "END"
my_worker(2, 8, status_func = status_func, finished_func = finished_func)
if __name__ == "__main__":
main()
example_gui.py:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
import wx
from thread import start_new_thread
import example_lib
wx.SetDefaultPyEncoding("iso-8859-15")
class MyFrame(wx.Frame):
def __init__(
self, parent = None, title = "Example", size = wx.Size(550, 420)
):
wx.Frame.__init__(self, parent, -1, title, size = size)
panel = wx.Panel(self)
vbox_main = wx.BoxSizer(wx.VERTICAL)
panel.SetSizer(vbox_main)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox_main.Add(vbox, 1, wx.EXPAND | wx.ALL, 5)
txt_status = wx.TextCtrl(
panel, style = wx.TE_MULTILINE | wx.TE_AUTO_SCROLL | wx.TE_READONLY
)
vbox.Add(txt_status, 1, wx.EXPAND | wx.ALL)
self.txt_status = txt_status
btn_start = wx.Button(panel, label = "Start")
vbox.Add(btn_start, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
btn_start.Bind(wx.EVT_BUTTON, self.start_worker)
self.btn_start = btn_start
def show_status(self, message):
"""
Zeigt den neuen Status in der Textbox an.
:param message: Dieser Text wird in der Textbox angehängt.
"""
message = message.rstrip() + "\n"
# Hier findet eine Trennung zwischen den Threads statt
wx.CallAfter(self.txt_status.AppendText, message)
def worker_finished(self):
"""
Wird ausgeführt, wenn der Worker fertig ist. Damit könnte man den
Start-Button wieder aktivieren, falls man diesen vorher deaktiviert
hat.
"""
# Hier findet eine Trennung zwischen den Threads statt
wx.CallAfter(self.btn_start.Enable)
def start_worker(self, event):
"""
Startet den Worker aus dem Modul "example_lib".
"""
# self.btn_start.Disable()
self.txt_status.SetValue("")
start_new_thread(
example_lib.my_worker, (),
dict(
start = 10, stop = 29, step = 2, status_func = self.show_status,
finished_func = self.worker_finished
)
)
def main():
"""Testing"""
app = wx.PySimpleApp()
f = MyFrame()
f.Center()
f.Show()
app.MainLoop()
if __name__ == "__main__":
main()
mfg
Gerold

Verfasst: Samstag 13. Oktober 2007, 13:09
von tomate
Danke!
So wie anderen Beispiel läuft es bei mir mittlerweile auch. Hoffentlich kriege ich den Rest jetzt auch noch hin

Verfasst: Samstag 13. Oktober 2007, 18:13
von tomate
Code: Alles auswählen
start_new_thread(
example_lib.my_worker, (),
dict(
start = 10, stop = 29, step = 2, status_func = self.show_status,
finished_func = self.worker_finished
)
)
Was genau kann ich "start_new_thread" übergeben?
Angenommen my_worker befindet sich in der Klasse "MeineKlasse".
Könnte ich dann eine Instanz dieser Klasse erzeugen, dieser die Parameter (start =10...) übergeben und dann erst start_new_thread aufrufen?
Verfasst: Samstag 13. Oktober 2007, 18:56
von gerold
tomate hat geschrieben:Angenommen my_worker befindet sich in der Klasse "MeineKlasse".
Könnte ich dann eine Instanz dieser Klasse erzeugen, dieser die Parameter (start =10...) übergeben und dann erst start_new_thread aufrufen?
Hallo tomate!
Ja das funktioniert so. Aber das hättest du selber auch ausprobieren können. Und die Hilfe zu start_new_thread findest du hier:
http://docs.python.org/lib/module-thread.html
In etwa so:
Code: Alles auswählen
class MeineKlasse(object):
def __init__(self,...):
...
def my_worker(self):
...
meine_klasseninstanz = MeineKlasse(start = 1, ...)
start_new_thread(meine_klasseninstanz.my_worker, ())
mfg
Gerold

Verfasst: Sonntag 14. Oktober 2007, 14:52
von tomate
Es läuft.
Danke