/pyface/ui/wx/expandable_panel.py

https://github.com/enthought/pyface · Python · 168 lines · 114 code · 26 blank · 28 comment · 1 complexity · 664b03a7dff64a5750cdaafb2ccd749e MD5 · raw file

  1. # (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
  2. # All rights reserved.
  3. #
  4. # This software is provided without warranty under the terms of the BSD
  5. # license included in LICENSE.txt and may be redistributed only under
  6. # the conditions described in the aforementioned license. The license
  7. # is also available online at http://www.enthought.com/licenses/BSD.txt
  8. #
  9. # Thanks for using Enthought open source!
  10. """ A Layered panel. """
  11. import warnings
  12. import wx
  13. from traits.api import Dict, Str
  14. from pyface.ui_traits import Image
  15. from .expandable_header import ExpandableHeader
  16. from .image_resource import ImageResource
  17. from .layout_widget import LayoutWidget
  18. class ExpandablePanel(LayoutWidget):
  19. """ An expandable panel. """
  20. # The default style.
  21. STYLE = wx.CLIP_CHILDREN
  22. collapsed_image = Image(ImageResource("mycarat1"))
  23. expanded_image = Image(ImageResource("mycarat2"))
  24. _layers = Dict(Str)
  25. _headers = Dict(Str)
  26. # ------------------------------------------------------------------------
  27. # 'object' interface.
  28. # ------------------------------------------------------------------------
  29. def __init__(self, parent=None, **traits):
  30. """ Creates a new LayeredPanel. """
  31. create = traits.pop("create", True)
  32. # Base class constructor.
  33. super().__init__(parent=parent, **traits)
  34. # Create the toolkit-specific control that represents the widget.
  35. if create:
  36. self.create()
  37. warnings.warn(
  38. "automatic widget creation is deprecated and will be removed "
  39. "in a future Pyface version, use create=False and explicitly "
  40. "call create() for future behaviour",
  41. PendingDeprecationWarning,
  42. )
  43. # ------------------------------------------------------------------------
  44. # 'Expandale' interface.
  45. # ------------------------------------------------------------------------
  46. def add_panel(self, name, layer):
  47. """ Adds a layer with the specified name.
  48. All layers are hidden when they are added. Use 'show_layer' to make a
  49. layer visible.
  50. """
  51. parent = self.control
  52. sizer = self.control.GetSizer()
  53. # Add the heading text.
  54. header = self._create_header(parent, text=name)
  55. sizer.Add(header, 0, wx.EXPAND)
  56. # Add the layer to our sizer.
  57. sizer.Add(layer, 1, wx.EXPAND)
  58. # All layers are hidden when they are added. Use 'show_layer' to make
  59. # a layer visible.
  60. sizer.Show(layer, False)
  61. # fixme: Should we warn if a layer is being overridden?
  62. self._layers[name] = layer
  63. return layer
  64. def remove_panel(self, name):
  65. """ Removes a layer and its header from the container."""
  66. if name not in self._layers:
  67. return
  68. sizer = self.control.GetSizer()
  69. panel = self._layers[name]
  70. header = self._headers[name]
  71. # sizer.Remove(panel)
  72. panel.Destroy()
  73. # sizer.Remove(header)
  74. header.Destroy()
  75. sizer.Layout()
  76. # ------------------------------------------------------------------------
  77. # Private interface.
  78. # ------------------------------------------------------------------------
  79. def _create_control(self, parent):
  80. """ Create the toolkit-specific control that represents the widget. """
  81. panel = wx.Panel(parent, -1, style=self.STYLE)
  82. sizer = wx.BoxSizer(wx.VERTICAL)
  83. panel.SetSizer(sizer)
  84. panel.SetAutoLayout(True)
  85. return panel
  86. def _create_header(self, parent, text):
  87. """ Creates a panel header. """
  88. sizer = wx.BoxSizer(wx.HORIZONTAL)
  89. panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN)
  90. panel.SetSizer(sizer)
  91. panel.SetAutoLayout(True)
  92. # Add the panel header.
  93. heading = ExpandableHeader(panel, title=text, create=False)
  94. heading.create()
  95. sizer.Add(heading.control, 1, wx.EXPAND)
  96. # connect observers
  97. heading.observe(self._on_button, "panel_expanded")
  98. heading.observe(self._on_panel_closed, "panel_closed")
  99. # Resize the panel to match the sizer's minimum size.
  100. sizer.Fit(panel)
  101. # hang onto it for when we destroy
  102. self._headers[text] = panel
  103. return panel
  104. # event handlers ----------------------------------------------------
  105. def _on_button(self, event):
  106. """ called when one of the expand/contract buttons is pressed. """
  107. header = event.new
  108. name = header.title
  109. visible = header.state
  110. sizer = self.control.GetSizer()
  111. sizer.Show(self._layers[name], visible)
  112. sizer.Layout()
  113. # fixme: Errrr, maybe we can NOT do this!
  114. w, h = self.control.GetSize().Get()
  115. self.control.SetSize((w + 1, h + 1))
  116. self.control.SetSize((w, h))
  117. def _on_panel_closed(self, event):
  118. """ Called when the close button is clicked in a header. """
  119. header = event.new
  120. name = header.title
  121. self.remove_panel(name)