|
| 1 | +import wx |
| 2 | + |
| 3 | + |
| 4 | +class CustomCollapsiblePanel(wx.Panel): |
| 5 | + def __init__(self, parent, title="Sample Title", expand=0, use_combo=False, combo_trigger_item=-1): |
| 6 | + wx.Panel.__init__(self, parent) |
| 7 | + self.__master_sizer = wx.BoxSizer(wx.VERTICAL) |
| 8 | + |
| 9 | + self.parent = parent # parent of this panel |
| 10 | + self.title = title |
| 11 | + self.is_expanded = expand # is_expanded status |
| 12 | + self.__using_combo = use_combo |
| 13 | + self.trigger_item = combo_trigger_item |
| 14 | + |
| 15 | + # this will be the main sizer for this panel |
| 16 | + self.vbox = wx.BoxSizer(wx.VERTICAL) |
| 17 | + |
| 18 | + # this sizer contains is_expanded button and title of frame |
| 19 | + self.hbox = wx.BoxSizer(wx.VERTICAL) |
| 20 | + |
| 21 | + # self.interactive_item is the item that is interacted with to is_expanded or collapse the panel |
| 22 | + if self.__using_combo: |
| 23 | + self.interactive_item = wx.ComboBox(self, style=wx.CB_READONLY, name="interactive_item") |
| 24 | + self.interactive_item.Bind(wx.EVT_COMBOBOX, self.on_interactive_item) |
| 25 | + else: |
| 26 | + self.interactive_item = wx.Button(self, label=title, size=(-1, 18), style=wx.BU_LEFT | wx.STATIC_BORDER, name='interactive_item') |
| 27 | + self.interactive_item.Bind(wx.EVT_BUTTON, self.on_interactive_item) |
| 28 | + |
| 29 | + # self.lbl = wx.StaticText(self, -1, size=(-1, 5), name='cplbl') |
| 30 | + |
| 31 | + self.hbox.Add(self.interactive_item, 1, wx.EXPAND) |
| 32 | + # self.hbox.Add(self.lbl, 0) |
| 33 | + |
| 34 | + # add to main sizer(vbox) |
| 35 | + self.vbox.Add(self.hbox, 0, wx.EXPAND) |
| 36 | + |
| 37 | + def on_interactive_item(self, event=None): |
| 38 | + """ |
| 39 | + Case for combo box: If the selected item matches the trigger item then expand, otherwise collapse |
| 40 | + Case for button: collapse if expanded and expand if collapsed |
| 41 | + :param event: |
| 42 | + :return: |
| 43 | + """ |
| 44 | + if isinstance(self.interactive_item, wx.ComboBox): |
| 45 | + if self.interactive_item.GetStringSelection() == self.trigger_item: |
| 46 | + self.expand_panel() |
| 47 | + else: |
| 48 | + self.collapse_panel() |
| 49 | + else: |
| 50 | + if self.is_expanded: |
| 51 | + self.collapse_panel() |
| 52 | + else: |
| 53 | + self.expand_panel() |
| 54 | + |
| 55 | + if event: |
| 56 | + event.Skip() |
| 57 | + |
| 58 | + def expand_panel(self): |
| 59 | + self.is_expanded = True |
| 60 | + self.__redraw_panel() |
| 61 | + self.GetTopLevelParent().SetSize((400, 300)) |
| 62 | + |
| 63 | + def __redraw_panel(self): |
| 64 | + for child in self.GetChildren(): |
| 65 | + if child.GetName() == "interactive_item": |
| 66 | + continue |
| 67 | + child.Show(self.is_expanded) |
| 68 | + self.parent.Layout() |
| 69 | + self.parent.SendSizeEvent() # make scrollbars visible if parent is scrolledWindow and if they are required automatically |
| 70 | + # self.lbl.SetFocus() # Remove focus from button when pressed |
| 71 | + self.parent.Refresh() |
| 72 | + |
| 73 | + def collapse_panel(self): |
| 74 | + self.is_expanded = False |
| 75 | + self.__redraw_panel() |
| 76 | + self.GetTopLevelParent().SetSize((400, 150)) |
| 77 | + |
| 78 | + def finish_layout(self): |
| 79 | + |
| 80 | + allSizers = [] |
| 81 | + childSizer = None |
| 82 | + # Get all the sizers containing all the children of this panel |
| 83 | + for child in self.GetChildren(): |
| 84 | + if child.GetName() == 'interactive_item' or child.GetName() == 'cplbl': |
| 85 | + continue |
| 86 | + |
| 87 | + childSizer = child.GetContainingSizer() |
| 88 | + if childSizer != None: |
| 89 | + # add the sizer in the list if it's no there. |
| 90 | + # can't use set as it changes the order of elements |
| 91 | + # this way we can have unique sizer or not repeating ones |
| 92 | + if not childSizer in allSizers: |
| 93 | + allSizers.append(childSizer) |
| 94 | + |
| 95 | + # Get root level sizers and add to main sizer name 'vbox' |
| 96 | + if len(allSizers): |
| 97 | + for sizer in self.getRootSizers(allSizers): |
| 98 | + self.vbox.Add(sizer, 0, wx.EXPAND) |
| 99 | + else: |
| 100 | + print 'children of this panel are not in any sizers. They should be in a sizer/s' |
| 101 | + |
| 102 | + # When deleting this panel in any case, masterSizer is also getting deleted. we have to create it again |
| 103 | + if not isinstance(self.__master_sizer, wx._core.BoxSizer): |
| 104 | + self.__master_sizer = wx.BoxSizer(wx.VERTICAL) |
| 105 | + |
| 106 | + self.__master_sizer.Add(self, 0, wx.EXPAND) |
| 107 | + |
| 108 | + # Rearrange everything |
| 109 | + self.SetSizer(self.vbox) |
| 110 | + self.Fit() |
| 111 | + self.on_interactive_item() |
| 112 | + |
| 113 | + def getRootSizers(self, sizerList): |
| 114 | + ''' |
| 115 | + 'sizerList' contains many sizers and may possible nested sizers or sizers added inside another sizers. |
| 116 | + This function process the list and returns only root level sizers. |
| 117 | + We'll add only root level sizers to main sizer of this class name 'vbox'. |
| 118 | + ''' |
| 119 | + finalList = sizerList[:] |
| 120 | + copyList = sizerList[:] |
| 121 | + |
| 122 | + for sizer in copyList: |
| 123 | + if len(sizer.GetChildren()): |
| 124 | + for child in sizer.GetChildren(): |
| 125 | + if child.GetClassName() == 'wxSizerItem': |
| 126 | + try: |
| 127 | + finalList.remove(child.GetSizer()) |
| 128 | + except: |
| 129 | + pass |
| 130 | + return finalList |
0 commit comments