wx.ComboCtrl绑定wx.ListCtrl自定义下拉的显示异常问题咨询
wx.ComboCtrl绑定wx.ListCtrl下拉弹窗解决方案
问题说明
开发绑定wx.ListCtrl作为下拉弹窗的wx.ComboCtrl组件,需要为下拉选项设置前景色标识条目状态,要求下拉展开和用户选中后均能正常显示对应颜色。当前存在三类问题:
- 显示异常:Linux(Ubuntu 20.04)环境下选中选项后,wx.ComboCtrl背景始终保持蓝色、前景色为白色,即便将焦点切换到其他组件也无法恢复,只有切换到其他窗口再切回当前窗口,才会显示默认灰色背景和预设的前景色;Windows环境下选中后背景为正常灰色,但选中项周围会出现虚线框。
- 布局适配异常:下拉弹出的wx.ListCtrl仅含单列,列宽过窄导致部分选项显示不全,需要实现列宽始终与组件宽度一致,且可跟随父窗口缩放自适应调整。
- 样式优化需求:消除Windows环境下选中项周围的虚线提示框。
修复方案
显示异常修复
- 为ListCtrl添加
wx.LC_HIDE_FOCUS_RECT样式,直接隐藏Windows平台下选中项的焦点虚线框 - 选中选项关闭弹窗时,主动调用ComboCtrl的
Refresh()方法强制重绘,解决Linux平台下选中后状态不更新的问题 - 存储每个选项的前景色,选中时同步设置到ComboCtrl上,保证输入框内文字颜色和下拉列表对应项颜色一致
布局适配修复
- 绑定ListCtrl的尺寸变化事件,每次控件大小调整时自动将列宽设置为ListCtrl的客户端宽度,适配父窗口缩放
- 重写下拉框尺寸调整方法,弹出下拉框时将列宽设置为下拉框的可用宽度,避免选项被截断
修复后完整代码
#!/usr/bin/env python import wx import os #---------------------------------------------------------------------- # This class is used to provide an interface between a ComboCtrl and the # ListCtrl that is used as the popoup for the combo widget. class ListCtrlComboPopup(wx.ComboPopup): def __init__(self): wx.ComboPopup.__init__(self) self.lc = None self.item_colours = [] # 存储每个选项的前景色 def AddItem(self, txt, _colour): idx = self.lc.GetItemCount() self.lc.InsertItem(idx, txt) _entry = self.lc.GetItem(idx) _entry.SetTextColour(wx.Colour(*_colour)) self.lc.SetItem(_entry) self.item_colours.append(_colour) # 保存颜色 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() def OnSize(self, evt): # 尺寸变化时自动调整列宽 if self.lc: self.lc.SetColumnWidth(0, self.lc.GetClientSize().width) evt.Skip() # 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): # 新增wx.LC_HIDE_FOCUS_RECT样式隐藏焦点虚线框 self.lc = wx.ListCtrl(parent, style=wx.LC_SINGLE_SEL | wx.SIMPLE_BORDER | wx.LC_REPORT | wx.LC_NO_HEADER | wx.LC_HIDE_FOCUS_RECT) self.lc.InsertColumn(0, '') self.lc.Bind(wx.EVT_MOTION, self.OnMotion) self.lc.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.lc.Bind(wx.EVT_SIZE, self.OnSize) # 绑定尺寸变化事件 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): # 选中后同步颜色到ComboCtrl并强制刷新 if self.value >= 0: combo = self.GetCombo() combo.SetForegroundColour(wx.Colour(*self.item_colours[self.value])) combo.Refresh() # 强制重绘解决Linux下状态不更新问题 print(self.GetStringValue()) 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): wx.ComboPopup.OnComboKeyEvent(self, event) # Implement if you need to support special action when user # double-clicks on the parent wx.ComboCtrl. 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): size = wx.ComboPopup.GetAdjustedSize(self, minWidth, prefHeight, maxHeight) # 弹出时设置列宽为下拉框可用宽度,减去滚动条宽度避免横向滚动 scroll_width = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) if self.lc.GetItemCount() * self.lc.GetItemHeight() > maxHeight else 0 self.lc.SetColumnWidth(0, minWidth - scroll_width) return size # 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 MyTestPanel(wx.Panel): def __init__(self, parent, log): self.log = log wx.Panel.__init__(self, parent, -1) txt = wx.TextCtrl(self, wx.ID_ANY, pos=(100,100)) comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "Third item", (10,10), size=(200,-1), style=wx.CB_READONLY) popupCtrl = ListCtrlComboPopup() # It is important to call SetPopupControl() as soon as possible comboCtrl.SetPopupControl(popupCtrl) # Populate using wx.ListView methods popupCtrl.AddItem("First Item", [255, 127, 0]) popupCtrl.AddItem("Second Item", [192, 127, 45]) popupCtrl.AddItem("Third Item", [25, 223, 172]) # 初始设置默认选中项的颜色 comboCtrl.SetForegroundColour(wx.Colour(25, 223, 172)) #---------------------------------------------------------------------- def runTest(frame, nb, log): win = MyTestPanel(nb, log) return win #---------------------------------------------------------------------- overview = """<html><body> <h2><center>wx.combo.ComboCtrl</center></h2> A combo control is a generic combobox that allows a totally custom popup. In addition it has other customization features. For instance, position and size of the dropdown button can be changed. </body></html> """ if __name__ == '__main__': import sys,os import run run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
内容的提问来源于stack exchange,提问作者Marc
相关产品推荐
相关产品推荐

