Strukturproblem beim einbinden von TreeCtrl

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Vannid Burdon
User
Beiträge: 12
Registriert: Freitag 22. Dezember 2006, 14:51

Hallo zusammen,

Ich habe ein TreeCtrl Element als "Custom Ctrl" Element eingebunden welches, wenn man auf eine Node klickt, die entsprechenden Daten in einem Notebook Tab laden soll. Wie stelle ich das am besten an? Ich habe die verschiedenen Events in der TreeCtrl Klasse definiert (z.b OnSelChanged) dort frage ich auch ab, welche Node gerade angeklickt wurde. In der Hauptklasse, wo ich die ganzen Ctrls einbinde, liegt auch mein Notebook somit auch mein Tab Handle. Muss ich nun das Tab Handle/Notebook an das TreeCtrl übergeben oder muss ich die Events für das TreeCtrl alle in der Hauptklasse definieren? Stehe auf dem Schlauch.

Gruss,

Vannid Burdon
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi und willkommen im Python Forum.

Hab folgendes kurz geschrieben. Ich hoffe das Hilft dir weiter :)

Der kern ist halt das du die Daten, zu jeder Node im Tree, halt der Node hinzufügst. Bei abfragen holst du die Daten der Entsprechenden Node und gibst die dann wo immer du willst aus.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os 
import sys
from itertools import izip, count


import wx
wx.SetDefaultPyEncoding("utf-8")

class MainFrame(wx.Frame):
    def __init__(self, parent=None, id=-1, title = "MyApp"):
         wx.Frame.__init__(self, parent, id, title)
         self.Centre()
         self.panel = wx.Panel(self)
         
         # TreeCtrl
         self.tree = wx.TreeCtrl(
             parent = self.panel, 
             id = -1, 
             style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_VARIABLE_ROW_HEIGHT
         )
         # Rechtsklick event an `self.tree binden`
         self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.TreeRightClick)
         
         # Notebook
         self.notebook = wx.Notebook(
             parent = self.panel,
             id = -1,
         )
         self.text_ctrl = wx.TextCtrl(self.notebook, style = wx.TE_READONLY |
                                                             wx.TE_MULTILINE)
         self.notebook.AddPage(
             page = self.text_ctrl,
             text = "test", 
             select = True, 
             imageId = -1
         )
         
         # Sizer
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         sizer.Add(self.tree, 1, wx.EXPAND)
         sizer.Add(self.notebook, 1, wx.EXPAND)
         self.panel.SetSizerAndFit(sizer)
         
         #
         # Tree mit werten Füllen!
         #
         
         # Datentype definieren.
         my_data = list()
         
         for i in xrange(100):
             my_data.append({
                 'text': 'foo %d' % i,
                 'value': i * i
                 # usw...
             })
         
         # Nun denn Tree erzeugen.
         self.root = self.tree.AddRoot("Tree-Trunk")
         for data, cntr in izip(my_data, count(1)):
             # Neue Node zur root hinzufügen.
             child = self.tree.AppendItem(self.root, "node %d" % cntr)
             # So, nun fügen wir dieser Node daten hinzu :)
             self.tree.GetItemData(child).SetData(data)
         
         # Tree aufklappen...
         self.tree.Expand(self.root)
    
    def TreeRightClick(self, event):
        # So nun erstmal die Child-Node holen.
        child = event.GetItem()
        # Wenn diese node nicht die root ist...
        if child != self.root:
            # so nun die Daten holen :)
            data = self.tree.GetItemData(child).GetData()
            print data
            
            # Daten von dieser Node ins `self.text_ctrl` vom `self.notebook` 
            # anhängen.
            self.text_ctrl.AppendText(
                "text = %s; value = %d\n" % (data['text'], data['value'])
            )

def main():
    app = wx.PySimpleApp()
    mf =  MainFrame()
    mf.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()
lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Vannid Burdon hat geschrieben:[...]Muss ich nun das Tab Handle/Notebook an das TreeCtrl übergeben oder muss ich die Events für das TreeCtrl alle in der Hauptklasse definieren? Stehe auf dem Schlauch.
Ach so :? Du übergibst als Parameter den Notebook an die Klasse die das TreeCtrl hat. Mit der Methode `self.notebook.GetPage(nummer_der_page)` kommst du dann an das Objekt der page (z.B. das TextCtrl in meinen Beispiel). Dann kannst du halt die Daten dort eintragen/Aktualisieren.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Mit der Methode `self.notebook.GetPage(nummer_der_page)` kommst du dann an das Objekt der page (z.B. das TextCtrl in meinen Beispiel). Dann kannst du halt die Daten dort eintragen/Aktualisieren.
Also so...

Code: Alles auswählen

def TreeRightClick(self, event):
        # So nun erstmal die Child-Node holen.
        child = event.GetItem()
        # Wenn diese node nicht die root ist...
        if child != self.root:
            # so nun die Daten holen :)
            data = self.tree.GetItemData(child).GetData()
            print data
            
            # Page (`self.text_ctrl`) vom `self.notebook` holen und
            # daten anhängen.
            text = self.notebook.GetPage(0) #<-- Page holen (`self.text_ctrl`)
            text.AppendText(
                "text = %s; value = %d\n" % (data['text'], data['value'])
            )
Vannid Burdon
User
Beiträge: 12
Registriert: Freitag 22. Dezember 2006, 14:51

Hallo sape, erstmal Danke für die Antwort aber das trifft mein Problem noch nicht ganz.
Ach so Confused Du übergibst als Parameter den Notebook an die Klasse die das TreeCtrl hat. Mit der Methode `self.notebook.GetPage(nummer_der_page)` kommst du dann an das Objekt der page (z.B. das TextCtrl in meinen Beispiel). Dann kannst du halt die Daten dort eintragen/Aktualisieren.
Das ist, was ich mich frage, ob es eine gute Lösung wäre.

Code: Alles auswählen

class PluginTreeCtrl(wx.TreeCtrl):
    def __init__(self, *args, **kwds):
        
        self.tree = wx.TreeCtrl.__init__(self, *args, **kwds)

        #self.parent = parent
        # {path:name}
        self.module_container = {}
        # (obj,name)
        self.module_objects = []
    
        root = self.AddRoot("Plugins")
        self.parent = root
        self.backup = self.parent
        self._search("Plugins", [".py"])
        self._load()
        
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)

    def _search(self, dir, types):
        for entry in os.listdir(dir):# jedes objekt im verzeichnis
            path = os.path.join(dir, entry)
            if os.path.isdir(path): #objekt ist ein verzeichnis
                old_dir = self.parent
                self.parent = self.AppendItem(self.parent,entry)
                self._search(path, types)
                # rekursiver auslauf
                self.parent = old_dir
            for type in types: # objekt ist eine datei
                if path.endswith(type):
                    entry = os.path.splitext(entry)[0]
                    self.AppendItem(self.parent,entry)
                    self.module_container[os.path.split(path)[0]] = entry

    def _load(self):
        for path, name in self.module_container.items():
            print path, name
            try:
                (file, path, desc) = imp.find_module(name, [path])
            except ImportError:
                continue

            if file is None:
                continue

            try:
                self.module_objects.append((imp.load_module(name, file, path, desc), name))
            finally:
                file.close()



    def OnSelChanged(self, event):
        selectedItem = self.GetItemText(event.GetItem())
        for j,i in self.module_objects:
            if selectedItem == i:
                pass# später

Code: Alles auswählen

class Hauptklasse:
....
self.ControlPanel = wx.Notebook(self.SplitterPanelRight, -1, style=0)
self.window_1 = PluginTreeCtrl(self.SplitterPanelLeft, -1, style=wx.TR_HAS_BUTTONS|wx.TR_LINES_AT_ROOT|wx.TR_DEFAULT_STYLE|wx.SUNKEN_BORDER)
...
Das ist meine "Custom TreeCtrl" Klasse, welche ich in die "Hauptklasse", wo unteranderem das Notebook definiert ist, aufgerufen wird.

Ist es nun eine gute Idee das Handle des Notebooks in die PluginTreeCtrl Klasse zu übergeben um somit in der OnSelChanged() den jeweiligen Inhalt der aktuellen Node in dem Notebook-Tab auszugeben?

Oder muss ich mit dem Handle der definierten PluginTreeCtrl Klasse in der Hauptklasse weiterarbeiten?

In deinen Beispielen verwendest du das TreeCtrl nur in einer einzigen Klasse und definierst dort alle Events der TreeCtrl Klasse. Wie gesagt dort stehe ich immer noch auf dem Schlauch.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Oh, OK.

So wie ich deine Klasse verstehe (Korrigiere mich wenn ich falsch liege), listet es Plugins ein und stellt in jeder Node diverse Daten bereit, die man sich anzeigen lassen kann. Das Ziel wo die Daten angezeigt werden, sollte Flexibel sein und nicht nur auf eine Page von nem `wx.Notebook` beschränkt sein. Der Nutzer deine Klasse sollte also selber entscheiden wo er die Daten ausgeben will. Daher...

Mein Gefühl sagt mir das ich lieber mit dem Handle der `PluginTreeCtrl` arbeiten würde die mir alle benötigten Daten liefert. Also konkret gesagt, nicht das `self.ControlPanel`-Handle an `self.window_1` weiterreichen.

Das `wx.EVT_TREE_SEL_CHANGED` event würde ich Zusätzlich in `Hauptklasse` noch mal Binden um dann an die Benötigten Daten zu kommen die dann an der betreffenden Page vom `self.ControlPanel` "gesendet" werden.

Code: Alles auswählen

class Hauptklasse:
    def __init__(...):
        ...
        self.ControlPanel = wx.Notebook(self.SplitterPanelRight, -1, style=0)
        self.window_1 = PluginTreeCtrl(
            self.SplitterPanelLeft, 
            id = -1,    
            style=wx.TR_HAS_BUTTONS |
                     wx.TR_LINES_AT_ROOT |
                     wx.TR_DEFAULT_STYLE |
                     wx.SUNKEN_BORDER
        )
        self.window_1.Bind(wx.EVT_TREE_SEL_CHANGED,  self.OnSelChanged)
    
    def OnSelChanged(self, event):
        # Hier dann alles holen und die Daten dan an den gewünschten
        # Empfänger (z.B. Page von...) im Abschluss senden.
        selectedItem = self.GetItemText(event.GetItem())
        for j,i in self.window_1.module_objects:
            if selectedItem == i:
                pass# später
Dadurch ist `PluginTreeCtrl` unabhängig und kann als eigenständiges Widget in verschiedene wx-Programmen genutzt werden. So kann der Empfänger der Daten einer Node alles beliebige sein und nicht nur eine Page von `wx.Notebook`. Wenn du jetzt in der `PluginTreeCtrl` Klasse es zum Bestandteil machst (Durch rüberreichen des Handles...), das der Empfänger immer eine Page von `wx.Notebook` ist, verliert dein Widget an Flexibilität.

Es ist halt, wie du schon erkennst, eine frage wie wiederverwendbar und flexibel dein Widget sein soll.

lg
Vannid Burdon
User
Beiträge: 12
Registriert: Freitag 22. Dezember 2006, 14:51

Danke sape!

:)
Antworten