I have a project where I’m trying to build a simple time-clock using Python 3 on a Raspberry Pi. The Pi is running 64-bit Bullseye.
In my script, I create a window with two columns, one for entry and one for display. The entry side is working (as far as I have gone). The display side sorta works, and this is the issue.
The user will enter their code, press "Enter" to see their information, and then press "In" or "Out" for clocking in or out. When the user presses "In", I want to display a message that says either "Clocked In" or "Already Clocked in".
The issue is that the clocked-in message does not display. The statement that fails is the msg_elem.Update( ...
. If I run the Python debugger, the message is displayed, but not in "normal" running.
My question is What am I doing wrong?
This is a working example…
import sys, os, platform
import PySimpleGUI as sg
from PIL import Image, ImageTk
from time import sleep
import io
#
# Elements
# create the Elements we want to control outside the form
out_elem = sg.Text('', size=(55, 1), font=('Helvetica', 18), text_color='black',justification='center')
in_elem = sg.Input(size=(10,1), do_not_clear=True)
img_elem = sg.Image(size=(240,240),key="-IMAGE-")
msg_elem = sg.Text('', size=(65, 1), font=('Helvetica', 18), text_color='black',justification='center',key='-MSG-')
#
# Columns
button_column = [
[sg.Text("User Input"),in_elem],
[sg.ReadFormButton('1', size=(3,3)),
sg.ReadFormButton('2', size=(3,3)),
sg.ReadFormButton('3', size=(3,3)),
sg.ReadFormButton('Clear', size=(6,3))],
[sg.ReadFormButton('4', size=(3,3)),
sg.ReadFormButton('5', size=(3,3)),
sg.ReadFormButton('6', size=(3,3)),
sg.ReadFormButton('Enter', size=(6,3))],
[sg.ReadFormButton('7', size=(3,3)),
sg.ReadFormButton('8', size=(3,3)),
sg.ReadFormButton('9', size=(3,3)),
sg.ReadFormButton('Quit', size=(6,3))],
[sg.T(''), sg.T(' ' * 8),
sg.ReadFormButton('0', size=(3,3))],
[sg.T('')],
[sg.ReadFormButton('In', size=(13,3)),
sg.ReadFormButton('Out', size=(13,3)),]
]
display_column = [
[sg.Text("User Details")],
[out_elem],
[sg.T(' ' * 30), img_elem],
[msg_elem],
]
#
layout = [
[sg.Column(button_column),
sg.VSeperator(),
sg.Column(display_column,justification='center',vertical_alignment='top')]
]
form = sg.Window('Time Clock', layout, auto_size_buttons=False, size=(800,480))
keys_entered = ''
while True:
button, values = form.Read()
if button is None:
form["-IMAGE-"].update()
out_elem.Update( " " )
break
elif button == 'Clear':
keys_entered = ''
pcpid = ''
empid = ''
form["-IMAGE-"].update()
in_elem.Update(keys_entered)
out_elem.Update( " " )
msg_elem.Update( " " )
elif button in '1234567890':
keys_entered = in_elem.Get()
keys_entered += button
elif button == 'Enter':
keys_entered = '123'
first_name = 'Mike'
last_name = 'Retiredguy'
empid = 12345
im1 = Image.open( 'mike.png' )
im1.thumbnail((240,240))
bio = io.BytesIO()
im1.save( bio, format="PNG")
empimage = bio.getvalue()
form["-IMAGE-"].update( empimage )
dsplAns = f"{empid} - {last_name}, {first_name}"
out_elem.Update( dsplAns )
elif button == 'In':
# import pdb; pdb.set_trace()
sqlAns = 1 # User already clocked in
if sqlAns > 0:
msg_elem.Update( "...is already clocked in! (A)" ) # <=== THIS IS WHAT FAILS
else:
msg_elem.Update( "...is clocked in! (B)" ) # <=== THIS IS WHAT FAILS
sleep(10)
# Clear input
keys_entered = ''
pcpid = ''
empid = ''
form["-IMAGE-"].update()
in_elem.Update(keys_entered)
out_elem.Update( " " )
msg_elem.Update( " " )
elif button == 'Quit':
sys.exit(0)
in_elem.Update(keys_entered)
#
# ###EOF###
I have tried this on the RPi, and on a Virtual system with Debian (not Pi) linux. Both give me the same result. I’ve searched here on Stack Overflow, and Google in general, and I think I’m doing it correctly, but failing.
2
Answers
The method
msg_elem.Update
just update the architecture of PySimpleGUI, not the GUI. Need to callwindow.refresh()
beforesleep(10)
if you want the GUI updated immediately, not until back towindow.read()
.sleep(10)
take long time for GUI to wait, so it will show "Not Responding".Demo Code
Here is a solution based on Jason’s solution that uses the PySimpleGUI timer capability to simplify the code so that threads are not required. As of the date of this post, you’ll need to get the GitHub version of PySimpleGUI to use
Window.timer_start()
andWindow.timer_stop_all()
. You could also use a flag to protect against multiple button clicks if desired.The Demo Program Demo_Window_Timer.py shows how to use this set of API calls in more detail and can be found with the other Demo Programs on GitHub.
The concept is the same as the previous answer. A timer is started when the button is clicked. To handle multiple button clicks, previous timers are canceled when a new button click happens.
The important line of code that starts the timer is:
When the timer expires, you’ll get an event that you can specify when you start the timer, or if none is specified, the default key value will be used. That’s what was done in this example. Detecting the timer has expired is this line:
The complete program…