python - 强制键盘焦点到 LineEdit QT

标签 python windows qt pyside

我正在尝试开发一个由全局按键绑定(bind)触发的 Windows 覆盖弹出窗口,一旦按下按键绑定(bind),它应该将焦点捕获到 QLineEdit 中。问题是,如果它曾经获得焦点,但我单击外部有效地移除焦点,则此后无法重新获得焦点。

这是我尝试用来强制键盘焦点位于 QLineEdit 上的代码的简化版本:

from PySide6 import QtCore, QtWidgets, QtGui
from pynput import keyboard

class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.input = QtWidgets.QLineEdit()
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.input)

        self.input.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setWindowState(QtCore.Qt.WindowActive)
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
    
    @QtCore.Slot()
    def toggle_visible(self):
        if self.isVisible():
            print("Hiding popup")
            self.hide()
        else:
            print("Showing popup")
            self.show()
            self.activateWindow()
            self.input.grabKeyboard()
            self.input.setFocus()

class KeybindPressed(QtCore.QObject):
    keybind_pressed = QtCore.Signal()

    def __call__(self):
        self.keybind_pressed.emit()


if __name__ == "__main__":
    app = QtWidgets.QApplication([])

    pressed = KeybindPressed()
    with keyboard.GlobalHotKeys({"<alt>+<space>": pressed}):

        widget = MyWidget()
        pressed.keybind_pressed.connect(widget.toggle_visible)
        widget.resize(800, 600)
        widget.show()

        app.exec()

这段记录显示了在显示时焦点停留在其他应用程序而不是返回到窗口的不良行为。

enter image description here

最佳答案

感谢ekhumoro我能够弄清楚如何强制聚焦在窗口上。下面是强制聚焦所需的代码:

导入和设置

windows = False
if os.name == "nt":
    import win32gui, win32con, win32process, win32api
    win32gui.SystemParametersInfo(win32con.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, win32con.SPIF_SENDWININICHANGE | win32con.SPIF_UPDATEINIFILE)
    windows = True

实际强制焦点的代码,当您的窗口应该获得焦点时将调用此代码:

def force_focus(qt_widget_instance: QtWidgets.QWidget):
    if windows:
        fgwin = win32gui.GetForegroundWindow()
        fg = win32process.GetWindowThreadProcessId(fgwin)[0]
        current = win32api.GetCurrentThreadId()
        if current != fg:
            win32process.AttachThreadInput(fg, current, True)
            win32gui.SetForegroundWindow(qt_widget_instance.winId())
            win32process.AttachThreadInput(fg, win32api.GetCurrentThreadId(), False)

这是实现此功能的完整示例:

from PySide6 import QtCore, QtWidgets, QtGui
from pynput import keyboard

import os

windows = False
if os.name == "nt":
    import win32gui, win32con, win32process, win32api
    win32gui.SystemParametersInfo(win32con.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, win32con.SPIF_SENDWININICHANGE | win32con.SPIF_UPDATEINIFILE)
    windows = True

def force_focus(qt_widget_instance: QtWidgets.QWidget):
    if windows:
        fgwin = win32gui.GetForegroundWindow()
        fg = win32process.GetWindowThreadProcessId(fgwin)[0]
        current = win32api.GetCurrentThreadId()
        if current != fg:
            win32process.AttachThreadInput(fg, current, True)
            win32gui.SetForegroundWindow(qt_widget_instance.winId())
            win32process.AttachThreadInput(fg, win32api.GetCurrentThreadId(), False)

class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.input = QtWidgets.QLineEdit()
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.input)

        self.input.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setWindowState(QtCore.Qt.WindowActive)
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
    
    @QtCore.Slot()
    def toggle_visible(self):
        if self.isVisible():
            print("Hiding popup")
            self.hide()
        else:
            print("Showing popup")
            self.show()
            force_focus(self)
            self.activateWindow()
            self.input.grabKeyboard()
            self.input.setFocus()



class KeybindPressed(QtCore.QObject):
    keybind_pressed = QtCore.Signal()

    def __call__(self):
        self.keybind_pressed.emit()


if __name__ == "__main__":
    app = QtWidgets.QApplication([])

    pressed = KeybindPressed()
    with keyboard.GlobalHotKeys({"<cmd>+<space>": pressed}):
    
        widget = MyWidget()
        pressed.keybind_pressed.connect(widget.toggle_visible)
        widget.resize(800, 600)
        widget.show()

        app.exec()

关于python - 强制键盘焦点到 LineEdit QT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68764703/

相关文章:

python - 如何使 Python 的 winreg 查看注册表中在 Adob​​e CC 程序的注册表编辑器中可见的条目?

windows - 如何在 Visual Studio 安装项目中将安装目录公开为公共(public)属性

python - 如何将Qt图形场景项保存在文件中并再次打开它?

windows - Qt - QTableWidget 不接受放置

python - 使用Pyqt4和QSound播放声音

python - python内置类型的比较排序

python - 用 `data_key` 定义的 Marshmallow 字段和用 `attribute` 定义的 Marshmallow 字段有什么区别(标识符相反?)

python - 如何改进正则表达式来提取电话号码?

c++ - C++ 中的 Win32-Api CreateFile(...) 失败

python - Windows : VIM doesn't find Python3 上的 GIT bash