Coverage for /home/pi/Software/model-railway-signalling/model_railway_signals/library/track_sensors.py: 97%
54 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 16:40 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 16:40 +0100
1#---------------------------------------------------------------------------------------------------
2# This module is used for creating and managing track sensor library objects on the canvas
3#---------------------------------------------------------------------------------------------------
4#
5# External API - classes and functions (used by the Schematic Editor):
6#
7# track_sensor_callback_type (tells the calling program what has triggered the callback):
8# track_sensor_callback_type.sensor_triggered (the track sensor has been triggered)
9#
10# create_track_sensor - Creates a track sensior and returns a "tag" for all tkinter canvas drawing objects
11# This allows the editor to move the track sensor object on the schematic as required
12# Mandatory Parameters:
13# Canvas - The Tkinter Drawing canvas on which the track sensor is to be displayed
14# sensor_id:int - The unique ID for the track sensor
15# x:int, y:int - Position of the point on the canvas (in pixels)
16# callback - the function to call on track sensor triggered events
17#
18# track_sensor_exists(sensor_id:int) - returns true if the if a track sensor object 'exists'
19#
20# delete_track_sensor(sensor_id:int) - To delete the specified track sensor from the schematic
21#
22# Classes and functions used by the other library modules:
23#
24# track_sensor_triggered (sensor_id:int, callback_type=None) - Called on gpio_sensor trigger
25# events if the gpio_sensor has been configured to generate a "track sensor passed" event
26#
27#---------------------------------------------------------------------------------------------------
29import enum
30import logging
31import tkinter as Tk
32from . import common
34#---------------------------------------------------------------------------------------------------
35# Public API classes (to be used by external functions)
36#---------------------------------------------------------------------------------------------------
38class track_sensor_callback_type(enum.Enum):
39 sensor_triggered = 61 # The sensor has been triggered (by the user or an GPIO sensor)
41#---------------------------------------------------------------------------------------------------
42# Track Sensors are maintained in a global dictionary (with a key of 'sensor_id')
43# Each dictionary entry (representing a track sensor) is a dictionary of key-value pairs:
44# 'canvas' - The tkinter canvas (that the drawing objects are created on)
45# 'callback' - The callback function to make on track sensor triggered events
46# 'button' - A reference to the Tkinter Button object (to simulate 'sensor triggered' events)
47# 'tags' - The tags applied to all canvas drawing objects for the Track Sensor instance
48#---------------------------------------------------------------------------------------------------
50track_sensors = {}
52#---------------------------------------------------------------------------------------------------
53# API Function to check if a Track Sensor library object exists (in the dictionary of Track Sensors)
54#---------------------------------------------------------------------------------------------------
56def track_sensor_exists(sensor_id:int):
57 if not isinstance(sensor_id, int):
58 logging.error("Track Sensor "+str(sensor_id)+": track_sensor_exists - Sensor ID must be an integer")
59 sensor_exists = False
60 else:
61 sensor_exists = str(sensor_id) in track_sensors.keys()
62 return (sensor_exists)
64#---------------------------------------------------------------------------------------------------
65# API function called from the gpio_sensors module on GPIO trigger events (if a gpio sensor has
66# been configured to raise "track sensor passed" events). Also used internally to track sensor
67# button press events (to simulate the "track sensor passed" event).
68#---------------------------------------------------------------------------------------------------
70def track_sensor_triggered (sensor_id:int, callback_type=None):
71 if not isinstance(sensor_id, int):
72 logging.error("Track Sensor "+str(sensor_id)+": track_sensor_triggered - Sensor ID must be an integer")
73 elif not track_sensor_exists(sensor_id):
74 logging.error("Track Sensor "+str(sensor_id)+": track_sensor_triggered - Sensor ID does not exist")
75 else:
76 logging.info("Track Sensor "+str(sensor_id)+": Track Sensor Passed Event ****************************************")
77 # Pulse the button to provide a visual indication (but not if a shutdown has been initiated)
78 if not common.shutdown_initiated: 78 ↛ 82line 78 didn't jump to line 82, because the condition on line 78 was never false
79 track_sensors[str(sensor_id)]["button"].config(bg="red")
80 common.root_window.after(1000,lambda:reset_sensor_button(sensor_id))
81 # Make the external callback specified for the track sensor
82 callback = track_sensors[str(sensor_id)]["callback"]
83 callback(sensor_id, track_sensor_callback_type.sensor_triggered)
84 return ()
86def reset_sensor_button (sensor_id:int):
87 if track_sensor_exists(sensor_id): track_sensors[str(sensor_id)]["button"].config(bg=common.bgraised)
89#---------------------------------------------------------------------------------------------------
90# API Function to create a Track Sensor library object on the schematic
91#---------------------------------------------------------------------------------------------------
93def create_track_sensor(canvas, sensor_id:int, x:int, y:int, callback):
94 global track_sensors
95 # Set a unique 'tag' to reference the tkinter drawing objects
96 canvas_tag = "sensor"+str(sensor_id)
97 if not isinstance(sensor_id, int) or sensor_id < 1:
98 logging.error("Track Sensor "+str(sensor_id)+": create_track_sensor - Sensor ID must be a positive integer")
99 elif track_sensor_exists(sensor_id):
100 logging.error("Track Sensor "+str(sensor_id)+": create_track_sensor - Sensor ID already exists")
101 else:
102 logging.debug("Track Sensor "+str(sensor_id)+": Creating library object on the schematic")
103 # Create the new drawing objects (tagged with the canvas_tag) - the oval is to give us
104 # a reasonable selection area when we subsequently get the bbox of the tagged objects
105 sensor_button = Tk.Button(canvas, text="O", padx=1, pady=1, font=('Courier',2,"normal"))
106 sensor_button.config(command=lambda:track_sensor_triggered(sensor_id)) 106 ↛ exitline 106 didn't run the lambda on line 106
107 canvas.create_window(x, y, window=sensor_button, tags=canvas_tag)
108 canvas.create_oval(x-15, y-15, x+15, y+15, outline="grey60", tags=canvas_tag)
109 # Store the details of the Track Sensor Object in the dictionary of Track Sensors
110 track_sensors[str(sensor_id)] = {}
111 track_sensors[str(sensor_id)]['canvas'] = canvas
112 track_sensors[str(sensor_id)]['button'] = sensor_button
113 track_sensors[str(sensor_id)]['callback'] = callback
114 track_sensors[str(sensor_id)]['tags'] = canvas_tag
115 # Return the canvas_tag for the tkinter drawing objects
116 return(canvas_tag)
118#---------------------------------------------------------------------------------------------------
119# Function to delete a Track Sensor library object from the schematic
120#---------------------------------------------------------------------------------------------------
122def delete_track_sensor(sensor_id:int):
123 global track_sensors
124 if not isinstance(sensor_id, int):
125 logging.error("Track Sensor "+str(sensor_id)+": delete_track_sensor - Sensor ID must be an integer")
126 elif not track_sensor_exists(sensor_id):
127 logging.error("Track Sensor "+str(sensor_id)+": delete_track_sensor - Sensor ID does not exist")
128 else:
129 logging.debug("Track Sensor "+str(sensor_id)+": Deleting library object from the schematic")
130 # Delete all tkinter drawing objects
131 track_sensors[str(sensor_id)]['canvas'].delete(track_sensors[str(sensor_id)]["tags"])
132 track_sensors[str(sensor_id)]['button'].destroy()
133 # Delete the track sensor entry from the dictionary of track sensors
134 del track_sensors[str(sensor_id)]
135 return()
137#####################################################################################################