如何在可编辑ComboBox获得外部焦点时清空文本,忽略内部焦点切换(QMenu/下拉框)
看起来你已经搭好了不错的基础框架,但在焦点判断的细节上可能还有遗漏——Qt里的焦点切换场景可比想象中要多一点。咱们来调整一下代码,精准实现你要的需求:
问题核心分析
你当前在CustomLineEdit的focusInEvent里只判断了event.reason() != QtCore.Qt.PopupFocusReason,但ComboBox的下拉列表、手动弹出的QMenu这类内部控件的焦点切换,有些场景并不完全属于PopupFocusReason,比如QMenu关闭时返回焦点的情况,就需要额外判断。
解决方案:改进焦点判断逻辑
我们需要在focusInEvent里同时检查两种内部焦点场景:
- 焦点来源是Qt定义的弹出窗口类型(对应
PopupFocusReason) - 当前存在属于父ComboBox的顶级弹出窗口(比如正在显示的QMenu)
修改后的CustomLineEdit类如下:
class CustomLineEdit(QtWidgets.QLineEdit): # This CustomClass is SPECIFICALLY made for CustomCombobox def __init__(self, parent=None): super().__init__(parent) def focusInEvent(self, event): super().focusInEvent(event) # 判断是否来自ComboBox内部的弹出控件(下拉框/QMenu) is_internal_focus = False # 场景1:焦点由弹出窗口返回(比如下拉框收起) if event.reason() == QtCore.Qt.PopupFocusReason: is_internal_focus = True # 场景2:当前有属于父ComboBox的顶级弹出窗口(比如QMenu正在显示) top_popup = QtWidgets.QApplication.activePopupWidget() if top_popup and top_popup.parent() == self.parent(): is_internal_focus = True # 只有外部焦点进入时才清空文本 if not is_internal_focus: self.clear() def paintEvent(self, event): # 保留你原有的paintEvent逻辑,无需修改 if self.hasFocus(): super().paintEvent(event) return top = QtWidgets.QApplication.activePopupWidget() if top is not None and top.parent() == self.parent(): super().paintEvent(event) return painter = QtGui.QPainter(self) #Basic Background opt = QtWidgets.QStyleOptionFrame() self.initStyleOption(opt) #getting parent for "what to draw" (primary/secondary) and font combo = self.parent() if isinstance(combo, QtWidgets.QComboBox): current_index = combo.currentIndex() model_index = combo.model().index(current_index, 0) display_text = model_index.data(QtCore.Qt.DisplayRole) user_text = model_index.data(QtCore.Qt.UserRole) primaryFont = combo.font() secondaryFont = combo.font() else: print('We should NEVER be here!!! Parent NOT QComboBox! Use only at your own risk!') display_text = self.text() user_text = "" primaryFont = self.font() secondaryFont = self.font() # Getting "to be painted"-Area and divide them into areas text_rect = self.style().subElementRect(QtWidgets.QStyle.SE_LineEditContents, opt, self) text_rect = text_rect.adjusted(2, 0, 0, 0) #custom padding to align with dropdown items primaryHeight = getfontheight_adjustfontsize(primaryFont) secondaryHeight = getfontheight_adjustfontsize(secondaryFont, SECONDARY_FONTSIZE_MODIFIER) totalTextHeight = primaryHeight + secondaryHeight y_offset = text_rect.top() + (text_rect.height() - totalTextHeight) // 2 primaryRect = QtCore.QRect(text_rect.left(), y_offset, text_rect.width(), primaryHeight) secondaryRect = QtCore.QRect(text_rect.left(), y_offset + primaryHeight, text_rect.width(), secondaryHeight) # Draw primary (top) painter.setFont(primaryFont) painter.setPen(self.palette().color(QtGui.QPalette.Text)) painter.drawText(primaryRect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(display_text)) # Draw secondary (bottom) painter.setFont(secondaryFont) painter.setPen(SECONDARY_FONT) painter.drawText(secondaryRect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(user_text))
验证场景
修改后可以覆盖你需要的所有情况:
- 从外部控件(比如你的表格)点击LineEdit:文本清空 ✅
- 展开/收起ComboBox下拉框:文本保留 ✅
- 按Enter弹出QMenu,关闭后回到LineEdit:文本保留 ✅
- 从QMenu选择选项后返回焦点:文本保留 ✅
备注:内容来源于stack exchange,提问作者Andreas M.
相关产品推荐
相关产品推荐

