views:

222

answers:

2

The preferred size of the wx.TreeCtrl seems to be the minimum size into which all elements will fit when the tree is completely collapsed. Is there a good (ie cross-platform compatible) way to compute the width of the tree with everything expanded? My current solution is this:

def max_width(self):
    dc = wx.ScreenDC()
    dc.SetFont(self.GetFont())
    widths = []
    for item, depth in self.__walk_items():
        if item != self.root:
            width = dc.GetTextExtent(self.GetItemText(item))[0] + self.GetIndent()*depth
            widths.append(width)
    return max(widths) + self.GetIndent()

This works great in win32, but no good under linux. Is there some way to get the TreeCtrl itself to tell me its size so that I can override the size it reports? (ALways returning the maximally expanded width)

edit: pardon me for not providing the functions I use above, but you get the idea, I'm walking the tree, and getting the width of every label, and returning the total width of the widest one (accounting for indent)

+1  A: 

I find that the GetBestSize seems to consider the collapsed items. For example, with the wxTreeCtrl in the demo (using Ubuntu, and wxPython 2.8.7.1), when I change the length of the inner text string (line 74 in my version of the demo) the return value of self.tree.GetBestSize() does take this new length of the inner string into account, even though the tree is unexpanded. Maybe you need to call SetQuickBestSize(False)?

tom10
I like this method better than manually calculating the best size from the tree itself, but it doesn't seem to work on win32. Even if I use SetQuickBestSize(False), GetBestSize() of a fully collapsed tree only returns the width of the root. Any other ideas?
Ryan
Hmm... I tried it with win32 and agree that it doesn't work. I guess the docs don't explicitly say that it should, but it seems like a bug worth reporting. As a kludge you could do: `tree.ExpandAll(); tree.GetBestSize(); tree.CollapseAll()` when the OS is win32.
tom10
That doesn't work either! The problem on win32 seems to be that GetBestSize doesn't get called if you're subclassing the control. SetMinSize seems to work though. Also, Freeze()ing, expanding everything, getting the best size, and Thaw()ing didn't work on win32 for me. I'll post my ultimate solution below. Thank you for the help!
Ryan
A: 

This code works for me on both windows and linux:

def compute_best_size(self):
    if os.name == 'nt':
        best_size = (self.__max_width_win32(), -1)
    else:
        best_size = (self.__max_width(), -1)
    self.SetMinSize(best_size)

def __max_width_win32(self):
    dc = wx.ScreenDC()
    dc.SetFont(self.GetFont())
    widths = []
    for item, depth in self.__walk_items():
        if item != self.root:
            width = dc.GetTextExtent(self.GetItemText(item))[0] + self.GetIndent()*depth
            widths.append(width)
    return max(widths) + self.GetIndent()

def __max_width(self):
    self.Freeze()
    expanded = {}
    for item in self.get_items():
        if item is not self.root:
            expanded[item] = self.IsExpanded(item)
    self.ExpandAll()
    best_size = self.GetBestSize()
    for item in expanded:
        if not expanded[item]: self.Collapse(item)
    self.Thaw()
    return best_size[0]

... And call compute_best_size() every time a new item is added to the tree.

Ryan