Merge branch 'HelioGuilherme66-40xcustomtreectrl' into wxPy-4.0.x

(cherry picked from commit 32ae30b988)
This commit is contained in:
Robin Dunn
2018-09-25 15:18:22 -07:00
parent 900f002518
commit addb1906c9
4 changed files with 173 additions and 33 deletions

View File

@@ -114,6 +114,10 @@ Changes in this release include the following:
* Fix rendering and mouse sensitivity in UltimateListCtrl when adding HyperText
items. (#1010)
* Added a parameter to lib.agw.CustomTreeCtrl.SetItemWindow(), to allow
positioning the Window (a small image) on the left of text in a
CustomTreeItem. (#PR886).

View File

@@ -848,8 +848,8 @@ class CustomTreeCtrlDemo(wx.Panel):
check = wx.CheckBox(pnl, -1, event)
eventssizer.Add(check, 0, tags, 3)
if event in ["EVT_TREE_ITEM_EXPANDED", "EVT_TREE_ITEM_COLLAPSED",
"EVT_TREE_SEL_CHANGED", "EVT_TREE_SEL_CHANGING"]:
if event in ["EVT_TREE_ITEM_EXPANDED", "EVT_TREE_ITEM_EXPANDING", "EVT_TREE_ITEM_COLLAPSED",
"EVT_TREE_ITEM_COLLAPSING", "EVT_TREE_SEL_CHANGED", "EVT_TREE_SEL_CHANGING"]:
check.SetValue(1)
@@ -921,6 +921,9 @@ class CustomTreeCtrlDemo(wx.Panel):
leftimagelist = wx.CheckBox(pnl, -1, "Use Left ImageList")
leftimagelist.Bind(wx.EVT_CHECKBOX, self.OnLeftImageList)
self.windowposition = wx.CheckBox(pnl, -1, "Use Image Window Left of Label")
self.windowposition.Bind(wx.EVT_CHECKBOX, self.OnWindowPos)
colourssizer.Add(sizer1, 0, wx.EXPAND)
colourssizer.Add(sizer2, 0, wx.EXPAND)
colourssizer.Add(sizer3, 0, wx.EXPAND)
@@ -928,6 +931,7 @@ class CustomTreeCtrlDemo(wx.Panel):
colourssizer.Add(sizer5, 0, wx.EXPAND)
colourssizer.Add(sizer6, 0, wx.EXPAND)
colourssizer.Add(leftimagelist, 0, wx.ALL, 5)
colourssizer.Add(self.windowposition, 0, wx.ALL, 5)
sizera = wx.BoxSizer(wx.HORIZONTAL)
self.checknormal = wx.CheckBox(pnl, -1, "Standard Colours")
@@ -1012,6 +1016,7 @@ class CustomTreeCtrlDemo(wx.Panel):
self.tree.Destroy()
self.tree = newtree
# Todo: The settings in the leftpanel should be reset too
# self.PopulateLeftPanel(self.tree.styles, self.tree.events) # Crashes on LeftImage selection
def OnCheckStyle(self, event):
@@ -1175,6 +1180,18 @@ class CustomTreeCtrlDemo(wx.Panel):
self.tree.Refresh()
def OnWindowPos(self, event):
checked = event.IsChecked()
self.tree.window_on_the_right = not checked
animIcon = self.tree.FindItem(self.tree.GetRootItem(), "item 1-f-4")
parentIcon = self.tree.GetItemParent(animIcon)
self.tree.Expand(parentIcon)
self.tree.Collapse(parentIcon)
self.tree.EnsureVisible(animIcon)
self.tree.RefreshItemWithWindows() # only if the style TR_ALIGN_WINDOWS_RIGHT is used
self.tree.Refresh() # Ineffective (only updates when collapse/expand)
def OnCheckNormal(self, event):
self.radiohorizontal.Enable(False)
@@ -1291,6 +1308,11 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
self.events = events
self.styles = treestyles
self.item = None
self.windowed_item = None
# To set the position on Window image (default==True)
self.window_on_the_right = True
self._animctrl = None
self.anim_gif_item = None # EVT_TREE_ITEM_CHECKED to update position
il = wx.ImageList(16, 16)
@@ -1326,6 +1348,12 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
combobox.Bind(wx.EVT_COMBOBOX, self.OnComboBox)
lenArtIds = len(ArtIDs) - 2
# This is for SetWindow on TreeItem, when collapsed is on the right, on the left otherwise
self.img = list()
self.img.append(wx.Bitmap("./bitmaps/pause.png", wx.BITMAP_TYPE_PNG))
self.img.append(wx.Bitmap("./bitmaps/play.png", wx.BITMAP_TYPE_PNG))
self.Image = wx.StaticBitmap(self, -1, bitmap=self.img[0])
for x in range(15):
if x == 1:
child = self.AppendItem(self.root, "Item %d" % x + "\nHello World\nHappy wxPython-ing!")
@@ -1342,7 +1370,7 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
if random.randint(0, 5) == 0:
self.AppendSeparator(self.root)
for y in range(5):
for y in range(6):
if y == 0 and x == 1:
last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=2, wnd=self.gauge)
elif y == 1 and x == 2:
@@ -1354,6 +1382,10 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)))
elif y == 4 and x == 1:
last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), wnd=combobox)
elif y == 5 and x == 1:
last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)))
last.SetWindow(self.Image, self.window_on_the_right) # Add the Window on on_the_right=True
self.windowed_item = last
else:
last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=2)
@@ -1372,6 +1404,9 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
item = self.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=1)
if random.randint(0, 3) == 1:
self.SetItem3State(item, True)
if z == 4 and y == 5 and x == 1:
self._add_animated_gif(item, self.window_on_the_right)
self.anim_gif_item = item
elif 0 < z <= 2:
item = self.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=2)
elif z == 0:
@@ -1405,7 +1440,9 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
if not hasattr(mainframe, "leftpanel"):
self.Bind(CT.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded)
self.Bind(CT.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding)
self.Bind(CT.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
self.Bind(CT.EVT_TREE_ITEM_COLLAPSING, self.OnItemCollapsing)
self.Bind(CT.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
self.Bind(CT.EVT_TREE_SEL_CHANGING, self.OnSelChanging)
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
@@ -1838,7 +1875,13 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
item = event.GetItem()
if item:
self.log.write("OnItemExpanding: %s" % self.GetItemText(item) + "\n")
if item == self.windowed_item:
item.DeleteWindow()
self.Image = wx.StaticBitmap(self, -1, bitmap=self.img[1])
item.SetWindow(self.Image, self.window_on_the_right)
child = self.GetLastChild(item)
if child == self.anim_gif_item:
self._add_animated_gif(self.anim_gif_item, self.window_on_the_right)
event.Skip()
@@ -1854,9 +1897,48 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
item = event.GetItem()
if item:
self.log.write("OnItemCollapsing: %s" % self.GetItemText(item) + "\n")
if item == self.windowed_item:
item.DeleteWindow()
self.Image = wx.StaticBitmap(self, -1, bitmap=self.img[0])
item.SetWindow(self.Image, self.window_on_the_right)
# Lets hide all Windows
self._collapse_node(item, lambda t: self._hideanimation(t))
event.Skip()
def _collapse_node(self, item, func):
item_was_expanded = self.IsExpanded(item)
if item != self.GetRootItem():
func(item)
if not self.IsExpanded(item):
return
for child in item.GetChildren():
self._collapse_node(child, func)
if not item_was_expanded:
self.Collapse(item)
def _hideanimation(self, item):
itemwindow = item.GetWindow()
if itemwindow:
itemwindow.Hide()
def _add_animated_gif(self, item, on_the_right):
if self._animctrl:
self._animctrl.Stop()
self._animctrl.Animation.Destroy()
self._animctrl.Destroy()
self._animctrl = None
from wx.adv import Animation, AnimationCtrl
img = os.path.join(bitmapDir, 'recording.gif')
ani = Animation(img)
obj = self
rect = (item.GetX()+20, item.GetY()+1) # Overlaps item icon
self._animctrl = AnimationCtrl(obj, -1, ani, rect)
self._animctrl.SetBackgroundColour(obj.GetBackgroundColour())
item.SetWindow(self._animctrl, on_the_right)
self._animctrl.Play()
def OnSelChanged(self, event):
@@ -1928,6 +2010,8 @@ class CustomTreeCtrl(CT.CustomTreeCtrl):
item = event.GetItem()
self.log.write("Item " + self.GetItemText(item) + " Has Been Checked!\n")
if item == self.anim_gif_item:
self._add_animated_gif(item, self.window_on_the_right)
event.Skip()

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

View File

@@ -3,8 +3,8 @@
# Inspired By And Heavily Based On wxGenericTreeCtrl.
#
# Andrea Gavana, @ 17 May 2006
# Latest Revision: 09 Jan 2014, 23.00 GMT
#
# Latest Revision: 02 Jul 2018, 00.10 GMT
# Modified by Helio Guilherme, @ 26 Apr 2018
#
# TODO List
#
@@ -299,14 +299,14 @@ License And Version
:class:`CustomTreeCtrl` is distributed under the wxPython license.
Latest Revision: Andrea Gavana @ 09 Jan 2014, 23.00 GMT
Latest Revision: Helio Guilherme @ 09 Aug 2018, 21.35 GMT
Version 2.6
Version 2.7
"""
# Version Info
__version__ = "2.6"
__version__ = "2.7"
import wx
from wx.lib.expando import ExpandoTextCtrl
@@ -1554,7 +1554,7 @@ class GenericTreeItem(object):
:class:`CustomTreeCtrl`. This is a generic implementation of :class:`TreeItem`.
"""
def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Default class constructor.
For internal use: do not call it in your code!
@@ -1581,6 +1581,8 @@ class GenericTreeItem(object):
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item;
:param bool `separator`: ``True`` if the item is a separator, ``False`` otherwise.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:note: Regarding radiobutton-type items (with `ct_type` = 2), the following
approach is used:
@@ -1663,9 +1665,10 @@ class GenericTreeItem(object):
self._enabled = False
self._wnd = wnd # are we holding a window?
self._windowontheright = on_the_right # on the right, or left?
if wnd:
self.SetWindow(wnd)
self.SetWindow(wnd, on_the_right)
def IsOk(self):
@@ -1909,13 +1912,16 @@ class GenericTreeItem(object):
self._width = w
def SetWindow(self, wnd):
def SetWindow(self, wnd, on_the_right=True):
"""
Sets the window associated to the item.
:param `wnd`: a non-toplevel window to be displayed next to the item, any
subclass of :class:`wx.Window`.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image. New in wxPython 4.0.4.
:raise: `Exception` if the input `item` is a separator and `wnd` is not ``None``.
"""
@@ -1923,6 +1929,7 @@ class GenericTreeItem(object):
raise Exception("Separator items can not have an associated window")
self._wnd = wnd
self._windowontheright = on_the_right
if wnd.GetSizer(): # the window is a complex one hold by a sizer
size = wnd.GetBestSize()
@@ -1935,6 +1942,11 @@ class GenericTreeItem(object):
# Do better strategies exist?
self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
# We also have to bind the wx.EVT_TREE_ITEM_COLLAPSING for the
# associated window, for example when it is an agw.animate.AnimationCtrl,
# otherwise it would stay visible. See the demo for an example.
self._wnd.Bind(wx.EVT_TREE_ITEM_COLLAPSING, self.OnTreeItemCollapsing)
self._height = size.GetHeight() + 2
self._width = size.GetWidth()
self._windowsize = size
@@ -1948,6 +1960,7 @@ class GenericTreeItem(object):
self._windowenabled = self._enabled
def GetWindow(self):
"""
Returns the window associated to the item (if any).
@@ -2023,6 +2036,27 @@ class GenericTreeItem(object):
event.Skip()
def OnTreeItemCollapsing(self, event):
"""
Handles the ``wx.EVT_TREE_ITEM_COLLAPSING`` event for the window associated with the item.
:param `event`: a :class:`GenericTreeItem` to be processed.
"""
# Helio: This is not OK?
# Should it be more "internal" code?
# It is working on the user application.
for item in event.GetItem().GetChildren():
itemwindow = item.GetWindow()
if itemwindow: # Hides the attached window if added
itemwindow.Hide()
# Hides the attached window if added
#if self._wnd:
# self._wnd.Hide()
event.Skip()
def GetType(self):
"""
Returns the item type.
@@ -4270,13 +4304,15 @@ class CustomTreeCtrl(wx.ScrolledWindow):
return item.GetWindow()
def SetItemWindow(self, item, wnd):
def SetItemWindow(self, item, wnd, on_the_right=True):
"""
Sets the window for the given item.
:param `item`: an instance of :class:`GenericTreeItem`;
:param `wnd`: if not ``None``, a non-toplevel window to be displayed next to
the item.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image. New in wxPython 4.0.4.
:raise: `Exception` if the input `item` is a separator and `wnd` is not ``None``.
"""
@@ -4293,7 +4329,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
else:
self.DeleteItemWindow(item)
item.SetWindow(wnd)
item.SetWindow(wnd, on_the_right)
self.CalculatePositions()
self.Refresh()
self.AdjustMyScrollbars()
@@ -4850,7 +4886,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
# operations
# -----------------------------------------------------------------------------
def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Actually inserts an item in the tree.
@@ -4869,6 +4905,8 @@ class CustomTreeCtrl(wx.ScrolledWindow):
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item;
:param bool `separator`: ``True`` if the item is a separator, ``False`` otherwise.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
@@ -4909,7 +4947,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
self._dirty = True # do this first so stuff below doesn't cause flicker
item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data, separator)
item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
if wnd is not None:
self._hasWindows = True
@@ -4920,7 +4958,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
return item
def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, on_the_right=True):
"""
Adds a root item to the :class:`CustomTreeCtrl`.
@@ -4935,6 +4973,8 @@ class CustomTreeCtrl(wx.ScrolledWindow):
use for the item in selected state; if `image` > -1 and `selImage` is -1, the
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
@@ -4967,7 +5007,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
self._dirty = True # do this first so stuff below doesn't cause flicker
self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data)
self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data, on_the_right)
if wnd is not None:
self._hasWindows = True
@@ -4989,7 +5029,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
return self._anchor
def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Prepends an item as a first child of parent.
@@ -5007,16 +5047,18 @@ class CustomTreeCtrl(wx.ScrolledWindow):
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item;
:param bool `separator`: ``True`` if the item is a separator, ``False`` otherwise.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
:see: :meth:`~CustomTreeCtrl.DoInsertItem` for possible exceptions generated by this method.
"""
return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data, separator)
return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Inserts an item after the given previous.
@@ -5036,6 +5078,8 @@ class CustomTreeCtrl(wx.ScrolledWindow):
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item;
:param bool `separator`: ``True`` if the item is a separator, ``False`` otherwise.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
@@ -5048,7 +5092,7 @@ class CustomTreeCtrl(wx.ScrolledWindow):
if not parent:
# should we give a warning here?
return self.AddRoot(text, ct_type, wnd, image, selImage, data)
return self.AddRoot(text, ct_type, wnd, image, selImage, data, on_the_right)
index = -1
if idPrevious:
@@ -5058,10 +5102,10 @@ class CustomTreeCtrl(wx.ScrolledWindow):
except:
raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data, separator)
return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
def InsertItemByIndex(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def InsertItemByIndex(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Inserts an item after the given previous.
@@ -5080,6 +5124,8 @@ class CustomTreeCtrl(wx.ScrolledWindow):
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item;
:param bool `separator`: ``True`` if the item is a separator, ``False`` otherwise.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
@@ -5092,10 +5138,10 @@ class CustomTreeCtrl(wx.ScrolledWindow):
# should we give a warning here?
return self.AddRoot(text, ct_type, wnd, image, selImage, data)
return self.DoInsertItem(parentId, idPrevious, text, ct_type, wnd, image, selImage, data, separator)
return self.DoInsertItem(parentId, idPrevious, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False):
def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, separator=False, on_the_right=True):
"""
Inserts an item after the given previous.
@@ -5108,12 +5154,12 @@ class CustomTreeCtrl(wx.ScrolledWindow):
"""
if type(input) == type(1):
return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data, separator)
return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
else:
return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data, separator)
return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data, separator, on_the_right)
def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None, on_the_right=True):
"""
Appends an item as a last child of its parent.
@@ -5130,6 +5176,8 @@ class CustomTreeCtrl(wx.ScrolledWindow):
use for the item in selected state; if `image` > -1 and `selImage` is -1, the
same image is used for both selected and unselected items;
:param object `data`: associate the given Python object `data` with the item.
:param bool `on_the_right`: ``True`` positions the window on the right of text, ``False``
on the left of text and overlapping the image.
:return: An instance of :class:`GenericTreeItem` upon successful insertion.
@@ -5140,9 +5188,9 @@ class CustomTreeCtrl(wx.ScrolledWindow):
if not parent:
# should we give a warning here?
return self.AddRoot(text, ct_type, wnd, image, selImage, data)
return self.AddRoot(text, ct_type, wnd, image, selImage, data, on_the_right)
return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data)
return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data, False, on_the_right)
def AppendSeparator(self, parentId):
@@ -6723,8 +6771,12 @@ class CustomTreeCtrl(wx.ScrolledWindow):
dc.DrawLabel(itemText, textrect)
wnd = item.GetWindow()
on_right = item._windowontheright # Helio: Should I make a getter?
if wnd:
wndx = wcheck + image_w + item.GetX() + text_w + 4
if on_right: # Helio: Original behaviour
wndx = wcheck + image_w + item.GetX() + text_w + 4
else:
wndx = wcheck + item.GetX()
xa, ya = self.CalcScrolledPosition((0, item.GetY()))
wndx += xa
if item.GetHeight() > item.GetWindowSize()[1]: