I have been experimenting with custom ComboCtrl
and ComboPopup
classes based on the example in the wxPython demo. I have noticed that when I press the left or right arrow keys the OnComboKeyEvent()
method is not triggered. However, it is triggered for all other keys including the left and right arrow keys on the numeric keypad.
Below is a stand-alone version of the example from wxPython demo. It has a print call added in OnComboKeyEvent()
to output the key codes. When I run it, I select an item from the drop-down list and then press the left and right arrow keys. I don’t get any output for these keys, but I do for any other keys.
Is this intentional or a bug?
I am running on Python 3.8.10 + wxPython 4.1.1 gtk3 (phoenix) wxWidgets 3.1.5 + Linux Mint 20.3. Does the same thing happen on other platforms?
import wx
#----------------------------------------------------------------------
# This class is used to provide an interface between a ComboCtrl and the
# ListCtrl that is used as the popup for the combo widget.
class ListCtrlComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt):
self.lc.InsertItem(self.lc.GetItemCount(), txt)
def OnMotion(self, evt):
item, flags = self.lc.HitTest(evt.GetPosition())
if item >= 0:
self.lc.Select(item)
self.curitem = item
def OnLeftDown(self, evt):
self.value = self.curitem
self.Dismiss()
# The following methods are those that are overridable from the
# ComboPopup base class. Most of them are not required, but all
# are shown here for demonstration purposes.
# This is called immediately after construction finishes. You can
# use self.GetCombo if needed to get to the ComboCtrl instance.
def Init(self):
self.value = -1
self.curitem = -1
# Create the popup child control. Return true for success.
def Create(self, parent):
self.lc = wx.ListCtrl(parent, style=wx.LC_LIST | wx.LC_SINGLE_SEL | wx.SIMPLE_BORDER)
self.lc.Bind(wx.EVT_MOTION, self.OnMotion)
self.lc.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
return True
# Return the widget that is to be used for the popup
def GetControl(self):
return self.lc
# Called just prior to displaying the popup, you can use it to
# 'select' the current item.
def SetStringValue(self, val):
idx = self.lc.FindItem(-1, val)
if idx != wx.NOT_FOUND:
self.lc.Select(idx)
# Return a string representation of the current item.
def GetStringValue(self):
if self.value >= 0:
return self.lc.GetItemText(self.value)
return ""
# Called immediately after the popup is shown
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
# Called when popup is dismissed
def OnDismiss(self):
wx.ComboPopup.OnDismiss(self)
# This is called to custom paint in the combo control itself
# (ie. not the popup). Default implementation draws value as
# string.
def PaintComboControl(self, dc, rect):
wx.ComboPopup.PaintComboControl(self, dc, rect)
# Receives key events from the parent ComboCtrl. Events not
# handled should be skipped, as usual.
def OnComboKeyEvent(self, event):
print("OnComboKeyEvent", event.GetKeyCode())
wx.ComboPopup.OnComboKeyEvent(self, event)
# Implement if you need to support special action when user
# double-clicks on the parent wxComboCtrl.
def OnComboDoubleClick(self):
wx.ComboPopup.OnComboDoubleClick(self)
# Return final size of popup. Called on every popup, just prior to OnPopup.
# minWidth = preferred minimum width for window
# prefHeight = preferred height. Only applies if > 0,
# maxHeight = max height for window, as limited by screen size
# and should only be rounded down, if necessary.
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.ComboPopup.GetAdjustedSize(self, minWidth, prefHeight, maxHeight)
# Return true if you want delay the call to Create until the popup
# is shown for the first time. It is more efficient, but note that
# it is often more convenient to have the control created
# immediately.
# Default returns false.
def LazyCreate(self):
return wx.ComboPopup.LazyCreate(self)
#----------------------------------------------------------------------
class MyFrame(wx.Frame):
def __init__(self, parent, id=wx.ID_ANY):
wx.Frame.__init__(self, parent, id)
main_sizer = wx.BoxSizer(wx.HORIZONTAL)
comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "", size=(200, -1))
main_sizer.Add(comboCtrl, 0, 0, 0)
popupCtrl = ListCtrlComboPopup()
# It is important to call SetPopupControl() as soon as possible
comboCtrl.SetPopupControl(popupCtrl)
self.SetSizer(main_sizer)
main_sizer.Fit(self)
self.Layout()
# Populate using wx.ListView methods
popupCtrl.AddItem("First Item")
popupCtrl.AddItem("Second Item")
popupCtrl.AddItem("Third Item")
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
# end of class MyApp
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()