0

I'm new to python and programming in general and I'm trying to build an algorithm using a state machine implementation by Pytransitions.

I'm trying to find a way to display text on a small external LCD screen connected to an RPi 4 Model B while simultaneously having the program run through 3 states, each displaying a different message.

Here is an example of what I tried, I'm pretty sure what's causing me trouble is win.mainloop(), but I have no idea how to fix it. From my understanding after reading the documentation, I must include it at the end of the code to spawn the window itself, but then the text update upon transition from state to state still don't work as I expected - they simply don't show up at all.

Here is the code for a simplified 3 state system:

from transitions import Machine
from tkinter import *
import time

win = Tk()
intvar = StringVar() #A tkinter string variable for labels
cntdwn = 10
intvar.set = 10

class Identification(object):
    def on_enter_A(self):
        print("We've just entered State A")
        update_window('System Started')
    def on_enter_B(self):
        print("We've just entered State B")
        update_window('Please look into the camera')
    def on_enter_C(self):
        print("We've just entered State C")
        update_window('Face and readings authorized')

def update_window(msg):
    label.configure(text=msg)

def rdy():
    #label= Label(win, text= "Sensors fully calibrated, please blow on the sensor", font=('Times New Roman bold',50))
    label.pack(padx=10, pady=10)
    label.configure(text="Sensors fully calibrated, please blow on the sensor")

def cntdwn_update(secs):
    if (secs):
        label.configure(text="Greetings,booting system & calibrating sensors " +str(secs))
        win.after(1000, cntdwn_update, secs-1)

SBS = Identification()
states = ['A', 'B', 'C'] #Set the states in which the machine operates
transitions = [
    { 'trigger': 'System_Ready', 'source': 'A', 'dest': 'B' },
    { 'trigger': 'Picture_Taken', 'source': 'B', 'dest': 'C' },
    { 'trigger': 'User_Verified', 'source': 'C', 'dest': 'A' }
    ] #Sets the transition parameter string for each state
machine = Machine(SBS, states=states, transitions = transitions, initial='A')

win.geometry("650x250") #Set the geometry
win.attributes('-fullscreen', True) #Create a fullscreen window
intvar.set = "Greetings, calibrating sensor (10s)"
label= Label(win, text= "Greetings, calibrating sensors " + str(cntdwn), font=('Times New Roman bold',50))
label.pack(padx=10, pady=10)
win.after(1000, cntdwn_update, cntdwn-1)
win.after(10000, rdy)
#win.after(12000, update_window, 'Please look into the camera')  #This works but I want this 
to activate when the state switches, not after an a set amount of time.

SBS.System_Ready()
SBS.Picture_Taken()
SBS.User_Verified()
win.mainloop()

What happens is as follows:

The screen displays "System Started" for 1 second, then it displays the "Sensors calibrating" message while self-updating every second for a total of 10 seconds, then it displays "Sensors fully calibrated, please blow on the sensor."

At no point does it print any of the strings attached to the state change event, i.e "Please look into the camera" or "Face and readings authorized".

How do I tackle this?

Any help would be much appreciated!

Suslik
  • 1
  • 1

1 Answers1

0

About a year later you probably figured it out but just in case somebody stumbled upon a similar problem.

These three lines will be called without any delay within a split second before the program hits the GUI's main loop:

SBS.System_Ready()   # shows 'Please look into the camera'
SBS.Picture_Taken()  # shows 'Face and readings authorized'
SBS.User_Verified()  # shows 'System Started'

I am not entirely sure the first two messages are shown at all since the GUI mainloop only starts after the label has been reset three times. But even if it is set, the messages are set for such a short amount of time that they are not actually perceivable.

In a terminal you can see that all three enter state callbacks are entered:

We've just entered State B
We've just entered State C
We've just entered State A

If you plan to change states and want to update prompts you probably want to do state changes and thus label changes in the GUI's main loop and introduce a delay so that prompts are actually perceivable by the user. You could add rdy to Identification:

class Identification(object):

    def on_enter_A(self):
        print("We've just entered State A")
        update_window('System Started')

    def on_enter_B(self):
        print("We've just entered State B")
        update_window('Please look into the camera')

    def on_enter_C(self):
        print("We've just entered State C")
        update_window('Face and readings authorized')

    def rdy(self):
        # label= Label(win, text= "Sensors fully calibrated, please blow on the sensor", font=('Times New Roman bold',50))
        label.pack(padx=10, pady=10)
        label.configure(text="Sensors fully calibrated, please blow on the sensor")
        self.System_Ready()

Then you remove the SBS calls before the main loop and pass win.after(10000, SBS.rdy) instead of just rdy.

You could also decouple the machine's state changes and only check for flags or events in your GUI's update loop. Depending on how heavy your state changes are on the processing side or when they contain blocking delays (not uncommon when working with sensors) decoupling is probably a better idea.

aleneum
  • 2,083
  • 12
  • 29