Problem mit wx.Timer

Plattformunabhängige GUIs mit wxWidgets.
Antworten
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Hallo,

ich habe für mein Programm verschiedene Funktionen in eine Python Datei (keine Klasse) ausgelagert, einer der Funktionen ist:

Code: Alles auswählen

def getX(self, message='none'):
    self.Log.info('Logmessage: '+message)
in einer weiteren Funktion starte ich einen Timer:

Code: Alles auswählen

    self.timer = wx.Timer(self)
    self.timer.Start(4000, oneShot=False)
    self.Bind(wx.EVT_TIMER, getX(self, message='foo'))
die Funktion wird dann einmalig aufgerufen aber nicht alle 4 Sekunden, woran könnte das liegen?

Grüße
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo,

ich kenne mich mit wx nicht besonders gut aus, aber auf jeden Fall ist die Zeile

Code: Alles auswählen

self.Bind(wx.EVT_TIMER, getX(self, message='foo'))
falsch.

Der zweite Parameter muss eine Funktion oder irgend etwas anderes aufrufbares sein. Du übergibst aber das Ergebnis des Aufrufst ``getX(self, message='foo'))`` als Parameter an Bind. Das kanst du mittels lambda lösen. Etwas schöner wäre ``functools.partial``.

Sebastian
Das Leben ist wie ein Tennisball.
deets

Da faellt einem gleich so manches in's Auge. Statt "none" besser wirklich None nehmen, und darauf testen, und nur was ausgeben wenn "message is not None" ist. CamelCase in Methoden-Namen ist auch nicht PEP8-konform, und etwas in einem Timer zu machen das "get" am Anfang heisst klingt fuer mich komisch.

Doch das ist alles Kosmetik - zum eigentlichen Problem: du rufst deine Funktion ja einfach direkt auf, statt sie als callback zu uebergeben. Da du auch noch parametrisieren willst, kannst du das zb mit partial machen:

Code: Alles auswählen

from functools import partial

self.Bind(.., partial(self.getX, message='foo'))
BlackJack

@red_dust: Das liegt daran, dass *Du* die Funktion aufrufst und dann versuchst deren *Rückgabewert* an das Timer-Ereignis zu binden. Das zweite Argument bei Deinem `Bind()`-Aufruf ist also `None`, weil das der (implizite) Rückgabewert von Deiner `getX()`-Funktion ist. An der Stelle muss eine Funktion übergeben werden, die dann vom Timer aufgerufen werden kann. Ich nehme mal an Du suchst `functools.partial()` um eine solche Funktion aus dem `getX` und den Argumenten zu erstellen.

Ich bin mir auch fast sicher das `Bind()` ist so nicht vollständig, denn da kommt der erstellte Timer nirgends vor.

Edit: Verdammt, zu langsam. :-(
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Danke schon mal für die Hilfe, das mit der Funktion habe ich jetzt verstanden
ich habe den Aufruf wie folgt korrigiert:

Code: Alles auswählen

    self.timer = wx.Timer(self)  
    self.timer.Start(4000, oneShot=False)
    self.Bind(wx.EVT_TIMER, partial(getX, message='test'), self.timer)
Jetzt bekomme ich allerdings den folgenden Fehler:

Code: Alles auswählen

AttributeError: 'TimerEvent' object has no attribute 'Log'
:?:

wenn ich übrigens ein self. vor die Funktion schreibe bekomme ich folgenden Fehler:

Code: Alles auswählen

AttributeError: 'MyProgram' object has no attribute 'getX'
woran liegt das?
Zuletzt geändert von red_dust am Montag 23. Juli 2012, 13:22, insgesamt 1-mal geändert.
lunar

Code: Alles auswählen

self.Bind(wx.EVT_TIMER, partial(self.getX, message='test'), self.timer)
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

wenn ich denn Code so übernehme:

Code: Alles auswählen

self.Bind(wx.EVT_TIMER, partial(self.getX, message='test'), self.timer)
dann bekomme ich die Fehlermeldung:

Code: Alles auswählen

AttributeError: 'MyProgram' object has no attribute 'getX'
was ja eigetnlich klar ist, da self ja keine Instanz meines Funktionen-Containers ist, aber wie löse ich das Problem?
Ist mein Konzept schon von Grund auf falsch, die Funktionen in einer eigenen Datei auszulagern?
BlackJack

@red_dust: Dann ersetze ``self`` halt mit einer Referenz auf das passende Objekt. Aber was ist denn bitte ein „Funktionen-Container”? Und wenn `getX()` eine Funktion ist, warum heisst das erste Argument dann `self`? Das ist sehr verwirrend.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Mit funktionen-Container meine ich:
ich habe für mein Programm verschiedene Funktionen in eine Python Datei (keine Klasse) ausgelagert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das nennt man "Modul" ;-)
Das Leben ist wie ein Tennisball.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Ah okay, verstanden, sorry ich habs nicht so mit der Fachsprache.
Also mein Modul heißt HDDUpdate.py in diesem habe ich meine Funktionen gesammelt.
Hier http://tutorial.pocoo.org/modules.html steht, dass ich einfach per Modulname.Funktion auf die Funktionen zugreifen kann, der Code:

Code: Alles auswählen

self.Bind(wx.EVT_TIMER, partial(HDDUpdate.getx, message='foo'), self.timer)
liefert aber auch eine Fehlermeldung:

Code: Alles auswählen

NameError: global name 'HDDUpdate' is not defined
lunar

@red_dust: Du musst das Modul natürlich auch importieren. Da es Dir aber offensichtlich an den entsprechenden Grundlagen fehlt, würde ich Dir für den Anfang raten, auf ein separates Modul zu verzichten, alle Funktionen in einer Datei zu belassen, und das Tutorial der Python-Dokumentation bis zum Schluss durchzuarbeiten.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

dass ich das Modul importieren muss ist mir klar, aber ich übergebe ja innerhalb des Moduls die Funktion getx an den Timer-Event Handler und nicht in meiner Hauptklasse.
lunar

@red_dust: Aha… ich glaube, jetzt kommt niemand mehr mit. Zeige uns bitte den vollständigen Quelltext. Du kannst ihn beispielsweise auf https://gist.github.com/ hochladen.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

Puhh das Programm ist schon ziemlich umfangreich,
im Prinzip habe ich meine Hauptklasse run.py
https://gist.github.com/3169445

in der ich per InitWidgetLists und OnInit (beide im Modul Widgets.py) meine Gui aufbaue und die Events zuweise.

in dem Modul OnEventsFunctions habe ich dann die Funktionen die aufgerufen werden wenn interagiert wird.
https://gist.github.com/3169436

darin wird dann das Modul HDDUpdate aufgerufen in dem meine tatsächlichen Funktionen hinterlegt sind
https://gist.github.com/3169428

Hoffe ihr könnt mir damit weiterhelfen
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

ist es ratsam aus meinem Modul hddupdate eine eigene Klasse zu machen?
deets

es ist vor allem ratsam sich zu entscheiden, was HDDUpdate-Funktionen sein sollen - Funktionen? Dann gehoert dort kein self rein. Oder Methoden? Dann muessen sie in eine Klasse, und bei den Timer-callbacks muss dann ein Objekt davon existieren.
red_dust
User
Beiträge: 16
Registriert: Montag 9. Juli 2012, 10:56

okay wenn ich bei den Funktionen bleibe woran knüpfe ich dann das

Code: Alles auswählen

.Bind(...
?
deets

Na, wie rufst du die Funktion denn auf *ohne* Timer?
Antworten