Demo doesn't start; Can TreeCtrl do this?

Hello,

I wanted to run the demo to see if the TreeCtrl widget is the right tool for a small project I have… but it fails running:

c:\wxPython-demo\demo>python demo.py
Traceback (most recent call last):
  File "c:\wxPython-demo\demo\demo.py", line 3, in <module>
    import Main
  File "c:\wxPython-demo\demo\Main.py", line 59, in <module>
    from distutils.version import LooseVersion
ModuleNotFoundError: No module named 'distutils'

c:\wxPython-demo\demo>pip install distutils
ERROR: Could not find a version that satisfies the requirement distutils (from versions: none)
ERROR: No matching distribution found for distutils

c:\wxPython-demo\demo>python --version
Python 3.12.0

c:\wxPython-demo\demo>python -c "import wx;print(wx.__version__)"
4.2.1

The project requires a “single-plane outliner” à la EccoPro. Is the TreeCtrl widget the right tool for this?

Thank you.

image

well, I’ve got a some Ecco shoes and the company is still going well (quality, I suppose, price accordingly), but EccoPro seems to have gone to the walls :flushed: (for the demo see)

My first program was if true run, not hello world.

Not everyone is your friend here. Word of advice.

thanks (but just think the flying circus without prelude :disappointed:)

No harm done here. I was looking at how you addressed a OP, which also could be a new user(noob). I hope they are not offended and go elsewhere, because they spent their time and trouble to not only create an account, but to log in and post only to be greeted by a smart assed post. Sorry for the longest run on sentence ever written by a novice pressman.

>>> import distutils
<stdin>:1: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives

Try running the program TreeCtrl.py directly.

1 Like

As Main.py imports distutils, which is deprecated in python 3.12 a possible solution, as recommended in the PEP 632
import packaging instead and use that.
See: Version Handling - Packaging

>>> from distutils.version import LooseVersion
>>> import wx
>>> import version
>>> version.VERSION_STRING
'4.2.1'
>>> wx.VERSION_STRING
'4.2.1'
>>> if LooseVersion(version.VERSION_STRING) != LooseVersion(wx.VERSION_STRING):
...  print("Wrong version")
... else:
...  print("Correct version")
... 
Correct version
>>> 
>>> from packaging.version import Version
>>> Version(wx.VERSION_STRING)
<Version('4.2.1')>
>>> parse(wx.VERSION_STRING)
<Version('4.2.1')>
>>> str(Version(wx.VERSION_STRING))
'4.2.1'
>>> if str(version.VERSION_STRING) != str(Version(wx.VERSION_STRING)):
...  print("Wrong version")
... else:
...  print("Correct version")
... 
Correct version
>>> 


For your first question, I can run wxdemo 4.2.1 / Python 3.12 on Windows:

C:\usr\home>wxdemo
sys.version_info(major=3, minor=12, micro=1, releaselevel='final', serial=0) 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] ['C:\\Python312\\Scripts\\wxdemo.exe']
Launch Demo for wxPython V4.2.1
Looking for wxPython-demo-4.2.1 at C:\Users\omoto\AppData\Local\wxPython
Launching C:\Users\omoto\AppData\Local\wxPython\wxPython-demo-4.2.1\demo\demo.py
Demo starting as PID 15460 - may take a few seconds!
Closing Launcher App!

Thank you.

There’s no wxdemo*.* in the directories:

c:\wxPython-demo>dir /b
demo
README.txt
samples

c:\wxPython-demo>dir wxdemo*.* /s
 Volume in drive C is Data
 Volume Serial Number is D850-C463
File Not Found

If you installed wxPython from PyPI, you will find “wxdemo.exe” in the “Scripts” directory where python is installed (for example, “C:\Python312\Scripts\wxdemo.exe”).
I forgot to mention that. I usually add the directory to the environment variable PATH.

1 Like

That figures. I unzipped the demo. Thx.

As for wxPython proper, I think I ran “pip install wxpython”, but it’s been a while so not sure.

:man_facepalming: Doh!

I never remember quite how different Windows can be to Unix.
I assumed that the wxpython demo would be the same and have the same structure.
Ah, well, never mind. The use of packaging.version probably still holds true, at least for Linux.

I guess if I see c:\ in a question, I should take that as a warning, that I should probably refrain from commenting and leave it to others. :grin:

Regards,
a chastened Rolf

Can the TreeCtrl widget 1) have a line between each item, and 2) a bullet in front of each instead of a “+” sign?

This doesnt cut it:

self.tree = wx.TreeCtrl(panel1, 1,wx.DefaultPosition,wx.DefaultSize,wx.TR_HIDE_ROOT |wx.TR_HAS_BUTTONS | wx.TR_ROW_LINES)

#blue line 00FFFF

I notice there’s a “wx.lib.agw.customtreectrl”: Should I use that alternative widget instead?

image

The CustomTreeCtrl allows you to add horizontal separators between items. Below is a slightly modified version of the example in the documentation to demonstrate:

import wx
import wx.lib.agw.customtreectrl as CT

class MyFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent, -1, "CustomTreeCtrl Demo", size=(400, 600))

        # Create a CustomTreeCtrl instance
        custom_tree = CT.CustomTreeCtrl(self, agwStyle=wx.TR_DEFAULT_STYLE|wx.TR_MULTIPLE|CT.TR_AUTO_CHECK_CHILD|CT.TR_AUTO_CHECK_PARENT)

        # Add a root node to it
        root = custom_tree.AddRoot("The Root Item")

        # Create an image list to add icons next to an item
        il = wx.ImageList(16, 16)
        fldridx     = il.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER,      wx.ART_OTHER, (16, 16)))
        fldropenidx = il.Add(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, (16, 16)))
        fileidx     = il.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16, 16)))

        custom_tree.SetImageList(il)

        custom_tree.SetItemImage(root, fldridx, wx.TreeItemIcon_Normal)
        custom_tree.SetItemImage(root, fldropenidx, wx.TreeItemIcon_Expanded)

        for x in range(15):
            child = custom_tree.AppendItem(root, "Item %d" % x)
            custom_tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
            custom_tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)

            for y in range(5):
                last = custom_tree.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=1)
                custom_tree.SetItem3State(last, True)
                custom_tree.SetItemImage(last, fldridx, wx.TreeItemIcon_Normal)
                custom_tree.SetItemImage(last, fldropenidx, wx.TreeItemIcon_Expanded)

                for z in range(5):
                    # Set ct_type=1 to add a checkbox to the item
                    item = custom_tree.AppendItem(last,  "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=1)
                    custom_tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Normal)

            custom_tree.AppendSeparator(root)

        custom_tree.Expand(root)


if __name__ == '__main__':
    app = wx.App(0)
    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()

2 Likes

You can use whatever images you like.
You simply have to load the wx.ImageList with your images.
In the case of RichardT’s example:

        # Create an image list to add icons next to an item
        b1 = wx.Bitmap('b1.png')
        b2 = wx.Bitmap('b2.png')
        b3 = wx.Bitmap('b3.png')
        il = wx.ImageList(16, 16)
        fldridx     = il.Add(b1)
        fldropenidx = il.Add(b2)
        fileidx     = il.Add(b3)
        custom_tree.SetImageList(il)

Gives you:

Using the following images:
b1 b2 b3

Note: they must all be the same size, as declared to the ImageList, in this case (16, 16)
Tested on Linux

2 Likes

Thanks. Ideally, I’m looking for a way to have lines between each item, so that the treelist looks like the classic yellow lined paper notebook.

I’ll look deeper, see if someone came up with a solution.

image

Or you can generate the bullet images:

        def _bullet(colour, w=16, r=8):
            bmp = wx.Bitmap((w, w))
            dc = wx.MemoryDC(bmp)
            dc.SetBackground(wx.Brush('black'))
            dc.SetBrush(wx.Brush(colour, style=wx.BRUSHSTYLE_SOLID))
            dc.DrawCircle(w//2, w//2, r)
            del dc
            bmp.SetMaskColour('black')
            return bmp
        b1 = _bullet('blue')
        b2 = _bullet('green')
        b3 = _bullet('red')
3 Likes

Apologies for suggesting using AppendSeparator.

If you include CT.TR_ROW_LINES in the 'agwStyle` parameter, you should get lines between all the rows:

1 Like

Adjusting for the amendments kindly suggested by @komoto48g and @RichardT we end up with:

import wx
import wx.lib.agw.customtreectrl as CT

class MyFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent, -1, "CustomTreeCtrl Demo", size=(400, 600))

        # Create a CustomTreeCtrl instance
        custom_tree = CT.CustomTreeCtrl(self, agwStyle=wx.TR_DEFAULT_STYLE|wx.TR_MULTIPLE|CT.TR_AUTO_CHECK_CHILD|CT.TR_AUTO_CHECK_PARENT|CT.TR_ROW_LINES)

        # Add a root node to it
        root = custom_tree.AddRoot("The Root Item")

        # Create an image list to add icons next to an item
        def _bullet(colour, w=16, r=4):
            bmp = wx.Bitmap((w, w))
            dc = wx.MemoryDC(bmp)
            dc.SetBackground(wx.Brush('black'))
            dc.SetBrush(wx.Brush(colour, style=wx.BRUSHSTYLE_SOLID))
            dc.DrawCircle(w//2, w//2, r)
            del dc
            bmp.SetMaskColour('black')
            return bmp

        b1 = _bullet('blue')
        b2 = _bullet('green')
        b3 = _bullet('red')
        il = wx.ImageList(16, 16)
        fldridx     = il.Add(b1)
        fldropenidx = il.Add(b2)
        fileidx     = il.Add(b3)
        custom_tree.SetImageList(il)

        custom_tree.SetItemImage(root, fldridx, wx.TreeItemIcon_Normal)
        custom_tree.SetItemImage(root, fldropenidx, wx.TreeItemIcon_Expanded)

        for x in range(15):
            child = custom_tree.AppendItem(root, "Item %d" % x)
            custom_tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
            custom_tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)

            for y in range(5):
                last = custom_tree.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=1)
                custom_tree.SetItem3State(last, True)
                custom_tree.SetItemImage(last, fldridx, wx.TreeItemIcon_Normal)
                custom_tree.SetItemImage(last, fldropenidx, wx.TreeItemIcon_Expanded)

                for z in range(5):
                    # Set ct_type=1 to add a checkbox to the item
                    item = custom_tree.AppendItem(last,  "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=1)
                    custom_tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Normal)

        custom_tree.Expand(root)


if __name__ == '__main__':
    app = wx.App(0)
    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()
3 Likes