[英]Steal focus in PyQt5/Pyside2
我正在創建一個啟動器,采用Albert 、 Alfred或uLauncher的風格。 我的應用程序在后台運行,並在按下熱鍵時顯示。 我使用pynput
來收聽熱鍵。 我不能使用 PyQt5 熱鍵的功能(不能嗎?),因為我需要監聽系統 scope 中的鍵盤事件,而不僅僅是應用程序的 scope。
按下快捷方式時,它會調用我的小部件的 show() 方法。 唯一的問題是,盡管使用了raise_
、 setFocus
和activateWindow
,但我無法將焦點重新放在我的 window 上。
我發現了一個(丑陋的)解決方法,它包括打開一個 QMessageBox(+ 調整它的外觀以使其不可見,但我沒有將它放在示例代碼中)並在之后立即關閉它。
當我在研究 Linux 時,該解決方法正在完成這項工作,我已經准備好忘記它是多么丑陋,因為它完成了這項工作。 但是我切換到 Windows (我的應用程序也必須在其上運行),現在這個厚顏無恥的技巧似乎導致我的應用程序凍結然后崩潰。 業力? 當然。
無論如何,如果我的應用程序無法抓住焦點,它就毫無用處,所以我問了兩個問題,我很高興只解決一個問題。 :)
這是一個可以使用的示例代碼。
非常感謝:)
編輯:我剛剛發現即使停用 QMessageBox 解決方法,應用程序最終也會崩潰(在 5、20、30 次熱鍵調用之后)。 所以問題也可能在於我將快捷方式綁定到 GUI 的方式,我擔心線程問題,但這超出了我的知識范圍:/
import sys
from PyQt5.QtWidgets import QLineEdit, QApplication, QMessageBox
from PyQt5.QtCore import QSize, Qt, QEvent
from pynput import keyboard
class Launcher(QLineEdit):
def __init__(self):
super().__init__()
self.setFixedSize(QSize(600, 50))
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setPlaceholderText('Search...')
self.installEventFilter(self)
self.set_shortcut('<ctrl>+`')
def set_shortcut(self, shortcut):
def for_canonical(f):
return lambda k: f(listener.canonical(k))
hotkey = keyboard.HotKey(
keyboard.HotKey.parse(shortcut),
self.wake_up)
listener = keyboard.Listener(
on_press=for_canonical(hotkey.press),
on_release=for_canonical(hotkey.release))
listener.start()
def wake_up(self):
print('Waking up')
self.show()
self.cheeky_focus_stealer()
def cheeky_focus_stealer(self):
self.setFocus()
self.raise_()
self.activateWindow()
# Working of linux, but causes freeze/crash on Windows 10
message_box = QMessageBox(self)
message_box.show()
message_box.hide()
def eventFilter(self, obj, event):
if obj is self and event.type() == QEvent.KeyPress:
if event.key() == Qt.Key_Escape:
self.hide()
return True
return super().eventFilter(obj, event)
def main():
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
window = Launcher()
window.show()
app.exec_()
if __name__ == "__main__":
main()
我發現了我的錯誤,所以我在這里發布了一段更新的代碼,因為它可能對任何試圖將全局熱鍵綁定到影響 GUI(即兩個不同的線程通信)的 function 的人有所幫助。
我的錯誤確實是將熱鍵觸發的操作直接綁定到我的show()
方法,這意味着pynput
線程將嘗試與QApplication
通信。
訣竅是使用pyqtSignal()
並要求它觸發show()
方法。 信號本身由熱鍵觸發。
以干凈的方式完成之后,我的cheeky_focus_stealer
再次工作,因為它是從GUI 線程運行的。
import sys
from PyQt5.QtWidgets import QLineEdit, QApplication, QMessageBox
from PyQt5.QtCore import QSize, Qt, QEvent, QObject, pyqtSignal
from pynput import keyboard
class Forwarder(QObject):
signal = pyqtSignal()
class Launcher(QLineEdit):
def __init__(self):
super().__init__()
self.setFixedSize(QSize(600, 50))
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setPlaceholderText('Search...')
self.installEventFilter(self)
self.set_shortcut('<ctrl>+`')
def set_shortcut(self, shortcut):
# The forwarder must be parented to the Launcher
forwarder = Forwarder(parent=self)
forwarder.signal.connect(self.wake_up)
def for_canonical(f):
return lambda k: f(listener.canonical(k))
hotkey = keyboard.HotKey(
keyboard.HotKey.parse(shortcut),
forwarder.signal.emit)
listener = keyboard.Listener(
on_press=for_canonical(hotkey.press),
on_release=for_canonical(hotkey.release))
listener.start()
def wake_up(self):
print('Waking up')
self.show()
self.cheeky_focus_stealer()
def cheeky_focus_stealer(self):
self.setFocus()
self.raise_()
self.activateWindow()
# Working of linux, but causes freeze/crash on Windows 10
message_box = QMessageBox(self)
message_box.show()
message_box.hide()
def eventFilter(self, obj, event):
if obj is self and event.type() == QEvent.KeyPress:
if event.key() == Qt.Key_Escape:
self.hide()
return True
return super().eventFilter(obj, event)
def main():
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
window = Launcher()
window.show()
app.exec_()
if __name__ == "__main__":
main()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.