Set keyboard layout using pyudev - python

I wrote this script to change the usb keyboard layout automatically on plug-in
import pyudev
from subprocess import call
monitor = pyudev.Monitor.from_netlink(pyudev.Context())
monitor.filter_by('usb')
def kbd_event(action, device):
if action == 'add':
call(["setxkbmap", "carpalx"])
observer = pyudev.MonitorObserver(monitor, kbd_event)
observer.start()
setxkbmap carpalx works if I type it in bash, but it doesn't change the layout in the above code. So I did this in bash:
setxkbmap carpalx
xmodmap -pke > carpalx2
changed the above call line to call(["xmodmap", "./carpalx2"]) and now the script works. I have the following problems:
Why does xmodmap work in the code and setxkbmap doesn't, but both work in bash?
Currently, kbd_event is called for every usb event and call(["xmodmap", "./carpalx2"]) is run for every usb device I plug in. How can I further filter the events so the layout changes only when I insert a keyboard?
With my current code, the keyboard layout changes every time I plug in my mouse :)

You can filter keyboards by checking for the ID_INPUT_KEYBOARD property:
if action == 'add' and device['ID_INPUT_KEYBOARD'] == '1':
print('a keyboard was added')
Concerning the difference between calling setxkbmap directly and using the script, I'd guess that the X server needs time to initialize the keyboard, too. UDev invokes the callback as soon as the keyboard has finished udev processing, but this can easily before the X11 server configures and initializes the new keyboard.
Check the X.org logs, and any error message that setxkbmap might print.

Related

Using Rainmeter with Python

So I have designed a layout in Rainmeter which serves as a GUI for my Voice Assistant program. Now, I want to integrate the running of Rainmeter with Python. For example, if an user already has a Rainmeter layout loaded in his/her system, then running my script will automatically override his/her layout and activate my Layout unless he/she manually changes it back to his/her own layout. This process would continue whenever my script is run. It basically goes like this: The user runs the script, it checks whether any other skin is loaded or not (Assuming that Rainmeter is installed on the system). If any other skin is loaded, it overrides the skin with my one else it bypasses the override function and directly loads my skin.
I have no idea on how to achieve this thing. I have successfully written the lines to start and exit rainmeter with python but I don't know anything about how to load the layouts! Please help!
Here is the script I have written to start and exit Rainmeter:
import os
trigger = input()
if trigger == "y":
try:
os.startfile("C:\Program Files\Rainmeter\Rainmeter.exe")
print("Rainmeter started successfully")
except:
print("There was an error")
trigger = input()
if trigger == "exit":
try:
os.system("taskkill /f /im Rainmeter.exe")
print("Rainmeter closed successfully")
except:
print("There was an error")
You can use the following code to load a Rainmeter Layout:
import subprocess
subprocess.call(["C:\Program Files\Rainmeter\Rainmeter.exe", "!LoadLayout", "abcd"])
Here we are using rainmeter bangs to load the layout. Change abcd with name of your layout.

grab() after device.leds() turns off led

I can read the NUML_LED state using evdev. But if I grab() the device after reading the leds when I exit the program the led is off even though the state is still on as far as the system is concerned.
import evdev
device = evdev.InputDevice('/dev/input/event3')
try:
print(device.leds(verbose=True))
print("Using device:\n" + str(device))
device.grab()
except (KeyboardInterrupt, SystemExit):
pass
If I just do one of grab() OR device.leds() without the other they (as expected) don't turn off. But invoking both messes with the leds.
Edit:
I just realised that if LED_NUML and LED_CAPSL are on when I exit the leds are off but when I press either num_lock or caps_lock the state of the other button gets fixed also.
IE:
- LED_NUML and LED_CAPSL are on
- run script
- LED_NUML and LED_CAPSL are off
- press num_lock turns num_lock off (LED_NUML is already off so it stays off)
- LED_CAPSL turns on
At this time it is not possible. There is an issue in the kernel. As ssieb says on the python evdev issues page about this issue.
When a device is released, all handlers attached to the device get restarted. My guess is that there is some handler that is resetting the LEDs.

Keypress detection

I've been trying to get keypresses to be detected in a Python program. I want to find a way to do this without using Tkinter, curses, or raw_input. Here's what I'm going at:
while True:
if keypressed==1:
print thekey
Does anyone know how this is possible?
Python has a keyboard module with many features. Install it, perhaps with this command:
pip3 install keyboard
Then use it in code like:
import keyboard #Using module keyboard
while True:#making a loop
try: #used try so that if user pressed other than the given key error will not be shown
if keyboard.is_pressed('a'): #if key 'a' is pressed
print('You Pressed A Key!')
break #finishing the loop
else:
pass
except:
break #if user pressed other than the given key the loop will break
You can set multiple Key Detection:
if keyboard.is_pressed('a') or keyboard.is_pressed('b') or keyboard.is_pressed('c'):
#then do this
I took the liberty of editing your question slightly so it makes sense and has an answer, at least on Windows. (IDLE only interacts with your keyboard by means of the tkinter interface to tk.) On Windows, the answer is to use the msvcrt module's console io functions
import msvcrt as ms
while True:
if ms.kbhit():
print(ms.getch())
For other systems, you will have to find the equivalent system-specific calls. For posix systems, these may be part of curses, which you said you did not to use, but I do not know.
These functions do not work correctly when the program is run is run from IDLE in its default mode. The same may be true for other graphics-mode IDEs.

What is the easiest way to detect key presses in python 3 on a linux machine?

Right now I'm trying to make a small code with a raspberry pi and and a makey makey. The makey makey is a small board that acts as a usb keyboard when certain contacts are powered. My question is what is the easiest way to detect those keypresses inside a python script. I understand using the GPIO pins would be easier, but right now I'm looking for this. I have seen examples such as using using getch() from msvcrt (which from what I understand is windows only,) using pygame.key, and using getKey. Which of theses is easiest to use? Are there any that can detect a key being pressed and a key being released?
Pseudo code:
import whatever needs importing
if the "W" key is pressed:
print ("You pressed W")
elif the "S" is pressed:
print ("You pressed S")
and so on. Thanks.
This is a simple loop that will put stdin in raw mode (disabling buffering so you don't have to press enter) to get single characters. You should do something smarter (like a with statement to disable it) but you get the idea here:
import tty
import sys
import termios
orig_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin)
x = 0
while x != chr(27): # ESC
x=sys.stdin.read(1)[0]
print("You pressed", x)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
I think you'd have to loop to detect key releases in Python.
ETA some more explanation:
On Linux, input to your program will be line buffered. This means that the operating system will buffer up input until it has a whole line, so your program won't even see anything the user typed until the user also hits 'enter'. In other words, if your program is expecting the user to type 'w' and the user does this, 'w' will be sitting in the OS's buffer until the user hits 'enter'. At this point the entire line is delivered to your program so you will get the string "w\n" as the user's input.
You can disable this by putting the tty in raw mode. You do this with the Python function tty.setcbreak which will make a call down the tty driver in linux to tell it to stop buffering. I passed it the sys.stdin argument to tell it which stream I wanted to turn buffering off for1. So after the tty.setcbreak call, the loop above will give you output for every key the user presses.
A complication, though, is that once your program exits, the tty is still in raw mode. You'll generally find this unsatisfying since you don't get any of the power that modern terminal settings offer (like when you use control or escape sequences). For example, notice that you might have trouble exiting the program with ctrl-C. Consequently you should put the terminal back into cooked mode once you are done reading input characters. The termios.tcsetattr call simply says "put the terminal back the way I found it". It knows how to do this by first calling termios.tcgetattr at the beginning of the program which is saying "tell me all the current settings for the terminal".
Once you understand all that, you should easily be able to encapsulate the functionality in a function that suits your program.
1 stdin is the stream that input comes to you from the user. Wikipedia can tell you more about standard streams.
Using a good lightweight module curtsies you could do something like this (taken from their examples/ directory):
from curtsies import Input
def main():
with Input(keynames='curses') as input_generator:
for e in input_generator:
print(repr(e))
if __name__ == '__main__':
main()
So pressing keys on your keyboard gives you something like this:
'a'
's'
'KEY_F(1)'
'KEY_F(2)'
'KEY_F(3)'
'KEY_F(4)'
'KEY_F(5)'
'KEY_LEFT'
'KEY_DOWN'
'KEY_UP'
'KEY_RIGHT'
'KEY_NPAGE'
'\n'
curtsies is used by bpython as a low level abstraction of terminal-related stuff.
The basic problem of decoding the input is that in different terminals and terminal emulator programs like xterm or gnome-terminals physically same keys produce different keycode sequences. That's why one needs to know which terminal settings should be used to decode input. Such a module helps to abstract from those gory details.
Since your question states that you are using a Raspberry Pi and a USB HID keyboard peripheral, but does not specify whether or not you have the Pi configured to boot into text or graphical mode where you will be running your script, I would suggest using libinput which will work in either case.
You can use libinput's python bindings to read keyboard (and most other input devices) events directly from the kernel.
pip3 install python-libinput
The interface to this subsystem is exposed through the character devices which usually live in /dev/input/. They are managed by udev rules which create one or more character devices per attached input device, and are added and removed dynamically when, for example, a USB keyboard is attached or unplugged, or a Bluetooth mouse connects or disconnects.
Libinput handles for you the task of opening and reading from all attached input devices, and opening and closing devices when they are added and removed, respectively.
Using libinput from python to read key events would look like this:
import libinput
def read_key_events():
# init libinput
li = libinput.LibInput(udev=True)
li.udev_assign_seat('seat0')
# loop which reads events
for event in li.get_event():
# test the event.type to filter out only keyboard events
if event.type == libinput.constant.Event.KEYBOARD_KEY:
# get the details of the keyboard event
kbev = event.get_keyboard_event()
kcode = kbev.get_key() # constants in libinput.define.Key.KEY_xxx
kstate = kbev.get_key_state() # constants libinput.constant.KeyState.PRESSED or .RELEASED
# your key handling will look something like this...
if kstate == libinput.constant.KeyState.PRESSED:
print(f"Key {kcode} pressed")
elif kstate == libinput.constant.KeyState.RELEASED:
if kbev.get_key() == libinput.define.Key.KEY_ENTER:
print("Enter key released")
elif kcode == libinput.define.Key.KEY_SPACE:
print("Space bar released")
else:
print(f"Key {kcode} released")
One minor gotcha to be aware of is that udev is commonly configured to set permissions on the event devices it creates in /dev/input/ to allow access only from users who are members of a special supplementary 'input' group, since to allow unrestricted access to raw user key and mouse input would be a major security flaw. As such, o if running this throws an error during the libinput initialisation, you may need to add input to your user's supplementary groups by running:
sudo usermod -G input -a "${USERNAME}"

Make an event where that detects if a boolean becomes true to display a message

I have a GUI application that works with printers connected via USB. What I've got now is the code to detect whenever a printer was connected to the computer. But what I'm trying to achieve is a simple tray message that displays only when someone plugs the printer.
So far, I was using QTimer, but the problem is that when the printer is still plug the message keeps appearing and that's not what I'm looking to do. With setSingleShot set in True, it does appear once but if I unplug it and plug it again the message won't appear.
What I'm basically looking for is a sort of time-based event but that's constantly checking, like on the OS that whenever you plug your printer it shows a status message only once, not every X seconds. Is there any other type of event I should try to get the result I want?
EDIT: Here's the code that detects the device:
import re, subprocess
DEVICE_RE = re.compile(".+ID\s(?P<id>\w+:\w+)")
# ...
def lsusb():
# A python version of the command 'lsusb' that returns a list of connected usbids
df = subprocess.check_output("lsusb", shell=True).decode('utf-8')
devices = []
for line in df.split('\n'):
info = DEVICE_RE.match(line)
if info:
dinfo = info.groupdict()
devices.append(dinfo['id'])
return devices
# ...
def detect():
usbs = lsusb()
if '0a5f:000a' in usbs:
return True
You could use a statemachine.
state no Printer
state Printer detected
state Printer active
With your QTimer you regularely check if lsusb finds the printer.
If found, you change to detected, open the popup and change to state active.
In state active you regularely check if the Printer still can be found. If not you change back into state no Printer.

Categories

Resources