skip to Main Content

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


  1. The method msg_elem.Update just update the architecture of PySimpleGUI, not the GUI. Need to call window.refresh() before sleep(10) if you want the GUI updated immediately, not until back to window.read(). sleep(10) take long time for GUI to wait, so it will show "Not Responding".

    Demo Code

    from time import sleep
    import threading
    import PySimpleGUI as sg
    
    def func(window, value):
        global running
        message = f'You clicked the button "{value}", this message will be cleared after 3s !'
        window.write_event_value('Update', message)
        sleep(3)
        window.write_event_value('Update', '')
        running = False
    
    sg.set_options(font=('Courier New', 12))
    
    layout = [
        [sg.Button('Hello'), sg.Button('World')],
        [sg.Text('', size=80, key='State')],
    ]
    window = sg.Window('Title', layout)
    running = False
    
    while True:
    
        event, values = window.read()
    
        if event == sg.WIN_CLOSED:
            break
        elif event in ('Hello', 'World') and not running:
            running = True
            threading.Thread(target=func, args=(window, event), daemon=True).start()
        elif event == 'Update':
            message = values[event]
            window['State'].update(message)
    
    window.close()
    
    Login or Signup to reply.
  2. 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() and Window.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:

    window.timer_start(3000, repeating=False)       # start a 3-second timer
    

    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:

        elif event ==  sg.EVENT_TIMER:
    

    The complete program…

    import PySimpleGUI as sg
    
    sg.set_options(font=('Courier New', 12))
    
    layout = [
        [sg.Button('Hello'), sg.Button('World')],
        [sg.Text('', size=80, key='State')],
    ]
    window = sg.Window('Title', layout)
    
    while True:
        event, values = window.read()
    
        if event == sg.WIN_CLOSED:
            break
        elif event in ('Hello', 'World'):
            window.timer_stop_all()                         # stop any previous timers
            window['State'].update(f'You clicked the button "{event}", this message will be cleared after 3s !')
            window.timer_start(3000, repeating=False)       # start a 3-second timer
        elif event ==  sg.EVENT_TIMER:
            window['State'].update('')
    
    window.close()
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search