You need to enable JavaScript to run this app. wx.ComboCtrl绑定wx.ListCtrl自定义下拉的显示异常问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

wx.ComboCtrl绑定wx.ListCtrl自定义下拉的显示异常问题咨询

wx.ComboCtrl绑定wx.ListCtrl下拉弹窗解决方案

问题说明

开发绑定wx.ListCtrl作为下拉弹窗的wx.ComboCtrl组件,需要为下拉选项设置前景色标识条目状态,要求下拉展开和用户选中后均能正常显示对应颜色。当前存在三类问题:

  • 显示异常:Linux(Ubuntu 20.04)环境下选中选项后,wx.ComboCtrl背景始终保持蓝色、前景色为白色,即便将焦点切换到其他组件也无法恢复,只有切换到其他窗口再切回当前窗口,才会显示默认灰色背景和预设的前景色;Windows环境下选中后背景为正常灰色,但选中项周围会出现虚线框。
  • 布局适配异常:下拉弹出的wx.ListCtrl仅含单列,列宽过窄导致部分选项显示不全,需要实现列宽始终与组件宽度一致,且可跟随父窗口缩放自适应调整。
  • 样式优化需求:消除Windows环境下选中项周围的虚线提示框。

修复方案

显示异常修复

  1. 为ListCtrl添加wx.LC_HIDE_FOCUS_RECT样式,直接隐藏Windows平台下选中项的焦点虚线框
  2. 选中选项关闭弹窗时,主动调用ComboCtrl的Refresh()方法强制重绘,解决Linux平台下选中后状态不更新的问题
  3. 存储每个选项的前景色,选中时同步设置到ComboCtrl上,保证输入框内文字颜色和下拉列表对应项颜色一致

布局适配修复

  1. 绑定ListCtrl的尺寸变化事件,每次控件大小调整时自动将列宽设置为ListCtrl的客户端宽度,适配父窗口缩放
  2. 重写下拉框尺寸调整方法,弹出下拉框时将列宽设置为下拉框的可用宽度,避免选项被截断

修复后完整代码

#!/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 22:24:05