wxTreeListCtrl still incorrect

The wxTreeListCtrl widget still does not return the correct
column in the HitTest() method. I applied the this patch

<http://lists.wxwindows.org/archive/wxPython-dev/msg00151.html>

to a wxPython2.4.1.2/Python2.3.1 build and included other
suggestions like these

<http://aspn.activestate.com/ASPN/Mail/Message/wxPython-users/1793125>
<http://aspn.activestate.com/ASPN/Mail/Message/wxPython-users/1826241>

in my code, but the result remains incorrect.

Attached is an example which demonstates the issues, basically a
combination of the wxPython demos wxTreeListCtrl and wxTreeCtrl.
It shows the problem when right-clicking on any item. Run it in
(a copy of) the wxPython demo directory.

/Jean Brouwers

PS) There are other issues with wxTreeListCtrl. For example,
right-clicking on an item occasionally returns an 'invalid tree
item'. And using the GetColumnWidth() method causes a segmentation
fault.

wxTreeListCtrl2.py (7.58 KB)

    def bias(self, root):
        self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

    def HitTest(self, point):
        p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
        i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
        i = wxTreeItemIdPtr(i)
        i.thisown = 1
        return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

Roger,

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.
But it is 0 (zero) when right-clicked on the third column, the one
labeled 'Column 2'.

Also, the right-click still returns 'invalid tree item' occasionally.

/Jean

Roger Binns wrote:

···

   def bias(self, root):
       self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

   def HitTest(self, point):
       p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
       i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
       i = wxTreeItemIdPtr(i)
       i.thisown = 1
       return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

One more piece of data, after adding more columns. The column
number return by HitTest() is always zero, except for 'Column 1'
where it is one.

/Jean

PS) The TreeListCtrl does everything we need, except returning
the correct column number. It would really nice to have that
part work as well. I tried working around the problem using
the GetColumnWidth(), but that causes a crash (on RH 8 Linux)

Jean Brouwers wrote:

···

Roger,

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.
But it is 0 (zero) when right-clicked on the third column, the one
labeled 'Column 2'.

Also, the right-click still returns 'invalid tree item' occasionally.

/Jean

Roger Binns wrote:

   def bias(self, root):
       self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

   def HitTest(self, point):
       p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
       i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
       i = wxTreeItemIdPtr(i)
       i.thisown = 1
       return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Here is a workaround for HitTest() to return the correct
column number, also after column widths have been changed.

But, this workaround requires the width of each column to be
set by an explicit call to SetColumnWidth(). Failing to do
so will result in a crash (segementation fault) on Linux. Not
sure about Windows.

The complete, updated example is attached.

/Jean Brouwers

     def HitTest(self, point):
         w = self.GetMainWindow()
         p = self.ScreenToClient(w.ClientToScreen(point))
          #XXX find column since HitTest() doesn't work correctly,
          # but this workaround fails unless the column width is
          # set explicitly for every column with SetColumnWidth()
         w, c = p.x, -1
         for i in range(self.GetColumnCount()):
             w -= self.GetColumnWidth(i)
             if w < 0:
                c = i
                break
          #XXX ignore column returned by HitTest() thru w
         i, f, w = gzmc.wxTreeListCtrl_HitTest(self, p)
         return (i, f, c)

Jean Brouwers wrote:

wxTreeListCtrl2.py (8.24 KB)

···

One more piece of data, after adding more columns. The column
number return by HitTest() is always zero, except for 'Column 1'
where it is one.

/Jean

PS) The TreeListCtrl does everything we need, except returning
the correct column number. It would really nice to have that
part work as well. I tried working around the problem using
the GetColumnWidth(), but that causes a crash (on RH 8 Linux)

Jean Brouwers wrote:

Roger,

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.
But it is 0 (zero) when right-clicked on the third column, the one
labeled 'Column 2'.

Also, the right-click still returns 'invalid tree item' occasionally.

/Jean

Roger Binns wrote:

   def bias(self, root):
       self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

   def HitTest(self, point):
       p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
       i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
       i = wxTreeItemIdPtr(i)
       i.thisown = 1
       return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Below is a *possible* fix for the HitTest() problem in the
wxTreeListCtrl widget.

It seems that the loops in the HitTest() method don't accumulate
the column widths. The that would result in incorrect column
values beyond the first one.

I'm not familiar enough with this code to claim anything more
than that. There may be other places where a fix is required.
The original author or sombody like Alberro Griggio must have
the final word.

/Jean Brouwers

PS) After this one, the crash with GetColumnWidth() is still there.

PPS) From patched wxTreeListCtrl.cppp file near lines 1667 and 1684:

// ALB
wxTreeListItem *wxTreeListItem::HitTest(const wxPoint& point,
                                         const wxTreeListMainWindow *theCtrl,
                                         int &flags, int& column, int level)
{
     column = theCtrl->GetMainColumn(); //-1;
     wxTreeListItem* res = HitTest(point, theCtrl, flags, level);

     if(!res) {
         column = -1;
         return res;
     }
     if (point.x >= theCtrl->m_owner->GetHeaderWindow()->GetWidth())
         column = -1;
     else if(flags & wxTREE_HITTEST_ONITEMINDENT) {
         int x = 0;
         for(size_t i = 0; i < theCtrl->GetMainColumn(); ++i) {
             int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
             if(point.x >= x && point.x < x+w) {
                 flags ^= wxTREE_HITTEST_ONITEMINDENT;
                 flags |= wxTREE_HITTEST_ONITEMCOLUMN;
                 column = i;
                 return res;
             }
             x += w; // this may be a fix ???
         }
     }
     else if(flags & wxTREE_HITTEST_ONITEMRIGHT) {
         int x = 0;
         size_t i;
         for(i = 0; i < theCtrl->GetMainColumn()+1; ++i) {
             x += theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
         }
         for(i = theCtrl->GetMainColumn()+1;
             i < theCtrl->GetColumnCount(); ++i) {
             int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
             if(point.x >= x && point.x < x+w) {
                 flags ^= wxTREE_HITTEST_ONITEMRIGHT;
                 flags |= wxTREE_HITTEST_ONITEMCOLUMN;
                 column = i;
                 return res;
             }
             x += w; // this may be a fix ???
         }
     }

Jean Brouwers wrote:

···

Here is a workaround for HitTest() to return the correct
column number, also after column widths have been changed.

But, this workaround requires the width of each column to be
set by an explicit call to SetColumnWidth(). Failing to do
so will result in a crash (segementation fault) on Linux. Not
sure about Windows.

The complete, updated example is attached.

/Jean Brouwers

    def HitTest(self, point):
        w = self.GetMainWindow()
        p = self.ScreenToClient(w.ClientToScreen(point))
         #XXX find column since HitTest() doesn't work correctly,
         # but this workaround fails unless the column width is
         # set explicitly for every column with SetColumnWidth()
        w, c = p.x, -1
        for i in range(self.GetColumnCount()):
            w -= self.GetColumnWidth(i)
            if w < 0:
               c = i
               break
         #XXX ignore column returned by HitTest() thru w
        i, f, w = gzmc.wxTreeListCtrl_HitTest(self, p)
        return (i, f, c)

Jean Brouwers wrote:

One more piece of data, after adding more columns. The column
number return by HitTest() is always zero, except for 'Column 1'
where it is one.

/Jean

PS) The TreeListCtrl does everything we need, except returning
the correct column number. It would really nice to have that
part work as well. I tried working around the problem using
the GetColumnWidth(), but that causes a crash (on RH 8 Linux)

Jean Brouwers wrote:

Roger,

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.
But it is 0 (zero) when right-clicked on the third column, the one
labeled 'Column 2'.

Also, the right-click still returns 'invalid tree item' occasionally.

/Jean

Roger Binns wrote:

   def bias(self, root):
       self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

   def HitTest(self, point):
       p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
       i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
       i = wxTreeItemIdPtr(i)
       i.thisown = 1
       return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

------------------------------------------------------------------------

from wxPython.wx import *
#from wxPython.gizmos import wxTreeListCtrl
import wxPython.gizmos as gzm
import wxPython.gizmosc as gzmc

import images

class wxTreeListCtrl2(gzm.wxTreeListCtrl):
    _bias = 0, 0
    '''From ActiveState Community - Boosting coder and team productivity with ready-to-use open source languages and tools.
    '''
    def GetFirstChild(self, *args, **kwds):
        return gzmc.wxTreeListCtrl_GetFirstChild(self, *args, **kwds)

    def GetNextChild(self, *args, **kwds):
        return gzmc.wxTreeListCtrl_GetNextChild(self, *args, **kwds)

    def bias(self, root):
        self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

    def HitTest(self, point):
        w = self.GetMainWindow()
        p = self.ScreenToClient(w.ClientToScreen(point))
         #XXX find column since HitTest() doesn't work correctly,
         # but this workaround fails unless the column width is
         # set explicitly for every column with SetColumnWidth()
        w, c = p.x, -1
        for i in range(self.GetColumnCount()):
            w -= self.GetColumnWidth(i)
            if w < 0:
               c = i
               break
         #XXX ignore column returned by HitTest() thru w
        i, f, w = gzmc.wxTreeListCtrl_HitTest(self, p)
        return (i, f, c)

#----------------------------------------------------------------------

class TestPanel(wxPanel):
    def __init__(self, parent, log):
        self.log = log
        wxPanel.__init__(self, parent, -1)
        EVT_SIZE(self, self.OnSize)

        tID = wxNewId()
        self.tree = wxTreeListCtrl2(self, tID, style = wxTR_DEFAULT_STYLE
                                   #| wxTR_ROW_LINES
                                   #| wxTR_NO_LINES | wxTR_TWIST_BUTTONS
                                   )
        isz = (16,16)
        il = wxImageList(isz[0], isz[1])
        fldridx = il.Add(wxArtProvider_GetBitmap(wxART_FOLDER, wxART_OTHER, isz))
        fldropenidx = il.Add(wxArtProvider_GetBitmap(wxART_FILE_OPEN, wxART_OTHER, isz))
        fileidx = il.Add(wxArtProvider_GetBitmap(wxART_REPORT_VIEW, wxART_OTHER, isz))
        smileidx = il.Add(images.getSmilesBitmap())

        self.tree.SetImageList(il)
        self.il = il

        # create some columns
        self.tree.AddColumn("Main column")
        self.tree.AddColumn("Column 1")
        self.tree.AddColumn("Column 2")
        self.tree.AddColumn("Column 3")
        self.tree.SetMainColumn(0) # the one with the tree in it...
        self.tree.SetColumnWidth(0, 175)
         # XXX set width for eveyr column, see HitTest() above
        self.tree.SetColumnWidth(1, 125)
        self.tree.SetColumnWidth(2, 100)
        self.tree.SetColumnWidth(3, 75)

        self.root = self.tree.AddRoot("The Root Item")
        self.tree.SetItemText(self.root, "col 1 root", 1)
        self.tree.SetItemText(self.root, "col 2 root", 2)
        self.tree.SetItemText(self.root, "col 3 root", 3)
        self.tree.SetItemImage(self.root, fldridx, which = wxTreeItemIcon_Normal)
        self.tree.SetItemImage(self.root, fldropenidx, which = wxTreeItemIcon_Expanded)

        self.tree.bias(self.root)

        for x in range(15):
            txt = "Item %d " % x
            child = self.tree.AppendItem(self.root, txt)
            self.tree.SetItemText(child, txt + "(c1)", 1)
            self.tree.SetItemText(child, txt + "(c2)", 2)
            self.tree.SetItemText(child, txt + "(c3)", 3)
            self.tree.SetItemImage(child, fldridx, which = wxTreeItemIcon_Normal)
            self.tree.SetItemImage(child, fldropenidx, which = wxTreeItemIcon_Expanded)

            for y in range(5):
                txt = "item %d-%s " % (x, chr(ord("a")+y))
                last = self.tree.AppendItem(child, txt)
                self.tree.SetItemText(last, txt + "(c1)", 1)
                self.tree.SetItemText(last, txt + "(c2)", 2)
                self.tree.SetItemText(last, txt + "(c3)", 3)
                self.tree.SetItemImage(last, fldridx, which = wxTreeItemIcon_Normal)
                self.tree.SetItemImage(last, fldropenidx, which = wxTreeItemIcon_Expanded)

                for z in range(3):
                    txt = "item %d-%s-%d" % (x, chr(ord("a")+y), z)
                    item = self.tree.AppendItem(last, txt)
                    self.tree.SetItemText(item, txt + "(c1)", 1)
                    self.tree.SetItemText(item, txt + "(c2)", 2)
                    self.tree.SetItemText(item, txt + "(c3)", 3)
                    self.tree.SetItemImage(item, fileidx, which = wxTreeItemIcon_Normal)
                    self.tree.SetItemImage(item, smileidx, which = wxTreeItemIcon_Selected)

        self.tree.Expand(self.root)

        EVT_TREE_ITEM_EXPANDED (self, tID, self.OnItemExpanded)
        EVT_TREE_ITEM_COLLAPSED (self, tID, self.OnItemCollapsed)
        EVT_TREE_SEL_CHANGED (self, tID, self.OnSelChanged)
        EVT_TREE_BEGIN_LABEL_EDIT(self, tID, self.OnBeginEdit)
        EVT_TREE_END_LABEL_EDIT (self, tID, self.OnEndEdit)
        EVT_TREE_ITEM_ACTIVATED (self, tID, self.OnActivate)

        w = self.tree.GetMainWindow()
        EVT_LEFT_DCLICK(w, self.OnLeftDClick)
        EVT_RIGHT_DOWN(w, self.OnRightDown)

    def OnLeftDClick(self, event):
## pt = event.GetPosition();
## item, flags, col = self.tree.HitTest(pt)
## self.log.WriteText("OnLeftDClick: %s\n" % self.tree.GetItemText(item))
## parent = self.tree.GetItemParent(item)
## self.tree.SortChildren(parent)
         event.Skip()

    def OnRightDown(self, event):
        pt = event.GetPosition();
        item, flags, col = self.tree.HitTest(pt)
        self.log.WriteText("OnRightDown: %s, %s, col %s\n" %
                           (self.tree.GetItemText(item), item.__class__, col))
        self.tree.SelectItem(item)

    def OnBeginEdit(self, event):
        self.log.WriteText("OnBeginEdit\n")
        # show how to prevent edit...
        if self.tree.GetItemText(event.GetItem()) == "The Root Item":
            wxBell()
            self.log.WriteText("You can't edit this one...\n")

            # Lets just see what's visible of its children
            cookie = 0
            root = event.GetItem()
            (child, cookie) = self.tree.GetFirstChild(root, cookie)
            while child.IsOk():
                self.log.WriteText("Child [%s] visible = %d" %
                                   (self.tree.GetItemText(child),
                                    self.tree.IsVisible(child)))
                (child, cookie) = self.tree.GetNextChild(root, cookie)

            event.Veto()

    def OnEndEdit(self, event):
        self.log.WriteText("OnEndEdit\n")
        # show how to reject edit, we'll not allow any digits
        for x in event.GetLabel():
            if x in string.digits:
                self.log.WriteText("You can't enter digits...\n")
                event.Veto()
                return

    def OnSize(self, event):
        w,h = self.GetClientSizeTuple()
        self.tree.SetDimensions(0, 0, w, h)

    def OnItemExpanded(self, event):
        item = event.GetItem()
        self.log.WriteText("OnItemExpanded: %s\n" % self.tree.GetItemText(item))

    def OnItemCollapsed(self, event):
        item = event.GetItem()
        self.log.WriteText("OnItemCollapsed: %s\n" % self.tree.GetItemText(item))

    def OnSelChanged(self, event):
        self.item = event.GetItem()
        self.log.WriteText("OnSelChanged: %s\n" % self.tree.GetItemText(self.item))
        if wxPlatform == '__WXMSW__':
            self.log.WriteText("BoundingRect: %s\n" %
                               self.tree.GetBoundingRect(self.item, True))
        #items = self.tree.GetSelections()
        #print map(self.tree.GetItemText, items)
        event.Skip()

    def OnActivate(self, event):
        self.log.WriteText("OnActivate: %s\n" % self.tree.GetItemText(self.item))

#----------------------------------------------------------------------

def runTest(frame, nb, log):
    win = TestPanel(nb, log)
    return win

#----------------------------------------------------------------------

overview = """<html><body>
<h2><center>wxTreeListCtrl2</center></h2>

The wxTreeListCtrl is essentially a wxTreeCtrl
with extra columns, such that the look is similar
to a wxListCtrl.

</body></html>
"""

if __name__ == '__main__':
    #raw_input("Press enter...")
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])])

------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Jean Brouwers wrote:

Below is a *possible* fix for the HitTest() problem in the
wxTreeListCtrl widget.

Alberto, can you check this please?

Thanks,
--Robin

···

It seems that the loops in the HitTest() method don't accumulate
the column widths. The that would result in incorrect column
values beyond the first one.

I'm not familiar enough with this code to claim anything more
than that. There may be other places where a fix is required.
The original author or sombody like Alberro Griggio must have
the final word.

/Jean Brouwers

PS) After this one, the crash with GetColumnWidth() is still there.

PPS) From patched wxTreeListCtrl.cppp file near lines 1667 and 1684:

// ALB
wxTreeListItem *wxTreeListItem::HitTest(const wxPoint& point,
                                        const wxTreeListMainWindow *theCtrl,
                                        int &flags, int& column, int level)
{
    column = theCtrl->GetMainColumn(); //-1;
    wxTreeListItem* res = HitTest(point, theCtrl, flags, level);

    if(!res) {
        column = -1;
        return res;
    }
    if (point.x >= theCtrl->m_owner->GetHeaderWindow()->GetWidth())
        column = -1;
    else if(flags & wxTREE_HITTEST_ONITEMINDENT) {
        int x = 0;
        for(size_t i = 0; i < theCtrl->GetMainColumn(); ++i) {
            int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
            if(point.x >= x && point.x < x+w) {
                flags ^= wxTREE_HITTEST_ONITEMINDENT;
                flags |= wxTREE_HITTEST_ONITEMCOLUMN;
                column = i;
                return res;
            }
            x += w; // this may be a fix ???
        }
    }
    else if(flags & wxTREE_HITTEST_ONITEMRIGHT) {
        int x = 0;
        size_t i;
        for(i = 0; i < theCtrl->GetMainColumn()+1; ++i) {
            x += theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
        }
        for(i = theCtrl->GetMainColumn()+1;
            i < theCtrl->GetColumnCount(); ++i) {
            int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
            if(point.x >= x && point.x < x+w) {
                flags ^= wxTREE_HITTEST_ONITEMRIGHT;
                flags |= wxTREE_HITTEST_ONITEMCOLUMN;
                column = i;
                return res;
            }
            x += w; // this may be a fix ???
        }
    }

Jean Brouwers wrote:

Here is a workaround for HitTest() to return the correct
column number, also after column widths have been changed.

But, this workaround requires the width of each column to be
set by an explicit call to SetColumnWidth(). Failing to do
so will result in a crash (segementation fault) on Linux. Not
sure about Windows.

The complete, updated example is attached.

/Jean Brouwers

    def HitTest(self, point):
        w = self.GetMainWindow()
        p = self.ScreenToClient(w.ClientToScreen(point))
         #XXX find column since HitTest() doesn't work correctly,
         # but this workaround fails unless the column width is
         # set explicitly for every column with SetColumnWidth()
        w, c = p.x, -1
        for i in range(self.GetColumnCount()):
            w -= self.GetColumnWidth(i)
            if w < 0:
               c = i
               break
         #XXX ignore column returned by HitTest() thru w
        i, f, w = gzmc.wxTreeListCtrl_HitTest(self, p)
        return (i, f, c)

Jean Brouwers wrote:

One more piece of data, after adding more columns. The column
number return by HitTest() is always zero, except for 'Column 1'
where it is one.

/Jean

PS) The TreeListCtrl does everything we need, except returning
the correct column number. It would really nice to have that
part work as well. I tried working around the problem using
the GetColumnWidth(), but that causes a crash (on RH 8 Linux)

Jean Brouwers wrote:

Roger,

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.
But it is 0 (zero) when right-clicked on the third column, the one
labeled 'Column 2'.

Also, the right-click still returns 'invalid tree item' occasionally.

/Jean

Roger Binns wrote:

   def bias(self, root):
       self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

   def HitTest(self, point):
       p = wxPoint(point.x + self._bias[0], point.y + self._bias[1])
       i, f, c = gzmc.wxTreeListCtrl_HitTest(self, p)
       i = wxTreeItemIdPtr(i)
       i.thisown = 1
       return (i, f, c)

Get rid of the bias and that version of hittest. This one works:

    def HitTest(self, point):
        w = self.GetMainWindow()
        return gizmosc.wxTreeListCtrl_HitTest(self, self.ScreenToClient(w.ClientToScreen(point)))

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

------------------------------------------------------------------------

from wxPython.wx import *
#from wxPython.gizmos import wxTreeListCtrl
import wxPython.gizmos as gzm
import wxPython.gizmosc as gzmc

import images

class wxTreeListCtrl2(gzm.wxTreeListCtrl):
    _bias = 0, 0
    '''From ActiveState Community - Boosting coder and team productivity with ready-to-use open source languages and tools.
    '''
    def GetFirstChild(self, *args, **kwds):
        return gzmc.wxTreeListCtrl_GetFirstChild(self, *args, **kwds)

    def GetNextChild(self, *args, **kwds):
        return gzmc.wxTreeListCtrl_GetNextChild(self, *args, **kwds)

    def bias(self, root):
        self._bias = 0, self.GetBoundingRect(root).GetHeight() + self.GetSpacing()

    def HitTest(self, point):
        w = self.GetMainWindow()
        p = self.ScreenToClient(w.ClientToScreen(point))
         #XXX find column since HitTest() doesn't work correctly,
         # but this workaround fails unless the column width is
         # set explicitly for every column with SetColumnWidth()
        w, c = p.x, -1
        for i in range(self.GetColumnCount()):
            w -= self.GetColumnWidth(i)
            if w < 0:
               c = i
               break
         #XXX ignore column returned by HitTest() thru w
        i, f, w = gzmc.wxTreeListCtrl_HitTest(self, p)
        return (i, f, c)

#----------------------------------------------------------------------

class TestPanel(wxPanel):
    def __init__(self, parent, log):
        self.log = log
        wxPanel.__init__(self, parent, -1)
        EVT_SIZE(self, self.OnSize)

        tID = wxNewId()
        self.tree = wxTreeListCtrl2(self, tID, style = wxTR_DEFAULT_STYLE
                                   #| wxTR_ROW_LINES
                                   #| wxTR_NO_LINES | wxTR_TWIST_BUTTONS
                                   )
        isz = (16,16)
        il = wxImageList(isz[0], isz[1])
        fldridx = il.Add(wxArtProvider_GetBitmap(wxART_FOLDER, wxART_OTHER, isz))
        fldropenidx = il.Add(wxArtProvider_GetBitmap(wxART_FILE_OPEN, wxART_OTHER, isz))
        fileidx = il.Add(wxArtProvider_GetBitmap(wxART_REPORT_VIEW, wxART_OTHER, isz))
        smileidx = il.Add(images.getSmilesBitmap())

        self.tree.SetImageList(il)
        self.il = il

        # create some columns
        self.tree.AddColumn("Main column")
        self.tree.AddColumn("Column 1")
        self.tree.AddColumn("Column 2")
        self.tree.AddColumn("Column 3")
        self.tree.SetMainColumn(0) # the one with the tree in it...
        self.tree.SetColumnWidth(0, 175)
         # XXX set width for eveyr column, see HitTest() above
        self.tree.SetColumnWidth(1, 125)
        self.tree.SetColumnWidth(2, 100)
        self.tree.SetColumnWidth(3, 75)

        self.root = self.tree.AddRoot("The Root Item")
        self.tree.SetItemText(self.root, "col 1 root", 1)
        self.tree.SetItemText(self.root, "col 2 root", 2)
        self.tree.SetItemText(self.root, "col 3 root", 3)
        self.tree.SetItemImage(self.root, fldridx, which = wxTreeItemIcon_Normal)
        self.tree.SetItemImage(self.root, fldropenidx, which = wxTreeItemIcon_Expanded)

        self.tree.bias(self.root)

        for x in range(15):
            txt = "Item %d " % x
            child = self.tree.AppendItem(self.root, txt)
            self.tree.SetItemText(child, txt + "(c1)", 1)
            self.tree.SetItemText(child, txt + "(c2)", 2)
            self.tree.SetItemText(child, txt + "(c3)", 3)
            self.tree.SetItemImage(child, fldridx, which = wxTreeItemIcon_Normal)
            self.tree.SetItemImage(child, fldropenidx, which = wxTreeItemIcon_Expanded)

            for y in range(5):
                txt = "item %d-%s " % (x, chr(ord("a")+y))
                last = self.tree.AppendItem(child, txt)
                self.tree.SetItemText(last, txt + "(c1)", 1)
                self.tree.SetItemText(last, txt + "(c2)", 2)
                self.tree.SetItemText(last, txt + "(c3)", 3)
                self.tree.SetItemImage(last, fldridx, which = wxTreeItemIcon_Normal)
                self.tree.SetItemImage(last, fldropenidx, which = wxTreeItemIcon_Expanded)

                for z in range(3):
                    txt = "item %d-%s-%d" % (x, chr(ord("a")+y), z)
                    item = self.tree.AppendItem(last, txt)
                    self.tree.SetItemText(item, txt + "(c1)", 1)
                    self.tree.SetItemText(item, txt + "(c2)", 2)
                    self.tree.SetItemText(item, txt + "(c3)", 3)
                    self.tree.SetItemImage(item, fileidx, which = wxTreeItemIcon_Normal)
                    self.tree.SetItemImage(item, smileidx, which = wxTreeItemIcon_Selected)

        self.tree.Expand(self.root)

        EVT_TREE_ITEM_EXPANDED (self, tID, self.OnItemExpanded)
        EVT_TREE_ITEM_COLLAPSED (self, tID, self.OnItemCollapsed)
        EVT_TREE_SEL_CHANGED (self, tID, self.OnSelChanged)
        EVT_TREE_BEGIN_LABEL_EDIT(self, tID, self.OnBeginEdit)
        EVT_TREE_END_LABEL_EDIT (self, tID, self.OnEndEdit)
        EVT_TREE_ITEM_ACTIVATED (self, tID, self.OnActivate)

        w = self.tree.GetMainWindow()
        EVT_LEFT_DCLICK(w, self.OnLeftDClick)
        EVT_RIGHT_DOWN(w, self.OnRightDown)

    def OnLeftDClick(self, event):
## pt = event.GetPosition();
## item, flags, col = self.tree.HitTest(pt)
## self.log.WriteText("OnLeftDClick: %s\n" % self.tree.GetItemText(item))
## parent = self.tree.GetItemParent(item)
## self.tree.SortChildren(parent)
         event.Skip()

    def OnRightDown(self, event):
        pt = event.GetPosition();
        item, flags, col = self.tree.HitTest(pt)
        self.log.WriteText("OnRightDown: %s, %s, col %s\n" %
                           (self.tree.GetItemText(item), item.__class__, col))
        self.tree.SelectItem(item)

    def OnBeginEdit(self, event):
        self.log.WriteText("OnBeginEdit\n")
        # show how to prevent edit...
        if self.tree.GetItemText(event.GetItem()) == "The Root Item":
            wxBell()
            self.log.WriteText("You can't edit this one...\n")

            # Lets just see what's visible of its children
            cookie = 0
            root = event.GetItem()
            (child, cookie) = self.tree.GetFirstChild(root, cookie)
            while child.IsOk():
                self.log.WriteText("Child [%s] visible = %d" %
                                   (self.tree.GetItemText(child),
                                    self.tree.IsVisible(child)))
                (child, cookie) = self.tree.GetNextChild(root, cookie)

            event.Veto()

    def OnEndEdit(self, event):
        self.log.WriteText("OnEndEdit\n")
        # show how to reject edit, we'll not allow any digits
        for x in event.GetLabel():
            if x in string.digits:
                self.log.WriteText("You can't enter digits...\n")
                event.Veto()
                return

    def OnSize(self, event):
        w,h = self.GetClientSizeTuple()
        self.tree.SetDimensions(0, 0, w, h)

    def OnItemExpanded(self, event):
        item = event.GetItem()
        self.log.WriteText("OnItemExpanded: %s\n" % self.tree.GetItemText(item))

    def OnItemCollapsed(self, event):
        item = event.GetItem()
        self.log.WriteText("OnItemCollapsed: %s\n" % self.tree.GetItemText(item))

    def OnSelChanged(self, event):
        self.item = event.GetItem()
        self.log.WriteText("OnSelChanged: %s\n" % self.tree.GetItemText(self.item))
        if wxPlatform == '__WXMSW__':
            self.log.WriteText("BoundingRect: %s\n" %
                               self.tree.GetBoundingRect(self.item, True))
        #items = self.tree.GetSelections()
        #print map(self.tree.GetItemText, items)
        event.Skip()

    def OnActivate(self, event):
        self.log.WriteText("OnActivate: %s\n" % self.tree.GetItemText(self.item))

#----------------------------------------------------------------------

def runTest(frame, nb, log):
    win = TestPanel(nb, log)
    return win

#----------------------------------------------------------------------

overview = """<html><body>
<h2><center>wxTreeListCtrl2</center></h2>

The wxTreeListCtrl is essentially a wxTreeCtrl
with extra columns, such that the look is similar
to a wxListCtrl.

</body></html>
"""

if __name__ == '__main__':
    #raw_input("Press enter...")
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])])

------------------------------------------------------------------------

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.

I don't care about the column number so it always worked for
me :slight_smile:

Roger

Roger,

This is confusing. The problem I mentioned specifically was the
incorrect column number.

What was the problem fixed by the HitTest() version you suggested?

/Jean

Roger Binns wrote:

···

Using your HitTest() still fails. The column number returned
in the 3rd element is (and was) correct for the first two columns.

I don't care about the column number so it always worked for me :slight_smile:

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

What was the problem fixed by the HitTest() version you suggested?

It got the row wrong.

Roger

As far as I can tell, your version and the one suggested earlier
give the same, correct result for the row item. Both with the
ROOT hidden and shown.

/Jean

PS) There is still another, new problem with the column when the
window is scolled horizontally. More about that in a separate
thread.

Roger Binns wrote:

···

What was the problem fixed by the HitTest() version you suggested?

It got the row wrong.

Roger

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

There is another, separate issue with wxTreeListScroll. If
the window is scrolled horizontally, a number of things seem
to be wrong. More likely, it is my limited understanding of
the details, but here are my observations.

1) Selecting a row after scrolling horizontally highlights
the row, but the highlighting does not extend all the way to
the right window edge. However, afeter the window is scrolled
back and forth, the highlighting is drawn correctly, but the
next hightlight is wrong again

2) The coordinates returned by GetPosition() in an RIGHT_DOWN
event seem to be in screen space and consequently do not take
any scrolling into account. Is that the expected behavior?

3) After applying the CalcScrolledPosition() method from the
main window, the position is still incorrect. This method
works fine for similar problems in other places, like wxGrid.

4) The behavior is the same on Windows XP and RH8/gtk2 using
wxPython2.4.1.2 and Python 2.3.1.

/Jean Brouwers

PS) As a result, the HitTest() workaround I sent in an earlier
message to correct the column index, only works (i) if the
window is not scrolled horizontally and (ii) the column widths
have been set explicitly.

If you use AssignImageList(), instead of SetImageList() in a
wxTreeListCtrl instance, wxPython crashes upon exit.

The problem exists both on Linux and Windows, but only with
wxTreeListCtrl. Using wxTreeCtrl.AssignImageList() works.

/Jean Brouwers

To avoid misunderstanding, using SetImageList() on wxTreeListCtrl
works fine.

Also, using AssignImageList() and SetImageList() with twxTreeCtrl
both work without this problem.

Jean Brouwers wrote:

···

If you use AssignImageList(), instead of SetImageList() in a
wxTreeListCtrl instance, wxPython crashes upon exit.

The problem exists both on Linux and Windows, but only with
wxTreeListCtrl. Using wxTreeCtrl.AssignImageList() works.

/Jean Brouwers

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Jean Brouwers wrote:

If you use AssignImageList(), instead of SetImageList() in a
wxTreeListCtrl instance, wxPython crashes upon exit.

The problem exists both on Linux and Windows, but only with
wxTreeListCtrl. Using wxTreeCtrl.AssignImageList() works.

Sounds like it is destroying the image list twice. Which version of wxPython are you using? IIRC, versions prior to 2.4.2.4 did not have the following code in the wrapper for AssignImageList, (but most people added it themselves after reading about it here.)

def AssignImageList(self, *_args, **_kwargs):
     val = gizmosc.wxTreeListCtrl_AssignImageList(self, *_args, **_kwargs)
     _args[0].thisown = 0 ## Add this line.
     return val

That extra line tells SWIG that this C++ object is no longer owned by the proxy object.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Jean Brouwers wrote:

There is another, separate issue with wxTreeListScroll. If
the window is scrolled horizontally, a number of things seem
to be wrong. More likely, it is my limited understanding of
the details, but here are my observations.

1) Selecting a row after scrolling horizontally highlights
the row, but the highlighting does not extend all the way to
the right window edge. However, afeter the window is scrolled
back and forth, the highlighting is drawn correctly, but the
next hightlight is wrong again

Alberto?

2) The coordinates returned by GetPosition() in an RIGHT_DOWN
event seem to be in screen space and consequently do not take
any scrolling into account. Is that the expected behavior?

No, I would expect it to be just like wxScrolledWindow, (because that is where it is coming from.) The coords should be the "device coordinants" or pixels relative to the physical window origin.

3) After applying the CalcScrolledPosition() method from the
main window, the position is still incorrect. This method
works fine for similar problems in other places, like wxGrid.

If the above is fixed then this should be too.

4) The behavior is the same on Windows XP and RH8/gtk2 using
wxPython2.4.1.2 and Python 2.3.1.

Ah, this answers my last question. Have you tried wxPython 2.4.2.4? BTW, the behavior is the same on both platforms because the same generic code is used everywhere for the wxTreeListCtrl.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Thank you very much, this patch indeed fixes the seg fault.

In the mean time, we run into a number of other issues with
wxTreeListCtrl which we would like to see. Again, most may
be due to lack of familiarity with wxPython or wxWindows on
our part.

The three most critical ones are:

1) How to get events in the column labels, the top row?

2) How to draw the lines between columns. Or if they are
drawn, how to change the color?

3) Can the row size (heigth) be cofnigured for specific rows?

it appears that 1) and 2) are possible with the wxListCtrl but
that functionality is not available in the wxTreeListCtrl.

/Jean Brouwers

Robin Dunn wrote:

···

Jean Brouwers wrote:

If you use AssignImageList(), instead of SetImageList() in a
wxTreeListCtrl instance, wxPython crashes upon exit.

The problem exists both on Linux and Windows, but only with
wxTreeListCtrl. Using wxTreeCtrl.AssignImageList() works.

Sounds like it is destroying the image list twice. Which version of wxPython are you using? IIRC, versions prior to 2.4.2.4 did not have the following code in the wrapper for AssignImageList, (but most people added it themselves after reading about it here.)

def AssignImageList(self, *_args, **_kwargs):
    val = gizmosc.wxTreeListCtrl_AssignImageList(self, *_args, **_kwargs)
    _args[0].thisown = 0 ## Add this line.
    return val

That extra line tells SWIG that this C++ object is no longer owned by the proxy object.

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Jean Brouwers wrote:

Thank you very much, this patch indeed fixes the seg fault.

In the mean time, we run into a number of other issues with
wxTreeListCtrl which we would like to see. Again, most may
be due to lack of familiarity with wxPython or wxWindows on
our part.

The three most critical ones are:

1) How to get events in the column labels, the top row?

Use EVT_LIST_COL_CLICK

2) How to draw the lines between columns. Or if they are
drawn, how to change the color?

I don't see any code for doing that, but it could probably be added without too much trouble.

3) Can the row size (heigth) be cofnigured for specific rows?

Use wxTR_VARIABLE_ROW_HEIGHT style and then when you set an item's font or other attributes the height will be calculated.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin,

Again, thank you for your quick reply and we should have thought
about this one ourselves.

We tried EVT_LIST_COL_CLICK, by copying all the EVT_LIST_COL_...
calls and handlers from the wxListCtrl demo.

The EVT_COL_RIGHT_CLICK event is indeed handled, but not the
other ones. Not sure yet why.

Also, several of the item() methods like GetAlign(), etc. which
seem to exist in the wxListCtrl, don't in the wxTreeListCtrl**.

We may not need all of those or can probably work around the
missing/failing ones. But it is just striking that these things
are missing. Wasn't wxTreeListCtrl intended to combine wxTreeCtrl
and wxListCtrl?

/Jean Brouwers

**) Most interesting is that (1) there is a method GetColumn()
in EVT_COL_RIGHT_CLICK events which (2) does work correctly. In
other words, the column is available in the events from the top
(label) row in wxTreeListCtrl, but *not* in events from other rows.

Robin Dunn wrote:

···

Jean Brouwers wrote:

Thank you very much, this patch indeed fixes the seg fault.

In the mean time, we run into a number of other issues with
wxTreeListCtrl which we would like to see. Again, most may
be due to lack of familiarity with wxPython or wxWindows on
our part.

The three most critical ones are:

1) How to get events in the column labels, the top row?

Use EVT_LIST_COL_CLICK

2) How to draw the lines between columns. Or if they are
drawn, how to change the color?

I don't see any code for doing that, but it could probably be added without too much trouble.

3) Can the row size (heigth) be cofnigured for specific rows?

Use wxTR_VARIABLE_ROW_HEIGHT style and then when you set an item's font or other attributes the height will be calculated.