@mintoxis: Als erstes solltest Du von dem Vorhaben Abstand nehmen eine globale Variable verwenden zu wollen. Das tust Du in dem gezeigten Quelltext mit `confirmed` auch gar nicht, denn `CommunicatingFrame.OnSpace()` verändert `confirmed` auf Modulebene gar nicht sondern hat einen eigene lokalen Namen der nichts mit dem auf Modulebene zu tun hat.
Und statt das jetzt tatsächlich global zu machen, sollte eher die ebenfalls modulglobale `gui` in einer Hauptfunktion verschwinden. Und nicht `gui` heissen denn das `MyApp`-Exemplar selbst ist keine GUI.
Wenn das `MyApp`-Objekt in einer Funktion verschwunden ist, fällt auf das `Background.DoSomething()` einfach so aus dem Nichts darauf zugreifen will. Werte die in Funktionen oder Methoden verwendet werden sollten immer als Argumente in die Funktion/Methode gelangen, ausser es handelt sich um Konstanten. Sonst hat man sehr schnell ein unüberschaubares Geflecht von Abhängigkeiten die den Code schwer nachvollziebar, schlecht testbar, und irgendwann unwartbar machen.
Da das konkrete `wx.App`-Exemplar ein Singleton ist und das von `wx` auch durchgesetzt wird, gibt es die Funktion `wx.GetApp()` um (fast) jederzeit und überall an dieses Objekt zu kommen.
Die Klasse `Background` ist überflüssig. Eine Klasse die keine `__init__()` besitzt, also weder direkt noch vererbt, ist schon ein recht starker „code smell”. Aber auch Klassen mit `__init__()` die nur eine weitere Methode besitzen sind in der Regel nur aufgeblasene Funktionen.
Wenn Code im Hintergrund, also parallel zur GUI-Hauptschleife ausgeführt werden soll, dann braucht man Threads, und all die Probleme die man sich damit einhandelt. Zum Beispiel das man nur vom Hauptthread aus die GUI verändern darf, weil GUI-Rahmenwerke in der Regel nicht „thread safe” sind. Das ist auch bei `wx` so. Dort sind Ereignisse „thread safe”, dass heisst die können zur Kommunikation zwischen Threads verwendet werden. Und man kann `wx.CallAfter()` und `wx.CallLater()` verwenden um Aufrufe aus anderen Threads über die GUI-Hauptschleife indirekt abzusetzen. Aus der Funktion die im Hintergrund läuft direkt so etwas wie Fenster erstellen ist nicht möglich. Das muss man alles in dem Thread mit der GUI-Hauptschleife erledigen.
`wx` bietet ein paar Konstrukte zur Unterstützung an. Zum Beispiel das `wx.lib.delayedresult`-Modul das es auf einfache Weise erlaubt eine Funktion asynchron auszuführen an deren Ende dann eine Rückruffunktion mit dem Ergebnis aufgerufen wird.
Um Bedingungen bei ``if`` & Co gehören keine Klammern. Und statt zweimal den Tastencode vom `Event`-Exemplar abzufragen könnte man das auch nur einmal tun und dann mit dem ``in``-Operator testen ob der Tastencode in einer Liste enthalten ist.
Man sollte keine literalen Zahlen als Wahrheitswerte verwenden. `wx.App` erwartet als erstes Argument einen Wahrheitswert, was man deutlich besser sieht wenn dort `False` statt 0 übergeben wird. Da `False` der Defaultwert ist, kann man sich das aber auch komplett sparen.
Die Vorsilbe `my` ist in 99,9% der Fälle unsinniges Beiwerk was keinerlei Mehrwert hat und deshalb weggelassen werden sollte.
Das `__frequency`-Klassenattribut hat dort nichts zu suchen und sieht auch nach so einer halbglobalen Geschichte aus die man gar nicht erst versuchen sollte. Doppelte führende Unterstriche machen dort auch keinen Sinn.
Ich komme dann ungefähr bei so etwas heraus (nur oberflächlich getestet):
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import time
import wx
from wx.lib.delayedresult import startWorker
def do_something():
print('This should run in background.')
time.sleep(5)
print('Background process done.')
class ConfirmationFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(400, 200))
panel = wx.Panel(self, wx.ID_ANY, style=wx.NO_BORDER)
panel.SetFocus()
panel.Bind(wx.EVT_CHAR, self.on_key)
self.confirmed = False
def on_key(self, event):
if event.GetKeyCode() in [wx.WXK_SPACE, wx.WXK_RETURN]:
self.SetTitle('Confirmed!')
self.confirmed = True
print(self.confirmed)
class MainFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(800, 600))
panel = wx.Panel(self, wx.ID_ANY, style=wx.NO_BORDER)
panel.SetFocus()
self.start_button = wx.Button(panel, id=wx.ID_ANY, label='Start Test')
self.start_button.Bind(wx.EVT_BUTTON, self.on_button)
self.confirmation_frame = None
def on_button(self, _event):
print('Starting background process.')
self.start_button.Enable(False)
assert self.confirmation_frame is None
self.confirmation_frame = ConfirmationFrame(
None,
wx.ID_ANY,
'Background process running, press SPACE to set the variable'
" confirmed to 'True'..."
)
self.confirmation_frame.Show(True)
wx.GetApp().SetTopWindow(self.confirmation_frame)
startWorker(self.handle_result, do_something)
def handle_result(self, result):
if self.confirmation_frame:
self.confirmation_frame.Destroy()
self.confirmation_frame = None
self.start_button.Enable(True)
print(result.get())
def main():
app = wx.PySimpleApp()
frame = MainFrame(None, wx.ID_ANY, 'Example')
frame.Show(True)
app.SetTopWindow(frame)
app.MainLoop()
if __name__ == '__main__':
main()