Binden von Events in wxTreeCtrl

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Ich benutze wxTreeCtrl als Basisklasse für meinen Baum und will es etwas erweitern. Dafür will ich paar Methoden für die Abarbeitung solcher Benutzeraktionen wie 1) left click, 2) right click, 3) double click usw. implementieren.

Mein Problem: ich kann nur das EVT_TREE_SEL_CHANGED an eine Methode binden. Andere Events (wie EVT_TREE_ITEM_RIGHT_CLICK oder EVT_TREE_ITEM_ACTIVATED) kann ich nicht binden (oder mache irgendwas falsch).


CODE AUSGELAGERT: http://paste.pocoo.org/show/91581/


So wie der Code jetzt ist sieht man in der Konsole nur 'in OnSelChanged', auch wenn man left click, right click, double click und die Windows-Menu Tast drückt.

Wenn man die Zeile 52 "wx.EVT_RIGHT_UP( self, self.onRightUp )" am Ende der __init__ wieder frei kommentiert, sieht man in der Konsole "in OnRightUp \ in contextMenu", wenn die Windows-Menu Taste betätigt wird. Maustaste richt klick hat keinen Effekt, sehr wohl aber wenn man ein doubl right click macht.

Wenn man die folgende Zeile 53 "wx.EVT_RIGHT_DOWN( self, self.onRightDown )" frei kommentiert, sieht man "in OnRightDown \ in OnRightUp \ in contextMenu" bei right klick.

So ein Verhalten ergibt für mich absolut keinen Sinn. Ist es verbugt oder verstehe ich das Konzept von Events und Bindings nicht?

Man beachte, dass EVT_RIGHT_UP (wo auch immer der herkommt - ich habe in der Doku keine Klasse gefunden, die dieses Ereignis feuert) keine Hilfe in dem Fall ist, weil es kein Ereignis auf dem Tree ist, sondern ein Maus-Ereignis, von dem ich nicht rauskriegen kann, auf welchem Item gecklickt wurde.

PS hat jemand so eine Art CheckedTreeCtrl implementiert? Die wxWidgets bietet es leider nicht an.


Edit by Gerold: Code ausgelagert
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Hat etwa noch nie einer so einen Baum gebraucht oder gibt es im Moment spannendere Themen?
Dann muss ich mir wohl den Baum selbst bastelln. :?
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Durch den ungeahnten Zufall habe ich den Hacken gefunden: wenn man die id auf 1 bei allen Bindungen setzt, dann funktioniert es irgendwie, und man kann so viele Ereignisse binden wie man will. So in etwa:

Code: Alles auswählen

        self.Bind( wx.EVT_TREE_SEL_CHANGED, self.onSelChanged, id = 1 )
        self.Bind( wx.EVT_TREE_ITEM_RIGHT_CLICK, self.onRightClick, id = 1 )
Was sich wohl die Authoren dabei gedacht hatten? :roll:

Das gibt mir Stoff für die Signatur.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo Sergio!

Ich musste erst aus deinem Wirrwarr herausfinden. Deshalb habe ich den Code umgeschrieben. :? Von welcher Programmiersprache kommst du, dass du ein Leerzeichen nach einer öffnenden Klammer und vor einer schließenden Klammer machst? ...war ganz komisch zu lesen, dein Code. Bitte lies dir das hier http://python.org/dev/peps/pep-0008/ mal durch, dann ist uns allen geholfen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import wx


class TreeView(wx.TreeCtrl):

    def __init__(self, *args, **kwargs):
        wx.TreeCtrl.__init__(self, *args, **kwargs)
        
        root = self.AddRoot('Root')
        os = self.AppendItem(root, 'First')
        pl = self.AppendItem(root, 'Second')
        tk = self.AppendItem(root, 'Third')
        a = self.AppendItem(os, 'A')
        b = self.AppendItem(os, 'B')
        
        #self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
        #self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
        
        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.on_tree_sel_changing)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.on_tree_sel_changed)
        
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.on_tree_item_right_click)
        
        self.ExpandAll()
    
    
    def on_tree_sel_changing(self, event):
        print 'in OnSelChanging'
        print "    is CommandEvent:", isinstance(event, wx.CommandEvent)

    
    def on_tree_sel_changed(self, event):
        print 'in OnSelChanged'
        print "    is CommandEvent:", isinstance(event, wx.CommandEvent)
        
        tree = event.GetEventObject()
        assert isinstance(tree, TreeView) # es ist eine TreeView-Instanz
        
        selected_id = tree.GetSelection()
        selected_item_text = tree.GetItemText(selected_id)
        print "    selected item text:", selected_item_text
    

    def on_tree_item_right_click(self, event):
        print "in EVT_TREE_ITEM_RIGHT_CLICK"
        print "    is CommandEvent:", isinstance(event, wx.CommandEvent)
        
        tree = event.GetEventObject()
        assert isinstance(tree, TreeView) # es ist eine TreeView-Instanz
        
        clicked_item_id = tree.HitTest(event.GetPoint())[0]
        assert isinstance(clicked_item_id, wx.TreeItemId) # es ist eine TreeItemId-Instanz
        
        clicked_item_text = tree.GetItemText(clicked_item_id)
        print "    clicked item text:", clicked_item_text


    #def on_left_down(self, event):
        #print 'in OnLeftDown'
        #print "    is CommandEvent:", isinstance(event, wx.CommandEvent)
        #event.Skip() # da kein CommandEvent


    #def on_right_down(self, event):
        #print 'in OnRightDown'
        #print "    is CommandEvent:", isinstance(event, wx.CommandEvent)
        #event.Skip() # da kein CommandEvent


class MainFrame(wx.Frame):

    def __init__(self, parent = None, title = 'Test'):
        wx.Frame.__init__(self, parent, -1, title, size = wx.Size (600, 500))

        # Ein Panel als Basiscontainer ist **PFLICHT**. Setze niemals Widgets 
        # direkt auf das Frame, außer du weißt was du tust.
        panel = wx.Panel(self)
        panel.SetBackgroundColour(wx.Colour(236, 233, 216, 255))
        
        window_sizer = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(window_sizer)
        
        tree_sizer = wx.BoxSizer(wx.HORIZONTAL)
        window_sizer.Add(tree_sizer, 1, wx.EXPAND )
        
        tree = TreeView(panel, style = wx.TR_HAS_BUTTONS)
        tree_sizer.Add(tree, 1, wx.EXPAND | wx.ALL, 10)


class MainApp(wx.PySimpleApp):

    def OnInit(self):
        frame = MainFrame()
        frame.Center()
        frame.Show()
        return True
    

def main():
    app = MainApp()
    app.MainLoop()


if(__name__ == '__main__'):
    main()
mfg
Gerold
:-)

PS: Und vergiss dieses ``id = 1``. Du bindest mit ``self.Bind`` den Event-Handler bereits an das TreeCtrl. Mit ``id`` überschreibst du die ursprüngliche, richtige Bindung wieder.

Lies dir einfach mal die entsprechenden Kapitel im Buch "wxPython in Action" (=Pflichtlektüre für wxPython-Programmierer) durch. Dann wird dir klarer.

.
Zuletzt geändert von gerold am Dienstag 18. November 2008, 14:54, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sergio hat geschrieben:Was sich wohl die Authoren dabei gedacht hatten? :roll:

Das gibt mir Stoff für die Signatur.
Hallo Sergio!

Urteile nicht über wxPython, bevor du das zugehörige Buch "wxPython in Action" gelesen hast. ;-)

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Ja, "id = 1" ist unnötig. Hab jetzt weggelassen und es funktioniert. :?
Na ja, hast überredet, ich guck gleich bei Amazon, was sich so haben lässt....

Die Leerzeichen sind nicht meine Erfindung und ich hab es vorher nie so geschrieben. Aber als ich die im Code eines Kollegen gesehen habe, fand ich den Code lesbarer. Finde ich immer noch, auch wenn der Style Guide das Gegenteil behauptet. Na, mal schauen.

Danke auf jeden Fall für den Rat (besonders das mit dem Buch).
hap performs existence
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Übrigens, wenn ich ein Panel auf das Frame packe und alles andere auf das Panel, dann sehe ich oft so was:
Bild
Ich muss wohl echt erst ein Buch lesen. :oops:
hap performs existence
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sergio hat geschrieben:Übrigens, wenn ich ein Panel auf das Frame packe und alles andere auf das Panel, dann...
Hallo Sergio!

Das Panel muss das **einzige** Widget sein, welches in den Frame gelegt wird. Sonst nimmt es nicht den ganzen Platz ein. Also hast du irgend ein Widget noch auf "self" statt auf "panel" gelegt. Suche nach "(self", dann findest du den Übeltäter. :-)

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Danke, Gerold. Du hast es wohl schwer drauf. :D
hap performs existence
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Ich stell mich zwar grad dumm an, aber das Buch ist leider noch nicht da. :)

Die Frage bezüglich ItemID.
Wenn ich in Zeile 59 folgendes hinzufüge:

Code: Alles auswählen

        print "    " + str(event.GetItem())
sehe ich auf der Konsole so Sachen wie

Code: Alles auswählen

in EVT_TREE_ITEM_RIGHT_CLICK
    is CommandEvent: True
    clicked item text: Third
    <wx._controls.TreeItemId; proxy of <Swig Object of type 'wxTreeItemId *' at 0x3973ee8> >
in EVT_TREE_ITEM_RIGHT_CLICK
    is CommandEvent: True
    clicked item text: Third
    <wx._controls.TreeItemId; proxy of <Swig Object of type 'wxTreeItemId *' at 0x3971d80> >
Ich habe gedacht, dass ich mit event.GetItem() die eindeutige ID des Baumknoten bekomme. Das ist wohl eher nicht der Fall.
Ist

Code: Alles auswählen

        clicked_item_id = tree.HitTest(event.GetPoint())[0]
die richtige (und einzige) Methode, die Referenz auf den Baumknoten zu bekommen? Und wofür ist dann die "event.GetItem()" da? Es ist doch wohl nicht die ID des events oder?
hap performs existence
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sergio hat geschrieben:die richtige (und einzige) Methode, die Referenz auf den Baumknoten zu bekommen
Hallo Sergio!

Code: Alles auswählen

event.GetItem()
tree.HitTest(event.GetPoint())[0]
Beides sollte ein TreeItemId-Objekt zurück liefern. Dieses Objekt ist keine normale Zahl, sondern ein Objekt mit der zusätzlichen Methode ``IsOk`` um zu prüfen, ob es sich wirklich um die ID eines noch gültigen TreeItems handelt.

HitTest bietet noch ein paar Zusatzinformationen, z.B. ob vor oder hinter das Item geklickt wurde, aber die sind in deinem Fall nicht wichtig.
``event.GetItem`` ist einfacher zu verwenden -- würde ich auch tun, wenn es mir auf die Schnelle eingefallen wäre. :-)

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Sergio
User
Beiträge: 8
Registriert: Donnerstag 13. November 2008, 13:24

Hmm... und warum kriege ich dann unterschiedliche IDs, wenn ich auf das selbe TreeItem zwei mal hinter einander klicke (siehe Beispiel oben)? Das ist ja gerade, was mich verwirrt und stört.
Die IsOk() sagt zwar immer True und ich kann mit der ID das Item bearbeiten, aber ich kann zum Beispiel die ID nicht abspeichern, um diese später wieder zu verwenden.
hap performs existence
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo Sergio!
Sergio hat geschrieben:und warum kriege ich dann unterschiedliche IDs, wenn ich auf das selbe TreeItem zwei mal hinter einander klicke
Du kriegst zwei verschiedene TreeItemId-Objekte mit unterschiedlichen Speicheradressen. Aber beide Objekte zeigen intern auf das selbe TreeItem.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten