Как сделать чтобы мой код работал и в фоновом режиме?
Есть такой код,
https://gist.github.com/newpander/c8c83f586c9f58a3228dec65c07acd5b
(код только в такой форме ибо не влезает).
но при переходе на другое окно он ничего не отображает.
Мой код работает пока выбрано его окно, при выборе другого любого окна код перестает что-либо отображать. Нужно чтобы код отображал все и в фоновом режиме
Ответы (1 шт):
Qt может получить доступ к событиям клавиатуры, только если любое из его окон верхнего уровня имеет фокус клавиатуры.
Если окно свернуто или другое окно сфокусировано, выне будетеполучать события клавиатуры.
Единственное решение - использовать внешнюю библиотеку, но у них есть ограничения
в зависимости от используемой ОС.
Eсть разные решения, вам может понадобиться посмотреть в
https://pypi.python.org/pypi/keyboard/ или
https://pypi.org/project/pynput/ .
Обратите внимание:
- ваш пример сокращен до минимального.
Для наблюдения происходящего используйте клавишу
A. - форма перенесена в отдельный модуль keyboard_ui.py.
- использовал библиотеку
keyboard, если окно не активное!!!
То что вы хотите, может выглядеть примерно так:
import time
import keyboard # pip install keyboard
from PyQt5.Qt import *
from keyboard_ui import Ui_MainWindow
keymap = {}
for key, value in vars(Qt).items():
if isinstance(value, Qt.Key):
keymap[value] = key.partition('_')[2]
modmap = {
Qt.ControlModifier: keymap[Qt.Key_Control],
Qt.AltModifier: keymap[Qt.Key_Alt],
Qt.ShiftModifier: keymap[Qt.Key_Shift],
Qt.MetaModifier: keymap[Qt.Key_Meta],
Qt.GroupSwitchModifier: keymap[Qt.Key_AltGr],
Qt.KeypadModifier: keymap[Qt.Key_NumLock],
}
class Example(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.prev_key = []
self.key_by_time = dict()
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.qwindowIsActive = True
self.hook = None
QTimer.singleShot(0, self.on_qwindow)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.label.setStyleSheet("color: red;")
self.prev_key = [self.label, ]
# ...
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.label.setStyleSheet("color: black;")
self.prev_key = [self.label, ]
# ...
def keyPressEvent(self, event):
_key, _keys = self.keyevent_to_string(event)
key = event.key()
if not event.isAutoRepeat() and key not in self.key_by_time:
self.key_by_time[key] = time.perf_counter()
if self.prev_key:
for k in self.prev_key:
k.setStyleSheet("")
if _keys[0] == 'Control' and key == Qt.Key_A:
self.A.setStyleSheet("color: red;")
self.Ctrl.setStyleSheet("color: red;")
self.prev_key = [self.Ctrl, self.A]
elif _keys[0] == 'Alt' and key == Qt.Key_A:
self.A.setStyleSheet("color: red;")
self.Alt.setStyleSheet("color: red;")
self.prev_key = [self.Alt, self.A]
elif _keys[0] == 'Shift' and key == Qt.Key_A:
self.A.setStyleSheet("color: red;")
self.ShifL.setStyleSheet("color: red;")
self.prev_key = [self.ShifL, self.A]
elif key == Qt.Key_A:
self.A.setStyleSheet("color: red;")
self.prev_key = [self.A, ]
# ...
def keyReleaseEvent(self, event):
_key, _keys = self.keyevent_to_string(event)
key = event.key()
if self.prev_key:
for k in self.prev_key:
k.setStyleSheet("")
if _keys[0] == 'Control' and key == Qt.Key_A:
self.A.setStyleSheet("color: black;")
self.Ctrl.setStyleSheet("color: black;")
self.prev_key = [self.Ctrl, self.A]
elif _keys[0] == 'Alt' and key == Qt.Key_A:
self.A.setStyleSheet("color: black;")
self.Alt.setStyleSheet("color: black;")
self.prev_key = [self.Alt, self.A]
elif _keys[0] == 'Shift' and key == Qt.Key_A:
self.A.setStyleSheet("color: black;")
self.ShifL.setStyleSheet("color: black;")
self.prev_key = [self.ShifL, self.A]
elif key == Qt.Key_A:
self.A.setStyleSheet("color: black;")
self.prev_key = [self.A, ]
if not event.isAutoRepeat() and key in self.key_by_time:
t = self.key_by_time.pop(key)
self.time_right.setText(f'Кнопка {key!r} была нажата {time.perf_counter() - t:.2f} секунд')
# ...
def keyevent_to_string(self, event):
sequence = []
for modifier, text in modmap.items():
if event.modifiers() & modifier:
sequence.append(text)
key = keymap.get(event.key(), event.text())
if key not in sequence:
sequence.append(key)
return '+'.join(sequence), sequence
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def on_qwindow(self):
self.qwindow = self.windowHandle()
self.qwindow.activeChanged.connect(self.handle_activeChanged)
def handle_activeChanged(self):
print("окно активное (isActive)? {}".format(self.qwindow.isActive())) # !!!
if self.qwindow.isActive():
self.qwindowIsActive = True
else:
self.qwindowIsActive = False
self.setGrabbing()
def setGrabbing(self):
if not self.qwindowIsActive: # если окно не активное
self.hook = keyboard.hook(self.keyboardEventReceived)
else:
if self.hook:
keyboard.unhook(self.hook)
def keyboardEventReceived(self, event):
print(f"keyboardEventReceived: event_type -> {event.event_type}, event.name -> {event.name}")
if event.event_type == 'down':
if event.name in ['A', 'a']:
print('A Press')
self.A.setStyleSheet("color: red;")
# ...
if event.event_type == 'up':
if event.name in ['A', 'a']:
print('A Release')
self.A.setStyleSheet("color: black;")
# ...
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = Example()
w.show()
sys.exit(app.exec_())
