Outline Control? TreeListCtr

I need a control that will allow reordering of ordered data in a
database table. The “Manage Bookmarks” option in Firefox has some of
the same utility, but it does not allow arbitrary orders of bookmarks,
only folders. I am not sure whether such a control exists or once
existed in Visual Basic, but I think that at least one version had such
a control. The idea is simple and the basic requirements are listed
below.

My questions are (1) does this functionality currently exist in
wxPython; (2) if not, could I modify the TreeListCtrl and accomplish
this functionality? ; Would I be better off just writing an
application from scratch using more basic controls

Desired functionality:
Outline
Control

  1. Would look similar to a TreeListCtrl

  2. Would allow demotion of a selected node to a child of its
    immediately preceding sibling.

  3. Would allow promotion of a selected node to a sibling of its
    parent immediately following the parent.

  4. Would allow reordering of siblings by dragging and dropping a
    selected sibling up or down the sibling column.

  5. Would allow reordering of siblings by a right-click menu item
    for a selected node. (move up, move down or arrows)

  6. Would allow reordering of siblings, demotion and promotion by
    arrow keys in a form operating on a selected node in the control.

  7. Could write structure and data to a database.

  8. Could write structure and data to a native python container.

  9. Could read structure and data from a database into the
    control.

  10. Could read structure and data from a native python container
    into the control.

  11. Could show and hide columns.

I think I know what you’re looking for in items 1-6. The rest of the items (except for maybe 11) are straightforward - just iterate through the database/python container and add items along the way.

There is no native control that has this exact functionality in wxPython, but the methods provided in the TreeListCtrl are sufficient to imitate it. You will need to implement methods that can do the following things on a TreeListCtrl (this is like a computer science exercise):

  • Shift nodes up - This means that a node goes up across its siblings, and (if you want) goes to become a sibling of its parent after traversing across all of the siblings.
  • Shift nodes down - Same as above, except downwards.
  • Shift nodes left - If a node has a parent, move it to the left and make it the sibling of the parent. This would mean that the sibling traverses across all of the siblings below it and becomes the sibling of its parent.
  • Shift nodes right - Make the node the child of the sibling directly above it.
    In implementing these methods, you will find that the following TreeCtrl/TreeListCtrl methods are very helpful:
  • GetItemParent(item)
  • GetLastChild(item)
  • GetNextSibling(item), GetPrevSibling(item)
  • GetNextVisible(item), GetPrevVisible(item) (these return the next and previous visible items. Basically, when you expand the whole tree, it returns the item that is right above (previous) or right below (next) the parameter item)
  • GetSelection()
  • InsertItem, AppendItem, PrependItem, InsertItemBefore
  • bool ItemHasChildren()
  • SelectItem(item) (useful for deciding where to start adding items as children)
    If you want to associate data (PyObjects) with the nodes, you can use SetPyData(item, data) and GetPyData(item), which deal with PyObjects as the data.

If you’re looking for a GetFirstChild() method, just use GetNextVisible(). I implemented some minor TreeCtrl (therefore TreeListCtrl as well) methods in my application, Notalon. I have attached the file as my treectrl.py
, the smaller one. I have also attached the treectrl.py of Frank Niessink’s Task Coach, which gives a better implementation of TreeListCtrl features. As for dragging and dropping, there is an example on the wiki, as well as in the Task Coach code.

In conclusion, what you want to do is possible, but you’ll have to implement it yourself.

-Saketh

treectrl.py (3.55 KB)

treectrl.py (18.8 KB)

···

On 8/11/06, Jim Fuqua Jim@jim-fuqua.com wrote:

I need a control that will allow reordering of ordered data in a
database table. The “Manage Bookmarks” option in Firefox has some of
the same utility, but it does not allow arbitrary orders of bookmarks,
only folders. I am not sure whether such a control exists or once
existed in Visual Basic, but I think that at least one version had such
a control. The idea is simple and the basic requirements are listed
below.

My questions are (1) does this functionality currently exist in
wxPython; (2) if not, could I modify the TreeListCtrl and accomplish
this functionality? ; Would I be better off just writing an
application from scratch using more basic controls

Desired functionality:
Outline
Control

  1. Would look similar to a TreeListCtrl
  1. Would allow demotion of a selected node to a child of its
    immediately preceding sibling.
  1. Would allow promotion of a selected node to a sibling of its
    parent immediately following the parent.
  1. Would allow reordering of siblings by dragging and dropping a
    selected sibling up or down the sibling column.
  1. Would allow reordering of siblings by a right-click menu item
    for a selected node. (move up, move down or arrows)
  1. Would allow reordering of siblings, demotion and promotion by
    arrow keys in a form operating on a selected node in the control.
  1. Could write structure and data to a database.
  1. Could write structure and data to a native python container.
  1. Could read structure and data from a database into the
    control.
  1. Could read structure and data from a native python container
    into the control.
  1. Could show and hide columns.

Would XML be a better approach? I am not very familiar with XML, but
have found scripts on the Internet which will load and unload an XML
file to and from a TreeCtrl.

I like to use XML to load and write data from Tree(List)Ctrls, because you can easily iterate through the data and load it. I use XML for my own convenience in applications, but your users may demand a different standard.

Am I likely to run up against size limits? It would be a shame to
spend a lot of time making this work on a small scale and find that it
would not work on a large scale either because of fixed size limits or
unacceptable time delays in reading and writing data or saving changes
to the structure.

I have never tested the difference in overhead between accessing large MySQL databases and large XML files, but I can safely say that Python + ElementTree work for files that are around 9000 lines (I have never needed to use anything larger).

If you are worried about speed, I hear that PyRXP backs up its claim of being the “fastest validating XML parser available for Python.” I have never used it, so I can’t advise you on that.

Here are two methods that I use in Notalon (which is available on SourceForge if you want to see the whole code) to write and read XML into my TreeCtrl. I only have a root node and children nodes of the root node, but you can extend the idea with recursion.

def WriteState(self, filename):
“”"
Iterate through the nodes, putting the XML’ed text into a specified file.
“”"

    current = self.tree.GetRootItem()
    root = Element("tree")
    root.set("title", self.tree.GetItemText(self.tree.GetRootItem()))
    roottext = unicode(self.tree.GetPyData

(self.tree.GetRootItem()))
if roottext == ‘None’:
roottext = “” # We don’t want it to literally write ‘None’
treedata = SubElement(root, “treedata”)
treedata.text
= roottext

    # Loop through children now
    while current.IsOk():
        new = self.tree.GetNextVisible(current)
        current = new
        if not current.IsOk(): break

        # Start translating the data
        new = SubElement(root, "node")
        new.set("title", unicode(self.tree.GetItemText(current)).encode('utf8'))

        data = SubElement(new, "data")

        datatext = unicode(self.tree.GetPyData(current)).encode('utf8')
        if datatext == 'None':
                    datatext = "" # Again, we don't want it to literally write 'None'

        data.text = datatext

    tree = ElementTree(root)
    tree.write(unicode(filename))

And the reading one…
def OpenFile(self, filename):

    """
    Opens a command-line argument file by parsing XML and changes components accordingly.
    """
    # Clear everything before starting
    self.tree.DeleteAllItems

()

    self.filename = filename
    opened = ElementTree(file = filename)
    iter = opened.getiterator()

    for element in iter:
        if element.tag == 'tree':
            if element.keys():
                for name, value in element.items():
                    # If the root's not there, we add a new one.
                    if not self.tree.GetRootItem().IsOk():
                        self.tree.AddRoot(value)
                    # If the root's there, we change the title.
                    else:
                        self.tree.SetItemText(self.tree.GetRootItem(), value)
            if element.getchildren(): # If root has data
                for child in element:
                    if child.tag == 'treedata':
                        self.tree.SetPyData(self.tree.GetRootItem(), child.text

)

        if element.tag == 'node':
            if element.keys():  # If the node has attributes
                for name, value in element.items():
                        newnode = self.tree.AppendItem(self.tree.GetRootItem(), value)
            if element.getchildren():
                for child in element:
                    # The children should just be one data child
                    self.tree.SetPyData(newnode, child.text)

    self.hist.AddFileToHistory(filename)
    if not self.fileclearrecent.IsEnabled():
        self.fileclearrecent.Enable(True)

    self.tree.Expand

(self.tree.GetRootItem())
self.control.SetValue(unicode(self.tree.GetPyData(self.tree.GetRootItem())))
self.SetModified(False)

This is surprising because I
am still trying to learn both Python and wxPython.

All Python/wxPython programmers learn something new about their programming language and GUI library of choice every day. I learned programming 3 months ago so I could write Notalon, and I am very pleased with the way that Python lets you learn while you are productive.

Jim Fuqua wrote:

Thanks to suggestions by Saketh, I am making progress on an outline control inheriting from the TreeListCtrl. This is surprising because I am still trying to learn both Python and wxPython.

Soon I will need to read and write the TreeListCtrl structure and data to and from disk storage. I expect the data to eventually occupy more than 10,000 lines.

Saving the parent child relationships and data could be done a number of different ways. My first reaction to the problem is to try to put it all in a MySQL database because I have some familiarity with MySQL. Perhaps there is a better way. That is my first question.

If you have saved and restored TreeListCtrl or TreeCtrl data to and from a database, what table structure did you find efficient to represent the parent child structure?

Would XML be a better approach? I am not very familiar with XML, but have found scripts on the Internet which will load and unload an XML file to and from a TreeCtrl.

Am I likely to run up against size limits? It would be a shame to spend a lot of time making this work on a small scale and find that it would not work on a large scale either because of fixed size limits or unacceptable time delays in reading and writing data or saving changes to the structure.

10K lines is basically trivial. On modern computers, you could store it in encrypted Pig Latin and load it before your user gets her finger off the mouse button.

So don't use performance at this scale as the key to deciding on your storage system.

Storing in a MySQL (or local SQL, such as SQLite) database is useful if you think you're going to have a lot of associated data, or need to access it with a variety of different orders, or if you're planning to move this app to a multiuser environment like the web and might move the database to another machine. But it carries with it the responsibility to install and maintain the database system.

An XML file gives you the potential for compatibility across applications. If you think you need to process it with other code besides your app, you might prefer XML.

If I feel that only my app is ever going to process this data, one trick I use is to store the data as a Python data structure.

You can write a block of data out with:

import pprint
pprint.pprint(data, open("datafile.txt", "w"))

and read it back in with:

data = eval(open("datafile.txt", "r").read()) # depending on platform, you may have to filter newlines here

No parsers to write! Fast! Easy! Cheap!

Careful, though, if you expect people to share data. It does open up rather significant security holes. :slight_smile:

    Kent

Kent Quirk wrote:

If I feel that only my app is ever going to process this data, one trick I use is to store the data as a Python data structure.

In that case, why not use pickle?

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov