Demo doesn't start; Can TreeCtrl do this?

be cautious, the ideas go straight into the cloud :cowboy_hat_face:

Nice :slight_smile:

Is there a way to make the lines 1) bolder so they’re actually visible when changing the background color to light yellow, 2) remove the checkbox before each item, or even 3) the “+” sign?

And also 1) open/close a sub-tree through the Enter key, 2) edit an existing item by just typing, 3) add a new item by hitting Enter, and 3) move items around the tree with ALT+arrow (left, right, up, down)?

For the curious, EccoPro can be downloaded here.

#TODO lines between items not visible when changing background color
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)
#TODO blue line 00FFFF #TR_ROW_LINES
custom_tree.SetBackgroundColour('#FFFFE8') #(255,0,0)

image

Hm, did twitter not change to X :astonished:

  1. The colour of the lines is automatically calculated from the background colour. See line 7117 in customtreectrl.py:
            if self.HasAGWFlag(TR_ROW_LINES):

                # if the background colour is white, choose a
                # contrasting colour for the lines
                medium_grey = wx.Pen(wx.Colour(200, 200, 200))
                dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0])
                dc.DrawLine(0, y_top, 10000, y_top)
                dc.DrawLine(0, y, 10000, y)

If you tweak the background colour a bit (e.g. '#f0f0d0') then the lines become a bit more noticeable.

  1. To remove the checkboxes you simply omit the ct_type=1 from the AddItem() calls. You then do not need to pass CT.TR_AUTO_CHECK_CHILD or CT.TR_AUTO_CHECK_PARENT to agwStyle.

  2. The ‘+’ and ‘-’ signs (or black triangles on my linux box) are referred to as ‘buttons’ in the docs. The bitmask wx.TR_DEFAULT_STYLE on my linux box is set to 5 (might be different on other platforms). That means it includes wx.TR_HAS_BUTTONS and wx.TR_NO_LINES. You can’t bitwise-or it with wx.TR_NO_BUTTONS because that has a value of zero.

Therefore I tried:

    agwStyle=CT.TR_NO_BUTTONS|CT.TR_NO_LINES|wx.TR_MULTIPLE|CT.TR_ROW_LINES

which seemed to work. However you now can’t just expand/collapse the item’s children by clicking on its button. Instead, you can either use the right/left arrow keys or double-click the item.

Here is the modified example:

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=CT.TR_NO_BUTTONS|CT.TR_NO_LINES|wx.TR_MULTIPLE|CT.TR_ROW_LINES)
        custom_tree.SetBackgroundColour('#f0f0d0')

        # 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)))
                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):
                    item = custom_tree.AppendItem(last,  "item %d-%s-%d" % (x, chr(ord("a")+y), z))
                    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()

A possible alternative would be to use the HyperTreeList class. I noticed in the example in the wxPython Demo there was a TR_FILL_WHOLE_COLUMN_BACKGROUND which had a better contrast between the lines and the background. However, I can’t find any reference to that style in the docs?

Here is another idea.
Create a subclass of CustomTreeCtrl and add a copy of the original PaintLevel() method. Then modify that method so it chooses a better colour for the lines.

In the example below I just set the colour to grey: wx.Colour(200, 200, 200).

custom_tree_control_demo_7.py (11.2 KB)

3 Likes

As the docs for the CustomTreeCtrl say that wx.TR_DEFAULT_STYLE should always be used, it’s probably better to pass:

wx.TR_DEFAULT_STYLE^CT.TR_HAS_BUTTONS|wx.TR_MULTIPLE|CT.TR_ROW_LINES

to agwStyle, rather than:

CT.TR_NO_BUTTONS|CT.TR_NO_LINES|wx.TR_MULTIPLE|CT.TR_ROW_LINES

EDIT: the forum software won’t let me post more than 2 consecutive replies, so I have added the following to this post…

I’ve been doing some experiments to see if I can find a simple alternative to the current algorithm used by the CustomTreeCtrl to select a contrasting colour for the lines.

The problem with the current algorithm is that it sets the line colour as medium grey only if the background colour is pure white, otherwise it sets the line colour to white. This makes the lines impossible to see for any bright background colours that are not pure white.

My first attempt was to sum the background colour’s RGB values and set the line colour to medium grey if the sum was above a certain threshold, otherwise it set the line colour to white. This worked better than the current algorithm, but there were still some background colours where it didn’t work very well.

My second attempt was to use a version of a function that converts the RGB value to HSV, calculates the contrasting colour and then converts it back to RGB. [I can’t remember where I found the original of this function online].

Below is an app which can be used to test these 2 functions. When you move the RGB sliders, the background colour of the ContrastPanel is changed and a Refresh() is triggered. This cause the panel to redraw itself using the new colours.

You need to comment/uncomment the static methods used in OnPaint() to test each one.

contrast_colour_2.py (3.6 KB)

Screenshot at 2024-02-12 13-40-00

2 Likes

Thanks much.

At this point, the only thing left is being able to open/close a sub-tree with the Enter key, and move items (up, down, left, right) in the tree with ALT+arrow keys

I’ll read up on wx.lib.agw.customtreectrl