I tried to find codes online about rising events in my PySimpleGUI program by simple keyboard clicks like ENTER or Ctrl+A for instance but I couldn't just find any about it, I went to the documentation of PySimpleGUI and didn't shut my tap without learning a thing.
Here is a simple code i wrote:
import PySimpleGUI as sg
layout = [[sg.I(key='In'), sg.B('Ok')],[sg.T(enable_events=True,key='T')]]
win=sg.Window("Keyboard Events", layout)
while True:
event, value= win.read()
#close event
if event == sg.WIN_CLOSED:
break
#greeting evnt
if event in ('Ok'): #( 'OK', 'KEYBOARD ENTER EVENT'):
msg = "Hello "+value['In'] # message to show user
win['T'].update(msg) # show user message
win['In'].update("") # clear input field after submitting
win.close()
What should I say to PySimpleGUI for let it run #greeting event when I press the ENTER key? Can someone help me please? Thanks guys!
Good question! And yes, its the documentation, not you. While this may not answer your question directly, here is some useful information.
You can choose to set return_keyboard_events=True as option when creating the window (sg.Window()). This will return every key hitting an input, as well as some scrolling events, meta keys, and so on.
window.read() returns a tuple (event, values). event can be:
None (sg.WIN_CLOSED)
A string of length one with the key from an input. For example 'a'.
The key of a control. For example, 'Ok'.
A tuple with more information. For example, clicking in a table gives a tuple of (keyname, "+CLICKED+", (row, column)
A special key, such as Return:6033979789 or Meta_L:922810622.
I use this as an alternative to bind_return_key when I might have multiple text entries on the screen:
if isinstance(event, str) and event.startswith('Return'):
event = 'return ' + self.window.find_element_with_focus().key
Also, I find printing to the console is necessary to debug events:
event, values = self.window.read()
print(f" event (type:{type(ui_event)}, {ui_event}, len:({len(ui_event)})"
f" with values {ui_values}")
Option required for element
sg.Input(do_not_clear=False, key='In') clear input field after event
sg.Button('Ok', bind_return_key=True) the return key will cause this button to be pressed
import PySimpleGUI as sg
layout = [[sg.I(do_not_clear=False, key='In'), sg.B('Ok', bind_return_key=True)],[sg.T(enable_events=True,key='T')]]
win=sg.Window("Keyboard Events", layout)
while True:
event, value= win.read()
#close event
if event == sg.WIN_CLOSED:
break
#greeting evnt
if event == 'Ok':
msg = "Hello "+value['In'] # message to show user
win['T'].update(msg) # show user message
win.close()
Related
community. I'm trying to put together a quick hotkey script in python here.
For some reason it doesn't react to function keys, meaning the expression '<ctrl>+<F2>': function_1 doesn't work.
I was not able to find any clues in the official documentation or other examples online. Any thoughts?
Here is the script for testing.
from pynput import keyboard
def function_1():
print('Function 1 activated')
def function_2():
print('Function 2 activated')
with keyboard.GlobalHotKeys({
'<ctrl>+<F2>': function_1,
'<ctrl>+t': function_2}) as h:
h.join()
You could try setting up a generic handler to see what events are generated:
from pynput import keyboard
from pynput.keyboard import Controller
keyboard_controller = Controller()
# The event listener will be running in this block
with keyboard.Events() as events:
for event in events:
if event.key == keyboard.Key.esc:
break
else:
print(f"Received event {event}")
Running this script will then print the key press events, pressing Esc will exit the script:
Received event Press(key='a')
Received event Release(key='a')
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Press(key=Key.f3)
Received event Release(key=Key.f3)
Received event Press(key=Key.ctrl)
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Release(key=Key.ctrl)
Note that this won't handle hot keys and I don't see some key presses, e.g. to get Key.media_volume_up events I press Fn + F3.
You could potentially use keyboard.Events() handling to trigger your own hotkey equivalent.
I solved this issue by moving away from pynput Global Hotkeys and using just keyboard instead. I still don't understand the nature of this issue with global hotkeys not recognizing F1 key..
Here is the solution I used. I also added the passthrough of values with lambda function for each hotkey.
import keyboard
def func1(key):
print("F1 is pressed with value: ", key)
def func2(key):
print("F2 is pressed with value: ", key)
# define hotkeys
keyboard.add_hotkey('F1', lambda: func1("value1"))
keyboard.add_hotkey('F2', lambda: func2("value2"))
# run the program until 'F12' is pressed
keyboard.wait('F12')
My Tkinter GUI won't appear because of loops, how can i make the GUI appear without disturbing the flow of the loops? Here's the code.
for record in data:
# The rest of the code...
# Datetime and auto validation for web automation
while True:
convert_time_record = datetime.datetime.strptime(record[2], '%H:%M')
date_now = datetime.datetime.now()
time_val = record[0] == date_now.strftime('%A') and convert_time_record.strftime('%H:%M:%S') == date_now.strftime('%H:%M:%S')
if time_val and record[3] == "Yes" and record[4] == "Link":
print("time true")
chrome.open(record[5])
time.sleep(5)
os.system("taskkill /im chrome.exe /f")
break
# Check if the method was by meeting ID
elif time_val and record[3] == "Yes" and record[4] == "Meeting ID":
# Open Zoom
subprocess.call("C:\\Users\\bryan\\AppData\\Roaming\\Zoom\\bin\\Zoom.exe")
time.sleep(3)
# Locate the center of the join button then move the cursor
Click('join_button.png')
time.sleep(3)
# Write the meeting id to the text field
pyautogui.write(record[5])
# Press the enter key
pyautogui.press('enter')
time.sleep(3)
# Write the passcode to the text field
pyautogui.write(record[6])
# Press the enter key
pyautogui.press('enter')
time.sleep(8)
join_computer_audio_btn = pyautogui.locateCenterOnScreen('join_audio.png')
pyautogui.moveTo(join_computer_audio_btn)
pyautogui.click()
root.mainloop()
I've tried putting root.mainloop() inside the for-loop and while-loop but that will make the loops unable to execute the function.
You should run root.mainloop() only once.
Using loops prevents the GUI from updating, therefore consider using a different thread to run the loop and send updates to the GUI from the other thread, or use an object for maintaining the state of the loop and use a non blocking function to get the state of the object and have it update and re-set the call with the after function of tkinter.
Edit: The below answer to use keyboard.on_press(callback, suppress=False) works fine in ubuntu without any issues.
But in Redhat/Amazon linux, it fails to work.
I have used the code snippet from this thread
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('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
break # if user pressed a key other than the given key the loop will break
But the above code requires the each iteration to be executed in nano-seconds. It fails in the below case:
import keyboard # using module keyboard
import time
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
print("sleeping")
time.sleep(5)
print("slept")
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
print("#######")
break # if user pressed a key other than the given key the loop will break
You can make use of event handlers in keyboard module to achieve the desired result.
One such handler is keyboard.on_press(callback, suppress=False):
Which invokes a callback for every key_down event.
You can refer more at keyboard docs
Here is the code you can try:
import keyboard # using module keyboard
import time
stop = False
def onkeypress(event):
global stop
if event.name == 'q':
stop = True
# ---------> hook event handler
keyboard.on_press(onkeypress)
# --------->
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
print("sleeping")
time.sleep(5)
print("slept")
if stop: # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
except:
print("#######")
break # if user pressed a key other than the given key the loop will break
for people that might need this in the future, you can use keyboard.wait() which will basically wait untill the key gets pressed
keyboard.wait("o")
print("you pressed the letter o")
Do keep in mind that it blocks code execution after it. if you want to run code if the key is not being pressed i'd suggest doing
if keyboard.is_pressed("0"):
#do stuff
else:
#do other stuff
Edit: never mind, the other answer uses pretty much the same approach
This is what i could come up with, using the same "keyboard" module, see in-code comments below
import keyboard, time
from queue import Queue
# keyboard keypress callback
def on_keypress(e):
keys_queue.put(e.name)
# tell keyboard module to tell us about keypresses via callback
# this callback happens on a separate thread
keys_queue = Queue()
keyboard.on_press(on_keypress)
try:
# run the main loop until some key is in the queue
while keys_queue.empty():
print("sleeping")
time.sleep(5)
print("slept")
# check if its the right key
if keys_queue.get()!='q':
raise Exception("terminated by bad key")
# still here? good, this means we've been stoped by the right key
print("terminated by correct key")
except:
# well, you can
print("#######")
finally:
# stop receiving the callback at this point
keyboard.unhook_all()
You could use a thread
import threading
class KeyboardEvent(threading.Thread):
def run(self):
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
keyread = KeyboardEvent()
keyread.start()
This would run in parallel to anything in the main thread and be dedicated to listening for that key press essentially.
So bassicaly the user will press the picture_pin buton first, then this calls the picture_taking() function which then it should stop and wait for either (Accept_Pin or Decline_Pin) buttons to be pressed, it should not let the user to continue unless a selection is made. so when the user makes his/her selection then it go back and wait for the picture_pin button. at this stage the Accept_Pin and Decline_Pin should have no affect at all.
I have a python program that waits for a button press from the user then it runs a function to do its thing. what I would like to accomplish is, in that function I would like to also wait for another button press.
(Main.py)
----etc
## PICTURE FUNCTION ##
def picture_taking():
.....etc
returnvalue = subprocess.check_output("sudo ./" + config.layout + ".sh", shell=True)
GPIO.wait_for_edge(Accept_pin, GPIO.FALLING)
GPIO.wait_for_edge(Decline_Pin, GPIO.FALLING)
pad1alreadyPressed = False
pad4alreadyPressed = False
while True:
pad1pressed = not GPIO.input(Accept_pin)
pad4pressed = not GPIO.input(Decline_Pin)
if pad1pressed and not pad1alreadyPressed:
print "Accepted photo:" + str(returnvalue)
pad1alreadyPressed = pad1pressed
if pad4pressed and not pad4alreadyPressed:
print "Declined photo:" + str(returnvalue)
pad4alreadyPressed = pad4pressed
(This here is my Main Program)
#Main section
while True:
time.sleep(0.2)
#camera.hflip = false
camera.shutter_speed = 2000000000
camera.exposure_mode = 'off'
#camera.iso = 1000
camera.preview_fullscreen = True
camera.preview_alpha = 150
camera.start_preview()
GPIO.wait_for_edge(picture_pin, GPIO.FALLING)
picture_taking()
So in the picture_taking() function I would like to ask the user if they accept this picture or not
if they press button (GPIO 19) then they accept or (GPIO 6) as Decline
after they do there selection the program should go back and wait for the main button to select below. and these two buttons should only be selectable inside the function.
I tried this in the Picture_taking() function
when I make the selection it only accepts the "Decline_pin" button, but after I press the "Decline button" then it reads "Accept button".
The other issue is that it does not go back and wait for the Main button "picture_pin" to be pressed. it seems to be stuck here and not exiting.
I am not sure if this is something to do with Indent stuff in Python.
Thank you.
(This should be a comment, not an answer but I don't have reputation 50 yet).
Is this your actual code? I can't understand how it ever returns from the while True: clause in picture_taking(). I'd think you'd need to have something like
while (!(pad1alreadyPressed or pad4alreadyPressed)):
How can you send cursor movements like the up,down,left,right keys with pexpect. The example below is to automate elinks which uses the up/down keys to select different links on a page.
from pexpect import spawn
child = spawn('elinks http://python.org')
#what goes here to send down key
child.interact()
The below script has the codes for all the four cursor movements, with an example of how one might use it in pexpect. To discover the exact string sequences for any typed in text you may use the get_keys.py script below.
KEY_UP = '\x1b[A'
KEY_DOWN = '\x1b[B'
KEY_RIGHT = '\x1b[C'
KEY_LEFT = '\x1b[D'
child.sendline(KEY_DOWN * 5) #send five key downs
get_keys.py
import curses
screen = curses.initscr()
screen.addstr("Press any set of keys then press enter\n")
keys = ''
while True:
event = screen.getkey()
if event == "\n":
break
keys += event
curses.endwin()
print repr(keys)
How about using escape sequence for up(^[[A) or down(^[[B) like this.
child.send("\033[A") # up
child.send("\033[B") # down
try this send '\033\117\102' for down key