WXPython-Probleme mit Events

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Hallo,
ich verstehe bei den Events etwas nicht. Ich kann ja in einem Event eine Funktion bestimmen die ausgeführt wird. Ich kann dieser aber keine Optionen übergeben!?!
Nun müsste ich das aber in einem Fall. Ich habe es dann umständlich gelöst.

Code: Alles auswählen

for i in self.lines:
                self.LoadImg(default_img,i)
                self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, self.OnClick)
Ich erzeuge in dieser Schleife mehrere Bilder. Wenn man auf diese Klickt wird eine Funktion OnClick ausgelöst, die aber "i" wissen müsste...
Ich habe das dann so gemacht, dass ich dem Bild die selbe id gab wie die Zahl die i enthält (darum übergebe ich an LoadImg i). Die id kann ich ja dann mit dem wxEvent.GetId bekommen.
Das halte ich aber für keine elegante Lösung.... Und was wenn i keine Zahl wäre in einem anderen Fall? Dann könnte ich das nicht über die ID lösen.
Hat da wer Rat??
Danke
Stolzi
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Wenn ich dich richtig verstehe, kannst Du lambda verwenden:

Code: Alles auswählen

self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, lambda e: self.OnClick(i))
Gruß, mawe
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Hmm, nein geht nicht:
Vielleicht sollte ich den Fall etwas genauer schildern... In der Schleife werden mehrere Bilder geladen. Die dann durch das Klicken auf das Bild geändert werden können. Dazu muss ich wissen auf welches Bild geklickt wurde.
Hier ist meine Notlösung in der ich wie gesagt den Umweg über die id nahm. Wie gesagt habe ich den 2. Parameter von LoadImg auch als id hergenommen und so konnte ich dann in der OnClick Funktion die id des Bildes übergeben und wusste somit welches gemeint ist:

Code: Alles auswählen

for i in self.lines: 
                self.LoadImg(default_img,i) 
                self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, self.OnClick)
...................

def OnClick(self,evt):
        id=evt.GetId()
        self.LoadImg(self.dir,id)
Dann nach deinem Vorschlag so:

Code: Alles auswählen

for i in self.lines: 
                self.LoadImg(default_img,i) 
                self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, lambda e: self.OnClick(i))
...................

def OnClick(self,evt):
        self.LoadImg(self.dir,evt)
evt enthält dann ja das übergebene i. Leider referenziert es aber dann komischerweise immer auf das letzte erzeugte Bild... Kanns mir leider nicht zusammendenken warum, da mir das lambda neu ist...
Danke
Stolzi
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Hab gerade beim wx-Demo DragImage.py gesehen. Ist das nicht in etwa was Du brauchst?

Gruß, mawe
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Nein ich denke nicht.
Die Funktion LoadImage macht nur folgendes:

Code: Alles auswählen

def LoadImg(self,path,i):
        if os.path.isfile(path):
           try:
               self.bitconst = wx.Image(path, wx.BITMAP_TYPE_ANY)
               self.bitconst = self.bitconst.Scale(self.thumb['width'],self.thumb['height'])
           except:
               self.bitconst = wx.EmptyImage(self.thumb['width'],self.thumb['height'])
        else:
           self.bitconst = wx.EmptyImage(self.thumb['width'],self.thumb['height'])
        self.bitconst=wx.BitmapFromImage(self.bitconst)
        if self.load[i]==0:
           self.bitmap[i] = wx.StaticBitmap(self.panel, i, self.bitconst)
           self.load[i]=1
        else:
           self.bitmap[i].SetBitmap(self.bitconst)
Sie ladet ein Bild self.bitmap. Und ich brauche halt den Bezug i dann beim Event, dass ich das richtige Bild überschreibe.
Verstehst du was ich meine?
Nochmal was das Script tun soll:
Bilder werden in der Schleife erzeugt. Beim Klick auf ein Bild wird ein neues über das alte geladen (das ich per File Dialog auswähle, aber das ist ja nebensächlich). Und da brauche ich halt die Referenz zum alten damit self.bitmap das richtige Bild überschreibt.
Kompilziert zu erklären, aber eigentlich ein einfaches Vorhaben ;-) Wenn ich noch anders erklären soll bitte sagen.
Danke
Stolzi
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!
Stolzi hat geschrieben: Kompilziert zu erklären
Ja :D
Also wenn ich's jetzt richtig verstanden habe ist das Problem folgendes:
Wenn Du auf ein Bild klickst, soll das Programm wissen, welches Bild das ist, oder? Ich hab mal etwas versucht:

Code: Alles auswählen

import wx

class TestFrame(wx.Frame):
	def __init__(self,parent,size):
		wx.Frame.__init__(self,parent,-1,"",(0,0),size)
		self.bitmap = []
		x,y=20,20
		for i in range(10):
			self.DrawImg("homer.bmp",i,(x*3*i,y))

	def DrawImg(self,path,i,pos):
		self.bitconst = wx.Image(path,wx.BITMAP_TYPE_ANY)
		self.bitconst = wx.BitmapFromImage(self.bitconst)
		self.bitmap.append(wx.StaticBitmap(self,-1,self.bitconst,pos))
		self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, lambda e:self.OnClick(i))
	
	def OnClick(self,i):
		self.bitmap[i].Hide()

class MyApp(wx.App):
	def OnInit(self):
		t = TestFrame(None,wx.Size(700,500))
		t.Show(True)
		self.SetTopWindow(t)
		return True

app = MyApp(0)
app.MainLoop()
Hier verschwindet immer das angeklickte Bild. Hilft das?

Gruß, mawe
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

*g* sehr komisch.
Wenn ich die Events direkt in der Schleife erzeugte, dann wurde beim Klick auf ein Bild immer das letzte i übergeben. Wenn ich sie jedoch wie du in einer seperaten Funktion erzeugte, dann funktioniert es. Gut dass ich da überhaupt nicht mitkomme. Kommt doch aufs gleiche raus, oder?
So mit dem lambda komme ich nun aber nicht wirklich mit. Auch der kurze Text in der Python Doku brachte keinen Aufschluss. Ok macht eine Funktion in einer Zeile, oder so, aber warum funktioniert das was du mir da gezeigt hast, was tut es?
Wohin verschwindet dann eigentlich das Event Objekt? Was wenn ich das auch noch brauche??
Das kann doch aber nicht der übliche Weg sein sowas zu machen, oder? Glaubst du es ist normal nicht gedacht einem Event eine Variable mitzugeben??
Danke
Stolzi
PS:Ich hoffe das waren nicht zu viele Fragen in aller Früh ;-)
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Guten Morgen Stolzi!
Stolzi hat geschrieben: Gut dass ich da überhaupt nicht mitkomme.
Da sind wir schon zu zweit :D
Wegen dem Event: Der wird bei meiner Version schon vom lambda abgefangen

Code: Alles auswählen

lambda e: ....        # e... Event
Wenn Du es später noch brauchst, kannst Du e ja der Funktion mitgeben:

Code: Alles auswählen

lambda e: self.OnClick(e,i)
Ob das der normale Weg ist weiß ich auch nicht. Funktioniert aber (ist das nicht die Hauptsache? :D) Es ist jedenfalls (glaube ich) die einzige Möglichkeit einem Event ein Argument mitzuschicken.
Bei dem Demo von dem ich oben gesprochen habe, machen sie das ja etwas anders. Zuerst die Position bestimmen, wo man mit der Maus geklickt hat, die Rechteckskoordinaten von jedem Bild bestimmen, dann nachsehen ob die Mausposition in einem der Rechtecke war.
Irgendwie umständlich, weil immer alle Bilder behandelt werden müssen, aber vielleicht gibt's da andere Vorteile?

Gruß, mawe
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Hast auf die Frage mit dem lambda auch noch eine Antwort? Würde gerne verstehen was das genau macht...
Danke
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

mawe hat geschrieben:

Code: Alles auswählen

lambda e: self.OnClick(e,i)
Hi. Obiges Beispiel macht dasselbe wie dieses hier:

Code: Alles auswählen

def eine_funktion(e):
    self.OnClick(e,i)
Es wird eine lokale Funktion definiert (wird ja erst in der Schleife erzeugt) und die wird gleich an eventhandler übergeben. Also ist der Name "eine_funktion" egal, da er danach nicht mehr benutzt wird. Dafür eignet sich lambda ganz prima (einzige Einschränkung: es dürfen nur Ausdrücke verwendet werden, keine expressions)... Du kannst ja mal in der Komandozeile den Unterschied zwischen f1 und f2 testen:

Code: Alles auswählen

def f1(x):
    return x+1
f2=lambda x: x+1
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Milan hat's ja schon gut erklärt. Wenn Du mehr über funktionale Programmierung mit Python wissen willst, schau mal da:
fp1, fp2, fp3

Gruß, mawe
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi zusammen,

ich hab gerade einen alten Beitrag von mir gesucht, ich wusste doch daß ich da auch mal was zu lambda geschrieben hatte.
http://python.sandtner.org/viewtopic.ph ... ght=lambda

Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
intruder0815

Hallo,

hab mal das Beispiel von mawe einwenig
modifiziert. Wenn man jetzt auf das Bild klickt, dann
wird halt der Index des Bildes geprintet und man könnte
über die Liste (self.bitmap) alle Sachen mit diesem
Bild machen.

Code: Alles auswählen

import wx 

class TestFrame(wx.Frame): 
    def __init__(self,parent,size): 
        wx.Frame.__init__(self,parent,-1,"",(0,0),size) 
        
        self.bitmap = []
        x,y=20,20 
        for i in range(10): 
            self.DrawImg("Python.bmp",i,(x*3*i,y)) 

    def DrawImg(self,path,i,pos): 
        self.bitconst = wx.Image(path,wx.BITMAP_TYPE_ANY) 
        self.bitconst = wx.BitmapFromImage(self.bitconst) 
        self.bitmap.append(wx.StaticBitmap(self,-1,self.bitconst,pos))        
        
        wx.EVT_LEFT_DOWN(self.bitmap[i], lambda e:self.OnClick(i))
    
    def OnClick(self, i): 
        print i

class MyApp(wx.App): 
    def OnInit(self): 
        t = TestFrame(None,wx.Size(700,500)) 
        t.Show(True) 
        self.SetTopWindow(t) 
        return True 

app = MyApp(0) 
app.MainLoop() 
Vielleicht hilft das ja weiter.

Gruß
Christian
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Ok das nächste mal suche ich erst ;-) Der beitrag von Dookie wäre wohl genug gewesen. Danke auf alle Fälle
@mawe: deine Links funzen nicht :-(
@intruder0815: ich kann keinen Unterschied feststellen, als den dass du für das Event die alte schreibweise verwendet hast. Oder hab ich nicht genau genug geschaut?
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi Stolzi,

das mit den Links, liegt an der neuen Unterteilung der GUI-Foren. Die müssen erst angepasst werden. Aber ich denk, jetzt blickst beim Lambda auch soweit durch um es zu verwenden.


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Wegen den Links: Das sind externe. Kann die Seiten derzeit aber auch so nicht laden. Vielleicht ein Problem mit dem dortigen Server?

Gruß, mawe
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Werds später nochmal versuchen.
Juhuuu ich glaub der Stolzi hats nun gelöst. Das dürfte der einfachste Weg sein denke ich. Hat mir einfach keine Ruhe gelassen.
Hab Mawes Beispiel ein wenig modifiziert. Sollte selbsterklärend sein:

Code: Alles auswählen

import wx

class TestFrame(wx.Frame):
    def __init__(self,parent,size):
        wx.Frame.__init__(self,parent,-1,"",(0,0),size)
        self.bitmap = []
        x,y=20,20
        for i in range(10):
            self.DrawImg("C:\Dokumente und Einstellungen\Administrator\Desktop\Python\phpEditor\DSCF0009.jpg",i,(x*4*i,y))

    def DrawImg(self,path,i,pos):
        self.bitconst = wx.Image(path,wx.BITMAP_TYPE_ANY)
        self.bitconst = wx.BitmapFromImage(self.bitconst)
        self.bitmap.append(wx.StaticBitmap(self,-1,self.bitconst,pos))
        self.bitmap[i].Bind(wx.EVT_LEFT_DOWN, self.OnClick)
        self.bitmap[i].var="Hello World! "+str(i)

    def OnClick(self,evt):
        obj=evt.GetEventObject()
        print obj.var
        obj.Hide()

class MyApp(wx.App):
    def OnInit(self):
        t = TestFrame(None,wx.Size(700,500))
        t.Show(True)
        self.SetTopWindow(t)
        return True

app = MyApp(0)
app.MainLoop()

Der Schlüssel wäre GetEventObject() gewesen. Warum einfach wenns auch kompliziert geht ;-)? Haben wir wohl alle übersehen.
Antworten