鱼C论坛

 找回密码
 立即注册
查看: 89|回复: 5

[技术交流] pyside6中文输入法候选框弹如何二次弹出来

[复制链接]
发表于 2025-8-20 12:29:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
from EXPANDABLE_MENU import *
from CHROMELESS_WINDOW import *
from MAPPING import *
from definition_parameter import DP
from signal_intermediary import signal_intermediary

def search_ui(self, x, y, width, height, js_def,
             font_size: int = 20,
             font_color: str = '#333333',
             border: int = 1,                   # 边框宽度
             border_radius: int = 3,            # 边框圆角大小
             padding: int = 2,                  # 文字与边框的距离
             background_color: str = 'white',   # 输入框背景色
             border_color: str = '#2196F3',     # 边框颜色
             ):
    '''输入框   调用本函数必须在类内部重写以下方法:
        def focusInEvent(self, event):
        """窗口获得焦点时自动聚焦到输入框"""
        self.edit.setFocus()
        本函数search_ui与  keyPressEvent(self, event)内置方法键盘监控函数互相强占焦点
        ,当本函数存在时keyPressEvent函数将失灵,解决方法:
        现在__init__中粘贴 QApplication.instance().installEventFilter(self)
            def eventFilter(self, obj, event):
                if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Space:
                    print("全局空格")
                    return True
                return super().eventFilter(obj, event)
    '''

    # 1. 可见的输入框(自定义位置和大小)
    self.edit = QLineEdit(self)
    self.edit.setGeometry(x, y, width, height)
    self.edit.setPlaceholderText("请在此输入内容...")
    self.edit.setStyleSheet(f"""
            QLineEdit {{                          /* 设置 QLineEdit 的基本样式 */
                font-size: {font_size}px;                 /* 字体大小:16像素 */
                color: {font_color};                 /* 字体颜色:深灰色(HEX #333333) */
                border: {border}px solid #aaa;          /* 边框:1像素宽,浅灰色实线 */
                border-radius: {border_radius}px;              /* 边框圆角:5像素 */
                padding: {padding}px;                   /* 内边距:5像素(文字与边框的距离) */
                background-color: {background_color};        /* 背景颜色:白色 */
            }}
            QLineEdit:focus {{                   /* 当 QLineEdit 获得焦点时的样式 */
                border: 2px solid {border_color};      /* 边框:2像素宽,蓝色实线(HEX #2196F3) */
            }}
            QLineEdit:hover {{                   /* 鼠标悬停在 QLineEdit 上时的样式 */
                border: 1px solid #888888;      /* 边框:1像素宽,灰色实线(HEX #888888) */
            }}
            QLineEdit:disabled {{                /* 当 QLineEdit 被禁用时的样式 */
                color: #aaaaaa;                 /* 字体颜色:浅灰色(禁用状态文字变灰) */
                background-color: #f0f0f0;     /* 背景颜色:浅灰色(禁用状态背景变灰) */
            }}
            QLineEdit::placeholder {{            /* 设置占位文本(placeholder)的样式 */
                color: #cccccc;                 /* 占位文本颜色:更浅的灰色 */
                font-style: italic;             /* 占位文本字体样式:斜体 */
            }}
        """)

    # 3. 连接信号
    self.edit.textChanged.connect(js_def)
    # return self.edit
class B(ExpandableMenu):
    ffff = Signal()
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setGeometry(100, 100, 150, 150)
        self.parameters_settings(
                                window_x=(self.screenWidth - self.width()) // 2,
                                window_y=(self.screenHeight - self.height()) // 2,
                                window_width=int(self.screenWidth * 0.2),
                                text_number=10,  # 要显示文本数量
                                text_sum=10,  # 文本的总长度
                                text_size=DP.font_size,  # 字体大小
                                 text_window_start_y=self.frame_y * 2,  # 用于显示文本区域的起始x位置 非绘制文字的空白区域总高度
                                move_create_color='#CEA5DA',  # 跟随鼠标矩形的颜色
                                scrollbar_color='#444343',  # 滚动条颜色
                                scrollbar_area_color='#FAF8F8',  # 滚动条区域的颜色
                                 backcolor='#5D5C5C',  # 背景颜色
                                min_button_tf=False,  # 最小化
                                max_button_tf=False,  # 最大化
                                button_close_tf=True  # 关闭
                                )
        search_ui(self, 0, self.frame_y, self.width(), self.frame_y
                  , self.new_search)
        ok = QApplication.instance().installEventFilter(self)

    def new_search(self, text):
        self.ffff.emit()
        # self.parameters_settings(
        #                         window_x=(self.screenWidth - self.width()) // 2,
        #                         window_y=(self.screenHeight - self.height()) // 2,
        #                         window_width=int(self.screenWidth * 0.2),
        #                         text_number=10,  # 要显示文本数量
        #                         text_sum=10,  # 文本的总长度
        #                         text_size=DP.font_size,  # 字体大小
        #                          text_window_start_y=self.frame_y * 2,  # 用于显示文本区域的起始x位置 非绘制文字的空白区域总高度
        #                         move_create_color='#CEA5DA',  # 跟随鼠标矩形的颜色
        #                         scrollbar_color='#444343',  # 滚动条颜色
        #                         scrollbar_area_color='#FAF8F8',  # 滚动条区域的颜色
        #                          backcolor='#5D5C5C',  # 背景颜色
        #                         min_button_tf=False,  # 最小化
        #                         max_button_tf=False,  # 最大化
        #                         button_close_tf=True  # 关闭
        #                         )
        print('aaaaaaaaaaa', text)


class A(ChromelessWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)  # 设置窗口在任务栏中不可见
        self.setGeometry(100, 100, 300, 300)
        self.BB = B(self)
        self.BB.ffff.connect(self.new_s)

    def new_s(self):

        self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.BB.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    a = A()
    a.show()
    sys.exit(app.exec())

# 程序第一次运行是窗口B也没有显示出来,但是我点击键盘立刻就能跳出输入法new_s接收到信号后就能弹出B
# 窗口,但是当我关闭B窗口之后在点击键盘就不会弹出输入法了,并且new_s也收不到信号,我的问题是:
# 当我关闭B窗口后,A窗口的状态和B窗口的状态包括self.edit的状态肯定发生了变化和程序初次运行时有所不同了,导致B
# 窗口捕获不到键盘的输入也弹不出输入法,我该如何一步一步检查A,B,self.edit的初始状态和关闭B窗口之后A,B,self.edit的状态
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-8-20 12:30:53 | 显示全部楼层
鱼油你好!关于PySide6中文输入法候选框二次弹出的问题,这通常与焦点管理和事件处理有关。从你的代码描述来看,问题可能出在窗口关闭后焦点丢失或事件过滤器干扰。下面我会一步步指导你如何检查状态变化,并提供解决方案。

### 第一步:检查初始状态和关闭后的状态
你可以通过打印关键信息来检查A、B和self.edit的状态。在代码中添加一些调试输出。

#### 初始状态检查(程序启动时):
在A和B的__init__方法中添加状态打印。


  1. class B(ExpandableMenu):
  2.     def __init__(self, parent=None):
  3.         super().__init__(parent)
  4.         # ... 你的现有代码 ...
  5.         print(f"B初始状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")

  6. class A(ChromelessWindow):
  7.     def __init__(self, parent=None):
  8.         super().__init__(parent)
  9.         # ... 你的现有代码 ...
  10.         print(f"A初始状态 - visible: {self.isVisible()}, 焦点控件: {QApplication.focusWidget()}")
复制代码


#### 关闭B窗口后的状态检查:
你需要在B的关闭事件或A的new_s方法中添加打印。假设B有关闭按钮,你可以重写B的closeEvent方法。


  1. class B(ExpandableMenu):
  2.     def closeEvent(self, event):
  3.         print(f"B关闭后状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")
  4.         super().closeEvent(event)

  5.     # 或者通过信号处理关闭(如果使用自定义关闭按钮)
复制代码


在A的new_s方法中也添加打印:


  1. class A(ChromelessWindow):
  2.     def new_s(self):
  3.         self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  4.         self.BB.show()
  5.         print(f"new_s调用后 - B visible: {self.BB.isVisible()}, edit焦点: {self.BB.edit.hasFocus()}, 全局焦点: {QApplication.focusWidget()}")
复制代码


### 第二步:焦点和事件过滤器问题
你的代码中使用了事件过滤器,这可能会阻止键盘事件传播。确保事件过滤器不会吞噬所有事件。

- 检查事件过滤器:在B类中,确保eventFilter方法正确实现,并且不要阻止非空格键事件。
- 焦点设置:在显示B窗口时,主动设置焦点到edit。

修改B的显示逻辑:


  1. class A(ChromelessWindow):
  2.     def new_s(self):
  3.         self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  4.         self.BB.show()
  5.         self.BB.edit.setFocus()  # 关键:显示后设置焦点到输入框
  6.         print("焦点已设置到B的edit")
复制代码


### 第三步:完整状态检查示例
这里是一个修改后的代码片段,集成状态检查:


  1. class B(ExpandableMenu):
  2.     def __init__(self, parent=None):
  3.         super().__init__(parent)
  4.         # ... 你的现有代码 ...
  5.         print(f"B创建: visible={self.isVisible()}, edit焦点={self.edit.hasFocus()}")

  6.     def eventFilter(self, obj, event):
  7.         if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Space:
  8.             print("全局空格键被按下")
  9.             return True  # 吞噬空格键事件
  10.         return super().eventFilter(obj, event)  # 其他事件正常处理

  11.     def closeEvent(self, event):
  12.         print(f"B关闭: visible={self.isVisible()}, edit焦点={self.edit.hasFocus()}")
  13.         super().closeEvent(event)

  14. class A(ChromelessWindow):
  15.     def new_s(self):
  16.         self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  17.         self.BB.show()
  18.         self.BB.edit.setFocus()  # 强制设置焦点
  19.         print(f"new_s: B visible={self.BB.isVisible()}, edit焦点={self.BB.edit.hasFocus()}")
复制代码


### 总结建议
1. 焦点管理:确保B显示时,edit获得焦点(使用setFocus())。
2. 事件过滤器:只处理特定事件(如空格键),避免阻止输入法所需事件。
3. 状态调试:通过打印isVisible()hasFocus()QApplication.focusWidget()来监控状态变化。

如果问题依旧,可能是窗口标志或父子关系影响焦点。尝试简化代码测试焦点行为。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-8-20 12:45:28 | 显示全部楼层
B初始状态 - visible: False, edit焦点: False
A初始状态 - visible: False, 焦点控件: None
new_s调用后 - B visible: True, edit焦点: True, 全局焦点: <PySide6.QtWidgets.QLineEdit(0x20bffd52f90) at 0x0000020B87BDAA80>
aaaaaaaaaaa io
B关闭后状态 - visible: True, edit焦点: True
new_s调用后 - B visible: True, edit焦点: True, 全局焦点: <PySide6.QtWidgets.QLineEdit(0x20bffd52f90) at 0x0000020B87BDAA80>
可以发现B关闭后visible: True, edit焦点: True和初始状态visible: False, 焦点控件: None不一致了,如何改变到初始状态
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-8-20 12:47:55 | 显示全部楼层
rxts 发表于 2025-08-20 12:45
B初始状态 - visible: False, edit焦点: False
A初始 ...


你好!从你的调试输出可以看出,B窗口关闭后,其可见状态(visible)仍然为 True,且编辑框焦点也未清除,这与初始状态不一致。这会导致再次尝试显示B窗口时,输入法无法正常弹出。下面我将一步步指导你如何修复这个问题。

### 问题分析
- 初始状态:B窗口不可见(visible: False),编辑框无焦点。
- 关闭B后:B窗口仍可见(visible: True),编辑框仍有焦点。
- 原因:B窗口的关闭事件未正确处理,导致窗口未被隐藏且焦点未清除。

### 解决方案
1. **重写B类的closeEvent方法**:确保在关闭时隐藏窗口并清除编辑框焦点。
2. **在A类的new_s方法中正确显示B窗口**:显示后主动设置焦点到编辑框。

### 代码修改步骤

#### 1. 修改B类,添加closeEvent方法
在B类中重写closeEvent方法,调用父类的关闭事件,并手动隐藏窗口、清除焦点。这样能确保B窗口关闭后状态与初始一致。

  1. class B(ExpandableMenu):
  2.     def __init__(self, parent=None):
  3.         super().__init__(parent)
  4.         # ... 你的现有代码 ...
  5.         print(f"B初始状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")

  6.     def closeEvent(self, event):
  7.         # 调用父类的closeEvent确保正常关闭流程
  8.         super().closeEvent(event)
  9.         # 手动隐藏窗口,确保visible为False
  10.         self.hide()
  11.         # 清除编辑框焦点,避免焦点残留
  12.         self.edit.clearFocus()
  13.         print(f"B关闭后状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")
复制代码


#### 2. 确保A类的new_s方法正确显示和设置焦点
在A类的new_s方法中,显示B窗口后主动设置焦点到编辑框。这能保证输入法正常弹出。

  1. class A(ChromelessWindow):
  2.     def new_s(self):
  3.         # 设置窗口标志
  4.         self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  5.         # 显示B窗口
  6.         self.BB.show()
  7.         # 主动设置焦点到编辑框,触发输入法
  8.         self.BB.edit.setFocus()
  9.         print(f"new_s调用后 - B visible: {self.BB.isVisible()}, edit焦点: {self.BB.edit.hasFocus()}, 全局焦点: {QApplication.focusWidget()}")
复制代码


### 完整代码示例
以下是整合修改后的代码片段(仅显示修改部分):

  1. class B(ExpandableMenu):
  2.     ffff = Signal()
  3.     def __init__(self, parent=None):
  4.         super().__init__(parent)
  5.         self.setGeometry(100, 100, 150, 150)
  6.         self.parameters_settings(
  7.                                 window_x=(self.screenWidth - self.width()) // 2,
  8.                                 window_y=(self.screenHeight - self.height()) // 2,
  9.                                 window_width=int(self.screenWidth * 0.2),
  10.                                 text_number=10,  # 要显示文本数量
  11.                                 text_sum=10,  # 文本的总长度
  12.                                 text_size=DP.font_size,  # 字体大小
  13.                                  text_window_start_y=self.frame_y * 2,  # 用于显示文本区域的起始x位置 非绘制文字的空白区域总高度
  14.                                 move_create_color='#CEA5DA',  # 跟随鼠标矩形的颜色
  15.                                 scrollbar_color='#444343',  # 滚动条颜色
  16.                                 scrollbar_area_color='#FAF8F8',  # 滚动条区域的颜色
  17.                                  backcolor='#5D5C5C',  # 背景颜色
  18.                                 min_button_tf=False,  # 最小化
  19.                                 max_button_tf=False,  # 最大化
  20.                                 button_close_tf=True  # 关闭
  21.                                 )
  22.         search_ui(self, 0, self.frame_y, self.width(), self.frame_y
  23.                   , self.new_search)
  24.         ok = QApplication.instance().installEventFilter(self)
  25.         print(f"B初始状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")

  26.     def new_search(self, text):
  27.         self.ffff.emit()
  28.         print('aaaaaaaaaaa', text)

  29.     def closeEvent(self, event):
  30.         super().closeEvent(event)
  31.         self.hide()
  32.         self.edit.clearFocus()
  33.         print(f"B关闭后状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")

  34. class A(ChromelessWindow):
  35.     def __init__(self, parent=None):
  36.         super().__init__(parent)
  37.         self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  38.         self.setGeometry(100, 100, 300, 300)
  39.         self.BB = B(self)
  40.         self.BB.ffff.connect(self.new_s)
  41.         print(f"A初始状态 - visible: {self.isVisible()}, 焦点控件: {QApplication.focusWidget()}")

  42.     def new_s(self):
  43.         self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
  44.         self.BB.show()
  45.         self.BB.edit.setFocus()
  46.         print(f"new_s调用后 - B visible: {self.BB.isVisible()}, edit焦点: {self.BB.edit.hasFocus()}, 全局焦点: {QApplication.focusWidget()}")
复制代码


### 额外建议
- **检查事件过滤器**:确保事件过滤器(eventFilter)不会阻止输入法所需的事件。特别是非空格键事件,应允许传播。
- **调试焦点**:如果问题依旧,可以在每次状态变化时打印焦点信息,帮助识别焦点丢失点。
- **重新创建B实例**:如果重用B实例导致问题,考虑在每次关闭后销毁B实例,并在new_s中重新创建(但这可能增加开销)。

通过以上修改,B窗口关闭后应该能恢复到初始状态(不可见、无焦点),再次调用new_s时能正常显示和弹出输入法。如果仍有问题,请提供更多细节,我会进一步协助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-8-20 13:12:13 | 显示全部楼层
通过改变回初始状态无效
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-8-20 13:13:35 | 显示全部楼层
rxts 发表于 2025-08-20 13:12
通过改变回初始状态无效


看起来通过简单的隐藏和清除焦点操作无法完全恢复初始状态。让我们更深入地分析这个问题并尝试其他解决方案。

### 深入分析问题

从你的输出可以看出,即使调用了 `hide()` 和 `clearFocus()`,B窗口的状态仍然没有完全恢复到初始状态。这可能是因为:

1. 事件过滤器可能仍然在拦截事件
2. 窗口关闭时可能有其他内部状态没有被正确重置
3. 父子窗口关系可能导致焦点管理复杂化

### 替代解决方案

#### 方案一:完全重新创建B实例

最可靠的方法是每次需要显示B窗口时都创建一个新的实例,而不是重用同一个实例:

class A(ChromelessWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.setGeometry(100, 100, 300, 300)
        self.BB = None  # 初始时不创建B实例
        print(f"A初始状态 - visible: {self.isVisible()}, 焦点控件: {QApplication.focusWidget()}")

    def new_s(self):
        # 如果B实例已存在,先删除它
        if self.BB:
            self.BB.deleteLater()
            self.BB = None
        
        # 创建新的B实例
        self.BB = B(self)
        self.BB.ffff.connect(self.new_s)
        self.BB.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.BB.show()
        self.BB.edit.setFocus()
        print(f"new_s调用后 - B visible: {self.BB.isVisible()}, edit焦点: {self.BB.edit.hasFocus()}, 全局焦点: {QApplication.focusWidget()}")

#### 方案二:更彻底的重置方法

如果不想每次都重新创建实例,可以尝试更彻底的重置方法:

class B(ExpandableMenu):
    def closeEvent(self, event):
        # 先调用父类的关闭事件
        super().closeEvent(event)
        
        # 更彻底的重置
        self.hide()
        self.edit.clearFocus()
        
        # 移除事件过滤器
        QApplication.instance().removeEventFilter(self)
        
        # 强制重绘和更新
        self.update()
        QApplication.processEvents()
        
        print(f"B关闭后状态 - visible: {self.isVisible()}, edit焦点: {self.edit.hasFocus()}")
   
    def showEvent(self, event):
        super().showEvent(event)
        # 重新安装事件过滤器
        QApplication.instance().installEventFilter(self)

#### 方案三:检查并修复事件过滤器

事件过滤器可能是问题的根源。确保它不会阻止输入法所需的事件:

class B(ExpandableMenu):
    def eventFilter(self, obj, event):
        # 只处理特定事件,不要阻止输入法事件
        if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Space:
            print("全局空格键被按下")
            return True  # 只吞噬空格键
        
        # 对于其他事件,特别是输入法相关事件,不要阻止
        if event.type() in [QEvent.InputMethod, QEvent.InputMethodQuery]:
            return False  # 允许输入法事件通过
        
        return super().eventFilter(obj, event)

### 调试建议

如果以上方法仍然无效,可以添加更多调试信息来帮助定位问题:

1. 检查焦点链:
   
   def print_focus_chain():
       widget = QApplication.focusWidget()
       chain = []
       while widget:
           chain.append(f"{widget.__class__.__name__}({widget.isVisible()})")
           widget = widget.parentWidget()
       print("焦点链:", " -> ".join(chain))

2. 检查输入法状态:
   
   def check_ime_status():
       app = QApplication.instance()
       print(f"输入法启用: {app.inputMethod().isVisible()}")
       print(f"活动窗口: {app.activeWindow()}")

3. 在关键位置添加这些调试调用,特别是在显示和关闭窗口时。

### 最终建议

如果问题仍然存在,我建议采用方案一(每次重新创建B实例),这是最可靠的方法。虽然会增加一些开销,但可以确保每次都有干净的状态。

如果性能是关键考虑因素,可以尝试方案二并结合更详细的调试来找出根本原因。事件过滤器很可能是问题的关键所在,需要确保它不会干扰输入法正常工作。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-9-1 11:13

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表