Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import time
import warnings

from statemachine import State, StateMachine

from behavior_generation_lecture_python.finite_state_machine.traffic_light_visualization import (
VisualizeTrafficLights,
)


class TrafficLightStateMachine(StateMachine):
"""A traffic light machine"""
"""A traffic light machine with non-blocking visualization"""

veh_green = State("veh_green", initial=True)
veh_yellow = State("veh_yellow")
Expand All @@ -33,40 +29,41 @@ def __init__(self, start_mainloop=True):
if start_mainloop:
self.visu.mainloop()

def _schedule(self, delay_sec, callback):
"""Helper to schedule an event without blocking the UI"""
self.visu.windows.after(int(delay_sec * 1000), callback)

def on_enter_veh_yellow(self):
self.visu.pedestrian_press_red()
time.sleep(3)
# Wait 3s before turning yellow (simulating reaction/delay phase)
self._schedule(3.0, self._phase_turn_yellow)

def _phase_turn_yellow(self):
self.visu.vehicle_prepare_to_stop()
time.sleep(3)
self.send("veh_stop")
# Wait 3s before stopping
self._schedule(3.0, lambda: self.send("veh_stop"))

def on_enter_veh_red_ped_red_1(self):
self.visu.vehicle_stop()
time.sleep(2)
self.send("ped_go")
self._schedule(2.0, lambda: self.send("ped_go"))

def on_enter_veh_red_ped_green(self):
self.visu.pedestrian_go()
time.sleep(5)
self.send("ped_stop")
self._schedule(5.0, lambda: self.send("ped_stop"))

def on_enter_veh_red_ped_red_2(self):
self.visu.pedestrian_stop()
time.sleep(5)
self.send("veh_prepare_to_go")
self._schedule(5.0, lambda: self.send("veh_prepare_to_go"))

def on_enter_veh_red_yellow(self):
self.visu.vehicle_prepare_to_go()
time.sleep(2)
self.send("veh_go")
self._schedule(2.0, lambda: self.send("veh_go"))

def on_enter_veh_green(self):
self.visu.vehicle_go()

def button_press(self, event):
if self.state == "veh_green":
if self.current_state.id == "veh_green":
self.send("veh_prepare_to_stop")
else:
warnings.warn(
"The button only has effect if currently vehicles have green."
)
warnings.warn("The button only works when vehicles have green.")
Original file line number Diff line number Diff line change
@@ -1,115 +1,117 @@
import tkinter as tk


class VisualizeTrafficLights:
def __init__(self):
self.windows = tk.Tk()
self.windows.title("Traffic Light FSM")
self.canvas = tk.Canvas(self.windows)

self.vehicle_red = self.__create_circle(55, 55, 50)
self.vehicle_yellow = self.__create_circle(55, 165, 50)
self.vehicle_green = self.__create_circle(55, 275, 50)
self.pedestrian_red = self.__create_circle(180, 55, 50)
self.pedestrian_green = self.__create_circle(180, 165, 50)
self.pedestrian_press_button = self.canvas.create_rectangle(140, 260, 220, 290)
self.pedestrian_press_label = self.canvas.create_text(180, 275, text="Press")
# self.pedestrian_press = tk.Button(text="Press", command=pedestrian_press_fun)
# self.canvas.create_window(180, 265, window=self.pedestrian_press)

self.__pedestrian_press_white()

self.canvas.config(width=400, height=400)
self.canvas.pack()
self.windows.configure(bg="#2c3e50") # Dark background for window

# Canvas settings
self.width = 400
self.height = 400
self.canvas = tk.Canvas(
self.windows,
width=self.width,
height=self.height,
bg="#2c3e50",
highlightthickness=0
)
self.canvas.pack(padx=20, pady=20)

# Traffic Light Colors
self.colors = {
"off_red": "#4a0000", "on_red": "#ff3b30",
"off_yellow": "#4a4a00", "on_yellow": "#ffcc00",
"off_green": "#002a00", "on_green": "#4cd964",
"housing": "#1a1a1a",
"text": "#ecf0f1"
}

# Draw Housing (Background Boxes)
# Vehicle Light Housing
self._draw_housing(50, 20, 160, 360)
# Pedestrian Light Housing
self._draw_housing(240, 20, 350, 240)

# Labels
self.canvas.create_text(105, 375, text="Vehicle", fill=self.colors["text"], font=("Helvetica", 12, "bold"))
self.canvas.create_text(295, 255, text="Pedestrian", fill=self.colors["text"], font=("Helvetica", 12, "bold"))

# Create Lights
self.vehicle_red = self.__create_circle(105, 80, 40, fill=self.colors["off_red"])
self.vehicle_yellow = self.__create_circle(105, 190, 40, fill=self.colors["off_yellow"])
self.vehicle_green = self.__create_circle(105, 300, 40, fill=self.colors["off_green"])

self.pedestrian_red = self.__create_circle(295, 80, 40, fill=self.colors["off_red"])
self.pedestrian_green = self.__create_circle(295, 190, 40, fill=self.colors["off_green"])

# Pedestrian Button
self.pedestrian_press_button = self.canvas.create_rectangle(
240, 280, 350, 320,
fill="#95a5a6", outline="", width=0
)
self.pedestrian_press_label = self.canvas.create_text(
295, 300,
text="PRESS BUTTON",
fill="#2c3e50",
font=("Helvetica", 10, "bold")
)

self.__update()

def _draw_housing(self, x1, y1, x2, y2):
r = 15 # radius for rounded corners effect
self.canvas.create_rectangle(x1, y1, x2, y2, fill=self.colors["housing"], outline="black", width=2)

def __create_circle(self, x, y, r, fill=None):
x0 = x - r
y0 = y - r
x1 = x + r
y1 = y + r
return self.canvas.create_oval(x0, y0, x1, y1, fill=fill)
return self.canvas.create_oval(x - r, y - r, x + r, y + r, fill=fill, outline="black", width=2)

def __update(self):
self.canvas.update_idletasks()
self.canvas.update()
self.windows.update_idletasks()
self.windows.update()

def vehicle_go(self):
self.canvas.itemconfig(self.vehicle_red, fill="black")
self.canvas.itemconfig(self.vehicle_yellow, fill="black")
self.canvas.itemconfig(self.vehicle_green, fill="green")
self.canvas.itemconfig(self.pedestrian_red, fill="red")
self.canvas.itemconfig(self.pedestrian_green, fill="black")

# Helper to set lights easily
def _set_lights(self, v_red, v_yel, v_grn, p_red, p_grn):
self.canvas.itemconfig(self.vehicle_red, fill=self.colors["on_red"] if v_red else self.colors["off_red"])
self.canvas.itemconfig(self.vehicle_yellow, fill=self.colors["on_yellow"] if v_yel else self.colors["off_yellow"])
self.canvas.itemconfig(self.vehicle_green, fill=self.colors["on_green"] if v_grn else self.colors["off_green"])

self.canvas.itemconfig(self.pedestrian_red, fill=self.colors["on_red"] if p_red else self.colors["off_red"])
self.canvas.itemconfig(self.pedestrian_green, fill=self.colors["on_green"] if p_grn else self.colors["off_green"])
self.__update()

def vehicle_prepare_to_stop(self):
self.canvas.itemconfig(self.vehicle_red, fill="black")
self.canvas.itemconfig(self.vehicle_yellow, fill="yellow")
self.canvas.itemconfig(self.vehicle_green, fill="black")
self.canvas.itemconfig(self.pedestrian_red, fill="red")
self.canvas.itemconfig(self.pedestrian_green, fill="black")
def vehicle_go(self):
self._set_lights(False, False, True, True, False)

self.__update()
def vehicle_prepare_to_stop(self):
self._set_lights(False, True, False, True, False)

def vehicle_stop(self):
self.canvas.itemconfig(self.vehicle_red, fill="red")
self.canvas.itemconfig(self.vehicle_yellow, fill="black")
self.canvas.itemconfig(self.vehicle_green, fill="black")
self.canvas.itemconfig(self.pedestrian_red, fill="red")
self.canvas.itemconfig(self.pedestrian_green, fill="black")

self.__update()
self._set_lights(True, False, False, True, False)

def pedestrian_go(self):
self.__pedestrian_press_white()

self.canvas.itemconfig(self.vehicle_red, fill="red")
self.canvas.itemconfig(self.vehicle_yellow, fill="black")
self.canvas.itemconfig(self.vehicle_green, fill="black")
self.canvas.itemconfig(self.pedestrian_red, fill="black")
self.canvas.itemconfig(self.pedestrian_green, fill="green")

self.__update()
self.__pedestrian_press_reset()
self._set_lights(True, False, False, False, True)

def pedestrian_stop(self):
self.canvas.itemconfig(self.vehicle_red, fill="red")
self.canvas.itemconfig(self.vehicle_yellow, fill="black")
self.canvas.itemconfig(self.vehicle_green, fill="black")
self.canvas.itemconfig(self.pedestrian_red, fill="red")
self.canvas.itemconfig(self.pedestrian_green, fill="black")

self.__update()
self._set_lights(True, False, False, True, False)

def vehicle_prepare_to_go(self):
self.canvas.itemconfig(self.vehicle_red, fill="red")
self.canvas.itemconfig(self.vehicle_yellow, fill="yellow")
self.canvas.itemconfig(self.vehicle_green, fill="black")
self.canvas.itemconfig(self.pedestrian_red, fill="red")
self.canvas.itemconfig(self.pedestrian_green, fill="black")

self.__update()
self._set_lights(True, True, False, True, False)

def pedestrian_press_red(self):
self.canvas.itemconfig(self.pedestrian_press_button, fill="red")

self.canvas.itemconfig(self.pedestrian_press_button, fill="#e74c3c") # Red button
self.canvas.itemconfig(self.pedestrian_press_label, fill="white")
self.__update()

def __pedestrian_press_white(self):
self.canvas.itemconfig(self.pedestrian_press_button, fill="white")

def __pedestrian_press_reset(self):
self.canvas.itemconfig(self.pedestrian_press_button, fill="#95a5a6") # Grey button
self.canvas.itemconfig(self.pedestrian_press_label, fill="#2c3e50")
self.__update()

def mainloop(self):
self.windows.mainloop()

def register_button_event(self, button_press_function):
self.canvas.tag_bind(
self.pedestrian_press_button, "<Button-1>", button_press_function
)
self.canvas.tag_bind(
self.pedestrian_press_label, "<Button-1>", button_press_function
)
self.canvas.tag_bind(self.pedestrian_press_button, "<Button-1>", button_press_function)
self.canvas.tag_bind(self.pedestrian_press_label, "<Button-1>", button_press_function)