wxTreeCtrl OnCompareItem

class GeneralTreeCtrl(object):
   def __init__(self, wxTreeCtrl):
      self.tree = wxTreeCtrl

   def __getattr__(self, name):
       return self.tree.__getattribute__(name)

class TreeCtrl(GeneralTreeCtrl):
   #inherit from GeneralTreeCtrl
   def __init__(self, wxTreeCtrl):
      GeneralTreeCtrl.TreeCtrl.__init__(self, wxTreeCtrl)

How do I override the OnCompareItems if my TreeCtrl classes are like above?
TreeCtrl is inherited from GeneralTreeCtrl, but GeneralTreeCtrl doesn't inherit
from wxTreeCtrl.

My classes are in such a away because I use GUI designer. After the layout
stage, then only I wrap the wxTreeCtrl using my class, this allow me not to
change the wxTreeCtrl attribues set in the designer.

According to my experience with wxPython, you shouldn't be able to add
either of the above as visual elements in any wxPython application. Why?
Simply beacuse neither TreeCtrl nor GeneralTreeCtrl subclass from
wx.Control.

If you want to use the output from some designer application, there are
other, better ways. Say, for example, that the output of your GUI
designer was a class called TreeCtrl in a file called CustomTreeCtrl.py

In the file that you write, you can do the following...

import CustomTreeCtrl

class TreeCtrl(CustomTreeCtrl.TreeCtrl):
    def __init__(self, ...):
        CustomTreeCtrl.TreeCtrl.__init__(self, ...)
        ...
    ...

You can then override methods as necessary, add functionality as
necessary, etc. If you ever need to call a method of the
CustomTreeCtrl.TreeCtrl class, you can do precisely as you did with the
__init__ call...

    CustomTreeCtrl.TreeCtrl.BaseMethod(self, ...)

- Josiah

···

Galvin <mygalvin@yahoo.com> wrote:

class GeneralTreeCtrl(object):
   def __init__(self, wxTreeCtrl):
      self.tree = wxTreeCtrl

   def __getattr__(self, name):
       return self.tree.__getattribute__(name)

class TreeCtrl(GeneralTreeCtrl):
   #inherit from GeneralTreeCtrl
   def __init__(self, wxTreeCtrl):
      GeneralTreeCtrl.TreeCtrl.__init__(self, wxTreeCtrl)

How do I override the OnCompareItems if my TreeCtrl classes are like above?
TreeCtrl is inherited from GeneralTreeCtrl, but GeneralTreeCtrl doesn't inherit
from wxTreeCtrl.

My classes are in such a away because I use GUI designer. After the layout
stage, then only I wrap the wxTreeCtrl using my class, this allow me not to
change the wxTreeCtrl attribues set in the designer.

According to my experience with wxPython, you shouldn't be able to add
either of the above as visual elements in any wxPython application. Why?
Simply beacuse neither TreeCtrl nor GeneralTreeCtrl subclass from
wx.Control.

If you want to use the output from some designer application, there are
other, better ways. Say, for example, that the output of your GUI
designer was a class called TreeCtrl in a file called CustomTreeCtrl.py

In the file that you write, you can do the following...

import CustomTreeCtrl

class TreeCtrl(CustomTreeCtrl.TreeCtrl):
    def __init__(self, ...):
        CustomTreeCtrl.TreeCtrl.__init__(self, ...)
        ...
    ...

You can then override methods as necessary, add functionality as
necessary, etc. If you ever need to call a method of the
CustomTreeCtrl.TreeCtrl class, you can do precisely as you did with the
__init__ call...

    CustomTreeCtrl.TreeCtrl.BaseMethod(self, ...)

- Josiah

I am using the Boa Constructor 0.4.4 to design the GUI. I am not adding my
custom control as visual elements in the designer. I'm wrapping the added visual
element (TreeCtrl) with my custom class and extend from it. I can still use the
control as if it inherits from wxTreeCtrl. Please see my source code here.

File: App1.py

···

-------------
#!/usr/bin/env python
#Boa:App:BoaApp

import wx

import Frame1

modules ={'Frame1': [1, 'Main frame of Application', u'Frame1.py']}

class BoaApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        self.main = Frame1.create(None)
        self.main.Show()
        self.SetTopWindow(self.main)
        return True

def main():
    application = BoaApp(0)
    application.MainLoop()

if __name__ == '__main__':
    main()

File: Frame1.py
#Boa:Frame:Frame1

import wx

def create(parent):
    return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1BTNSORT, wxID_FRAME1PANEL1, wxID_FRAME1TREECTRL1,
] = [wx.NewId() for _init_ctrls in range(4)]

class TreeCtrl(object):
    def __init__(self, wxTreeCtrl):
        self.tree = wxTreeCtrl
        self._imageList = wx.ImageList(height=16, width=16)
        self.tree.SetImageList(self._imageList)

        #setup the images
        isz = (16,16)
        self._imgFolder = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_FOLDER, wx.ART_OTHER, isz))
        self._imgFolderOpen = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_FILE_OPEN, wx.ART_OTHER, isz))
        self._imgFile = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))

    def __getattr__(self, name):
        return self.tree.__getattribute__(name)

    def addRoot(self, label, data):
        root = self.tree.AddRoot(label)
        self.tree.SetPyData(root, data)
        self.tree.SetItemImage(root, self._imgFolder, wx.TreeItemIcon_Normal)
        self.tree.SetItemImage(root, self._imgFolderOpen,
               wx.TreeItemIcon_Expanded)
        return root

    def appendItem(self, parent, label, data):
        child = self.tree.AppendItem(parent, label)
        self.tree.SetPyData(child, data)
        self.tree.SetItemImage(child, self._imgFolder, wx.TreeItemIcon_Normal)
        self.tree.SetItemImage(child, self._imgFolderOpen,
              wx.TreeItemIcon_Expanded)
        return child

    def appendLeaf(self, parent, label, data):
        child = self.tree.AppendItem(parent, label)
        self.tree.SetPyData(child, data)
        self.tree.SetItemImage(child, self._imgFile, wx.TreeItemIcon_Normal)
        return child

class Frame1(wx.Frame):
    def _init_coll_boxSizer1_Items(self, parent):
        # generated method, don't edit

        parent.AddWindow(self.treeCtrl1, 0, border=0, flag=0)
        parent.AddWindow(self.panel1, 0, border=0, flag=0)

    def _init_sizers(self):
        # generated method, don't edit
        self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL)

        self._init_coll_boxSizer1_Items(self.boxSizer1)

    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
              pos=wx.Point(201, 182), size=wx.Size(376, 339),
              style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
        self.SetClientSize(wx.Size(368, 305))

        self.treeCtrl1 = wx.TreeCtrl(id=wxID_FRAME1TREECTRL1, name='treeCtrl1',
              parent=self, pos=wx.Point(0, 0), size=wx.Size(368, 272),
              style=wx.TR_HAS_BUTTONS)

        self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
              pos=wx.Point(0, 270), size=wx.Size(368, 34),
              style=wx.TAB_TRAVERSAL)

        self.btnSort = wx.Button(id=wxID_FRAME1BTNSORT, label=u'Sort',
              name=u'btnSort', parent=self.panel1, pos=wx.Point(8, 8),
              size=wx.Size(75, 23), style=0)
        self.btnSort.Bind(wx.EVT_BUTTON, self.OnButtonSort,
              id=wxID_FRAME1BTNSORT)

        self._init_sizers()

    def __init__(self, parent):
        self._init_ctrls(parent)

        self.treeCtrl1 = TreeCtrl(self.treeCtrl1)
        self.populateTree()

    def populateTree(self):
        self.root = self.treeCtrl1.addRoot("Root", None)
        self.folderA = self.treeCtrl1.appendItem(self.root, "Folder A", None)
        self.folderB = self.treeCtrl1.appendItem(self.root, "Folder B", None)
        z = self.treeCtrl1.appendLeaf(self.folderA, "item Z", None)
        a = self.treeCtrl1.appendLeaf(self.folderA, "item A", None)
        b = self.treeCtrl1.appendLeaf(self.folderA, "item B", None)

    def OnButtonSort(self, event):
        #this will invoke the default OnCompareItems, which I want to override
        self.treeCtrl1.SortChildren(self.folderA)

Found the solution. Create a method sortChildren in class TreeCtrl.

class TreeCtrl(object)
    ......
    def sortChildren(self, node):
        setattr(self.tree.__class__, "OnCompareItems", OnCompareItems)
        self.tree.SortChildren(node)

Create OnCompareItems method outside of the TreeCtrl class. Somehow if I try to
create the OnCompareItems in class TreeCtrl, it failed to be invoked!

def OnCompareItems(self, item1, item2):
    #argument self is needed even though now it's not a class method
    pass
    # return -1 or 0 or 1
   
I think I could move the setattr line into method __init__ as well, but I didnt
bother to try it out. This current solution suits me better.

That will override the compare items method on all wx.TreeCtrl instances.

As I stated in my previous email, you should subclass wx.TreeCtrl and
override the OnCompareItems method directly. Here is a variant of your
code that subclasses wx.TreeCtrl, and that sorts items based on the item
label.

- Josiah

File: App1.py

···

Galvin <mygalvin@yahoo.com> wrote:

Found the solution. Create a method sortChildren in class TreeCtrl.

class TreeCtrl(object)
    ......
    def sortChildren(self, node):
        setattr(self.tree.__class__, "OnCompareItems", OnCompareItems)
        self.tree.SortChildren(node)

-------------
#!/usr/bin/env python
#Boa:App:BoaApp

import wx

import Frame1

modules ={'Frame1': [1, 'Main frame of Application', u'Frame1.py']}

class BoaApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        self.main = Frame1.create(None)
        self.main.Show()
        self.SetTopWindow(self.main)
        return True

def main():
    application = BoaApp(0)
    application.MainLoop()

if __name__ == '__main__':
    main()

File: Frame1.py
#Boa:Frame:Frame1

import wx

def create(parent):
    return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1BTNSORT, wxID_FRAME1PANEL1, wxID_FRAME1TREECTRL1,
] = [wx.NewId() for _init_ctrls in range(4)]

class TreeCtrl(wx.TreeCtrl):
    def __init__(self, *args, **kwargs):
        wx.TreeCtrl.__init__(self, *args, **kwargs)
        
        self._imageList = wx.ImageList(height=16, width=16)
        self.SetImageList(self._imageList)

        #setup the images
        isz = (16,16)
        self._imgFolder = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_FOLDER, wx.ART_OTHER, isz))
        self._imgFolderOpen = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_FILE_OPEN, wx.ART_OTHER, isz))
        self._imgFile = self._imageList.Add(wx.ArtProvider_GetBitmap(
                        wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))

    def addRoot(self, label, data):
        root = self.AddRoot(label)
        self.SetPyData(root, data)
        self.SetItemImage(root, self._imgFolder, wx.TreeItemIcon_Normal)
        self.SetItemImage(root, self._imgFolderOpen,
               wx.TreeItemIcon_Expanded)
        return root

    def appendItem(self, parent, label, data):
        child = self.AppendItem(parent, label)
        self.SetPyData(child, data)
        self.SetItemImage(child, self._imgFolder, wx.TreeItemIcon_Normal)
        self.SetItemImage(child, self._imgFolderOpen,
              wx.TreeItemIcon_Expanded)
        return child

    def appendLeaf(self, parent, label, data):
        child = self.AppendItem(parent, label)
        self.SetPyData(child, data)
        self.SetItemImage(child, self._imgFile, wx.TreeItemIcon_Normal)
        return child
    
    def OnCompareItems(self, item1, item2):
        #implement your comparison function here
        return cmp(self.GetItemText(item1), self.GetItemText(item2))

class Frame1(wx.Frame):
    def _init_coll_boxSizer1_Items(self, parent):
        # generated method, don't edit

        parent.AddWindow(self.treeCtrl1, 0, border=0, flag=0)
        parent.AddWindow(self.panel1, 0, border=0, flag=0)

    def _init_sizers(self):
        # generated method, don't edit
        self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL)

        self._init_coll_boxSizer1_Items(self.boxSizer1)

    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
              pos=wx.Point(201, 182), size=wx.Size(376, 339),
              style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
        self.SetClientSize(wx.Size(368, 305))

        self.treeCtrl1 = TreeCtrl(id=wxID_FRAME1TREECTRL1, name='treeCtrl1',
              parent=self, pos=wx.Point(0, 0), size=wx.Size(368, 272),
              style=wx.TR_HAS_BUTTONS)

        self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
              pos=wx.Point(0, 270), size=wx.Size(368, 34),
              style=wx.TAB_TRAVERSAL)

        self.btnSort = wx.Button(id=wxID_FRAME1BTNSORT, label=u'Sort',
              name=u'btnSort', parent=self.panel1, pos=wx.Point(8, 8),
              size=wx.Size(75, 23), style=0)
        self.btnSort.Bind(wx.EVT_BUTTON, self.OnButtonSort,
              id=wxID_FRAME1BTNSORT)

        self._init_sizers()

    def __init__(self, parent):
        self._init_ctrls(parent)

        self.populateTree()

    def populateTree(self):
        self.root = self.treeCtrl1.addRoot("Root", None)
        self.folderA = self.treeCtrl1.appendItem(self.root, "Folder A", None)
        self.folderB = self.treeCtrl1.appendItem(self.root, "Folder B", None)
        z = self.treeCtrl1.appendLeaf(self.folderA, "item Z", None)
        a = self.treeCtrl1.appendLeaf(self.folderA, "item A", None)
        b = self.treeCtrl1.appendLeaf(self.folderA, "item B", None)

    def OnButtonSort(self, event):
        #this will invoke the default OnCompareItems, which I want to override
        self.treeCtrl1.SortChildren(self.folderA)

Yes you're right, my method will override the compare items method on all
wx.TreeCtrl instances. That will be a problem if I have more than one
wx.TreeCtrl and each need to have different way of sorting. But this could be
avoided when invoking sortChildren and pass in OnCompareItems as an addtional
argument.

I've tried your method but the BOA Constructor designer failed to open the file!

Including context will make your replies easier to understand.

As for Boa not knowing how to handle the result I sent you, that is
unfortunate. Have you tried moving the definition of 'TreeCtrl' into a
different module that is imported by your Frame1.py? That may or may
not help, but I'm surprised that Boa can't handle such minimal
customization of classes or changing of source.

Alternatively, you could try one of the other GUI designers out there;
XRCed, wxGlade, or even Dabo Designer. The nice thing about XRCed is
that you are just working with XML and you can refer to custom clases by
name. If you decide to dig into Dabo, they have made some useful
abstractions over base wxPython, which you may find removes your desire
or need to use a GUI designer in the first place.

- Josiah

···

Galvin <mygalvin@yahoo.com> wrote:

Yes you're right, my method will override the compare items method on all
wx.TreeCtrl instances. That will be a problem if I have more than one
wx.TreeCtrl and each need to have different way of sorting. But this could be
avoided when invoking sortChildren and pass in OnCompareItems as an addtional
argument.

I've tried your method but the BOA Constructor designer failed to open the file!