pynput - non-specified key combo triggers hotkey fxn after ALT-TAB (Windows) - pynput

Code I'm running in .ipynb file (jupyter notebook in VS Code):
from pynput import keyboard
def function_1():
print('Function 1 activated')
with keyboard.GlobalHotKeys({
'<alt>+<ctrl>+c': function_1}
) as h:
h.join()
When this is running, pressing CTRL+ALT+C triggers function_1, as expected. Pressing CTRL+C (no ALT) does not trigger function_1, also as expected.
But if I press ALT-TAB to switch windows, CTRL+C now triggers function_1.
I changed the code to use '<alt>+<ctrl>+b' and get same result. CTRL+B will trigger if ALT-TAB is pressed beforehand.
Can anyone replicate or see what I'm doing wrong?

I don't have a problem with Ctrl+B with the following code:
import time
from random import randint as randy
from pynput import keyboard
# https://pynput.readthedocs.io/en/latest/keyboard.html#global-hotkeys
def on_activate():
print('Global hotkey activated!')
global stop
stop = True
return False # stop the listener
def for_canonical(f):
return lambda k: f(listener.canonical(k))
# hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+<alt>+h'), on_activate)
# hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+<alt>+c'), on_activate)
# hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+c'), on_activate)
hotkey = keyboard.HotKey(keyboard.HotKey.parse('<ctrl>+b'), on_activate)
# create a non-blocking listener
listener = keyboard.Listener(on_press=for_canonical(hotkey.press), on_release=for_canonical(hotkey.release))
listener.start()
stop = False
while not stop:
print(f"Rolling 2d6: {randy(1,6) + randy(1,6):2}")
time.sleep(0.2)
print("Finished!")
The console shows:
>python3.9 -i pyn_hotkey.py
Rolling 2d6: 6
Rolling 2d6: 11
Rolling 2d6: 10
Rolling 2d6: 11
Rolling 2d6: 4
Rolling 2d6: 10
Rolling 2d6: 9
Global hotkey activated!
Finished!
>>> ^B
Note that in spite of my expectation, returning False doesn't stop the listener, so I'm not sure what's going on there; listener.stop() works.

Related

Is there any way I can speed up my macro that records keyboard and mouse movements? (Python)

I was creating this keyboard and mouse macro that I could use. I wanted to be able to speed the process up. The playback isn't very fast because it only goes as fast as I can record it. I would want to make sure that both the mouse and keyboard are faster so they can sync. Is there any way that is possible? Such as clicking the space button will make it go 1.5x faster.
My code:
import mouse
import keyboard
import pyautogui
keyboard.wait('delete')
currentMouseX1, currentMouseY1 = pyautogui.position()
mouse_events = []
mouse.hook(mouse_events.append)
keyboard.start_recording()
keyboard.wait("delete")
mouse.unhook(mouse_events.append)
keyboard_events = keyboard.stop_recording()
#Keyboard threadings:
while True:
pyautogui.moveTo(currentMouseX1, currentMouseY1)
k_thread = threading.Thread(target = lambda :keyboard.play(keyboard_events))
k_thread.start()
#Mouse threadings:
m_thread = threading.Thread(target = lambda :mouse.play(mouse_events))
m_thread.start()
#waiting for both threadings to be completed
k_thread.join()
m_thread.join()```

When I run this sample pygame code DOS window appears and suddenly disappears without waiting any user input

When I run this sample PyGame code, a command window appears and suddenly disappears without waiting any user input. What is wrong? I am a newbie and trying to learn python. I have only VBA experiance previously.
The Python version is: Python 3.8.
import pygame
import sys
pygame.init()
win = pygame.display.set_mode((500,500))
pygame.display.set_caption("First Game")
x = 50
y = 50
width = 40
height = 60
vel = 5
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
win.fill((0,0,0)) # Fills the screen with black
pygame.draw.rect(win, (255,0,0), (x, y, width, height))
pygame.display.update()
pygame.quit()
sys.exit()
Both your pygame.quit() and sys.exit() should be outside of the run loop, so that they execute only when the loop exits.
What you have now will run a single iteration, because it exits at the end of that iteration. Some people say a short game is a good game but you can take that too far :-)
What I see when I run your code is that the window with the red square appears briefly, then the whole program exits. When I fix your indentation, the window stays there and I can move the red square with the arrow keys:
In other words, this is the form you need:
run = True
while run:
do_stuff()
pygame.quit()
sys.exit() # Probably not needed since you'll exit anyway.
It also sounds like you're running it from an IDE, given that the window disappears. I always like to run it from the command line so that, should an exception occur (or important output shows up), I can see it after the program has finished.

How to display an icon in the systray reflecting NumLk state

My computer doesn't have any way of letting me know if my NumLk is on or off, so I am trying to add an icon in my systray that will changed depending on the state of my NumLk. This .py will always be running when my computer is on.
So far I was able to mix 3 codes and I am able to display the icon in the systray but it doesn't get updated when the state of NumLk change. Actually if I press NumLk twice, I still get the same icon (the on one) and I get this error:
QCoreApplication::exec: The event loop is already running
File "\systray_icon_NumLk_on_off.py", line 21, in on_key_press
main(on)
File "\systray_icon_NumLk_on_off.py", line 46, in main
sys.exit(app.exec_())
SystemExit: -1
My code may not be the best way to do it, so any alternative is welcome! Here is what I came up so far:
#####get the state of NumLk key
from win32api import GetKeyState
from win32con import VK_NUMLOCK
#how to use: print(GetKeyState(VK_NUMLOCK))
#source: http://stackoverflow.com/questions/21160100/python-3-x-getting-the-state-of-caps-lock-num-lock-scroll-lock-on-windows
#####Detect if NumLk is pressed
import pyglet
from pyglet.window import key
window = pyglet.window.Window()
#source: http://stackoverflow.com/questions/28324372/detecting-a-numlock-capslock-scrlock-keypress-keyup-in-python
on=r'on.png'
off=r'off.png'
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.NUMLOCK:
if GetKeyState(VK_NUMLOCK):
#print(GetKeyState(VK_NUMLOCK))#should be 0 and 1 but
main(on)
else:
main(off)
#window.event
def on_draw():
window.clear()
### display icon in systray
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
#source: http://stackoverflow.com/questions/893984/pyqt-show-menu-in-a-system-tray-application - add answer PyQt5
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
def main(image):
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon(image), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
pyglet.app.run()
The reason for QCoreApplication::exec: The event loop is already running is actually because you're trying to start app.run() twice. Qt will notice there's already an instance running and throw this exception. When instead, what you want to do is just swap the icon in the already running instance.
Your main problem here is actually the mix of libraries to solve one task if you ask me.
Rather two tasks, but using Qt5 for the graphical part is fine tho.
The way you use Pyglet is wrong from the get go.
Pyglet is intended to be a highly powerful and effective graphics library where you build a graphics engine ontop of it. For instance if you're making a game or a video-player or something.
The way you use win32api is also wrong because you're using it in a graphical window that only checks the value when a key is pressed inside that window.
Now, if you move your win32api code into a Thread (a QtThread to be precise) you can check the state no matter if you pressed your key inside your graphical window or not.
import sys
import win32api
import win32con
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread, enumerate
from time import sleep
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(QtWidgets.qApp.quit)
self.setContextMenu(menu)
class KeyCheck(QtCore.QThread):
def __init__(self, mainWindow):
QtCore.QThread.__init__(self)
self.mainWindow = mainWindow
def run(self):
main = None
for t in enumerate():
if t.name == 'MainThread':
main = t
break
while main and main.isAlive():
x = win32api.GetAsyncKeyState(win32con.VK_NUMLOCK)
## Now, GetAsyncKeyState returns three values,
## 0 == No change since last time
## -3000 / 1 == State changed
##
## Either you use the positive and negative values to figure out which state you're at.
## Or you just swap it, but if you just swap it you need to get the startup-state correct.
if x == 1:
self.mainWindow.swap()
elif x < 0:
self.mainWindow.swap()
sleep(0.25)
class GUI():
def __init__(self):
self.app = QtWidgets.QApplication(sys.argv)
self.state = True
w = QtWidgets.QWidget()
self.modes = {
True : SystemTrayIcon(QtGui.QIcon('on.png'), w),
False : SystemTrayIcon(QtGui.QIcon('off.png'), w)
}
self.refresh()
keyChecker = KeyCheck(self)
keyChecker.start()
sys.exit(self.app.exec_())
def swap(self, state=None):
if state is not None:
self.state = state
else:
if self.state:
self.state = False
else:
self.state = True
self.refresh()
def refresh(self):
for mode in self.modes:
if self.state == mode:
self.modes[mode].show()
else:
self.modes[mode].hide()
GUI()
Note that I don't do Qt programming often (every 4 years or so).
So this code is buggy at it's best. You have to press Ctrl+C + Press "Exit" in your menu for this to stop.
I honestly don't want to put more time and effort in learning how to manage threads in Qt or how to exit the application properly, it's not my area of expertis. But this will give you a crude working example of how you can swap the icon in the lower corner instead of trying to re-instanciate the main() loop that you did.

Pyqt docks get hidden when window minimized and restored

When I minimize the application window on Windows XP and restore it later, the dock will be hidden. This has to do with view menu which has toggles to set visibility and of course is connected by signals.
I hope this will save someone a few hours of debugging.
Here is a full functional example with both wrong and right code:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
class Ui_QMainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(200, 200)
self.menubar = QtGui.QMenuBar(self)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 27))
self.menuMenu = QtGui.QMenu(self.menubar)
self.setMenuBar(self.menubar)
self.dock = QtGui.QDockWidget(self)
self.dock.setObjectName("dock")
self.dockContents = QtGui.QWidget()
self.dockContents.setObjectName("dockContents")
self.dock.setWidget(self.dockContents)
self.addDockWidget(QtCore.Qt.DockWidgetArea(4), self.dock)
self.action = QtGui.QAction(self)
self.action.setCheckable(True)
self.action.setChecked(True)
self.action.setObjectName("action")
self.menuMenu.addAction(self.action)
self.menubar.addAction(self.menuMenu.menuAction())
self.setWindowTitle("Example of dock remaining minimized")
self.menuMenu.setTitle("Menu")
self.dock.setWindowTitle("I'm a dock")
self.action.setText("Dock visibility")
if True:
# This is NOT working on Windows XP.
# Minimize the window and restore again, the dock is gone.
# Other than that it works.
QtCore.QObject.connect(self.action,
QtCore.SIGNAL("toggled(bool)"),
self.dock.setVisible)
QtCore.QObject.connect(self.dock,
QtCore.SIGNAL("visibilityChanged(bool)"),
self.action.setChecked)
else:
# This DOES work, but boy it looks nasty, writing useless
# per dock is not nice.
QtCore.QObject.connect(self.action,
QtCore.SIGNAL("triggered()"),
self.toggle_dock)
QtCore.QObject.connect(self.dock,
QtCore.SIGNAL("visibilityChanged(bool)"),
self.action.setChecked)
def toggle_dock(self):
self.dock.setVisible(not self.dock.isVisible())
def main():
app = QtGui.QApplication(sys.argv)
ui = Ui_QMainWindow()
ui.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
There is a much simpler way to do this, using QDock.toggleViewAction. This function returns a ready-made action that handles the checked state automatically.
So your code would become simply:
self.action = self.dock.toggleViewAction()
self.action.setObjectName("action")
self.menuMenu.addAction(self.action)
self.menubar.addAction(self.menuMenu.menuAction())
self.setWindowTitle("Example of dock remaining minimized")
self.menuMenu.setTitle("Menu")
self.dock.setWindowTitle("I'm a dock")
self.action.setText("Dock visibility")
and you can then get rid of all the signal handling.

Python event binding with tkinter

So guys I'm new to GUIs in Python and I've been trying to understand the envents process, here's my code, and the intention that whenever I press the 'a' key it should print "key pressed'. But it won't work for me.
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
from tkinter import *
root = Tk()
def callback(event):
print("key pressed")
canvas = Canvas(root, width=100, height=100, bg='blue')
canvas.bind("a", callback)
canvas.pack()
root.mainloop()
It doesn't execute the callback function because the focus of the program is on the Tk element. If you replace that line with root.bind("a", callback), it will work as you expect.
The problem is that the canvas element doesn't receive the focus when you click on it like other widgets as Entry, so it will only respond to keydown events if you call first canvas.focus_set().

Resources