-1

wxPython I have the Frame with three Panels. Each Panel has wx.ComboBox There is no possibility to make one wx.ComboBox for several parents so I have three different combo-boxes on three panels with the same list for selection.

Is there a simple way to sync selection for these combo-boxes? E.g. if choose something in the first combo-box on the first panel it should be chosen in the second and third combo-boxes on the other hidden panels.

  • Welcome to StackOverflow! When asking a question on SO it is always best to include the code that you are referring to or if it is complicated a specific piece of code that is causing the problem. Ask yourself, if I was answering the question, what information would I need to be able to do so. – Rolf of Saxony Oct 09 '18 at 15:33

3 Answers3

0

I think the easiest method would be to use pubsub. You can have each panel instance subscribed to a listener and then when you change the combobox, it can send a message out to the listeners telling them to update.

I created a simple Notebook with three panels that demonstrates the concept:

import wx
from wx.lib.pubsub import pub 


class TabPanel(wx.Panel):

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        languages = ['Python', 'C++', 'PHP', 'Go']
        self.combo = wx.ComboBox(self, value='Python', choices=languages, 
                                 size=(80, -1))
        self.combo.Bind(wx.EVT_COMBOBOX, self.on_selection)

        pub.subscribe(self.update_selection, 'combo_update')

    def update_selection(self, selection):
        """
        pubsub listener
        """
        self.combo.SetSelection(selection)

    def on_selection(self, event):
        """
        combobox event handler
        """
        selection = self.combo.GetSelection()
        pub.sendMessage('combo_update', selection=selection)


class MyFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='Comboboxes!')
        panel = wx.Panel(self)
        notebook = wx.Notebook(panel)

        for page in range(1, 4):
            tab = TabPanel(notebook)
            notebook.AddPage(tab, 'Tab {}'.format(page))
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
        panel.SetSizer(sizer)

        self.Show()

if __name__ == '__main__':
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()
Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • In case of using pubsub I have import problem like this one: https://stackoverflow.com/questions/42140876/theres-invalid-syntax-in-pub-py – Anton Melentiev Oct 10 '18 at 06:06
  • Are you using a recent version of wxPython? This import should work with wxPython 2.9 and up, I believe. If you are using 2.8, then you are using an old version of pubsub – Mike Driscoll Oct 10 '18 at 15:19
  • Yes, it's because of an old version of Python. Thx. – Anton Melentiev Oct 11 '18 at 07:28
  • If you can't upgrade, then check out this article I wrote about the differences between the two versions of pubsub - https://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/ – Mike Driscoll Oct 12 '18 at 21:07
0

You can simply use GetSelection() in the event callback and set all of the comboboxes to the result obtained.
i.e.

import wx

class MyFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title="Test")
        self.MyList = ["Abc","Def","Ghi"]
        self.panel1 = wx.Panel(self,-1)
        self.panel2 = wx.Panel(self,-1)
        self.panel3 = wx.Panel(self,-1)
        self.Picker1 = wx.ComboBox(self.panel1, pos=(10,10), value="1", size=(100, -1), choices=self.MyList)
        self.text1 = wx.StaticText(self.panel1, -1, "1", pos=(120,10))
        self.Picker2 = wx.ComboBox(self.panel2, pos=(10,10), value="2", size=(100, -1), choices=self.MyList)
        self.text2 = wx.StaticText(self.panel2, -1, "2", pos=(120,10))
        self.Picker3 = wx.ComboBox(self.panel3, pos=(10,10), value="3", size=(100, -1), choices=self.MyList)
        self.text3 = wx.StaticText(self.panel3, -1, "3", pos=(120,10))
        self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox)
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.panel1)
        vbox.Add(self.panel2)
        vbox.Add(self.panel3)
        self.SetSizer(vbox)
        self.Show()
        self.panel1.SetFocus()
        self.panel2.Hide()
        self.panel3.Hide()

    def EvtComboBox(self,event):
        ev = event.GetSelection()
        self.Picker1.SetSelection(ev)
        self.Picker2.SetSelection(ev)
        self.Picker3.SetSelection(ev)
        self.panel2.Show()
        self.panel3.Show()
        self.Layout()

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    app.MainLoop()

I have added a simple piece of text beside each combobox for clarity and initially hidden panels 2 & 3 as you intimate that they are hidden, in your question. Note self.Layout() is required to display the unhidden panels.

Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
  • Thx, it works. I've also used the same way. But I dreamed of the built-in way where I don't need to loop through all combo-boxes. Anyway, thank you again for your response. – Anton Melentiev Oct 10 '18 at 06:14
  • You could have a callback for each control or use `event.GetId()` to identify the control that changed but I see no real point. It's more efficient to change all 3 rather than attempt to exclude the originating control. – Rolf of Saxony Oct 10 '18 at 06:33
0

I found the decision for me. It is possible to create the combo-box for the Frame and use it in each panel. I put an example here. Maybe it would be helpful for somebody.

import wx

########################################################################
class PanelOne(wx.Panel):

    def __init__(self, parent, selection):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)

        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
        self.label = wx.StaticText(self, label='Firs panel')

        self.sizer1.Add(selection, 1, wx.EXPAND)
        self.sizer1.Add(self.label, 1, wx.EXPAND)
        self.SetSizer(self.sizer1)


########################################################################
class PanelTwo(wx.Panel):

    def __init__(self, parent, selection):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)

        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
        self.labe2 = wx.StaticText(self, label='Second panel')

        self.sizer2.Add(selection, 1, wx.EXPAND)
        self.sizer2.Add(self.labe2, 1, wx.EXPAND)
        self.SetSizer(self.sizer2)


########################################################################
class MyForm(wx.Frame):

    # ----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          "Panel Switcher Tutorial")

        self.selection = wx.ComboBox(self, size=(200, -1), choices=['One', 'Two', 'Three'], style=wx.CB_DROPDOWN, value='select')

        self.panel_one = PanelOne(self, self.selection)
        self.panel_two = PanelTwo(self, self.selection)
        self.panel_two.Hide()

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.panel_one, 1, wx.EXPAND)
        self.sizer.Add(self.panel_two, 1, wx.EXPAND)
        self.SetSizer(self.sizer)

        # make menu
        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        switch_panels_menu_item = fileMenu.Append(wx.ID_ANY,
                                                  "Switch Panels",
                                                  "Some text")
        self.Bind(wx.EVT_MENU, self.onSwitchPanels,
                  switch_panels_menu_item)
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

    # ----------------------------------------------------------------------
    def onSwitchPanels(self, event):
        """"""
        if self.panel_one.IsShown():
            self.SetTitle("Panel Two Showing")
            self.panel_one.Hide()
            self.panel_two.Show()
        else:
            self.SetTitle("Panel One Showing")
            self.panel_one.Show()
            self.panel_two.Hide()
        self.Layout()


# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()