Coverage for /home/pi/Software/model-railway-signalling/model_railway_signals/editor/objects/objects_common.py: 99%
73 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 17:29 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 17:29 +0100
1#------------------------------------------------------------------------------------
2# This module contains all the common internal functions for managing layout objects
3#------------------------------------------------------------------------------------
4#
5# External API functions intended for use by other editor modules:
6#
7# initialise (canvas,width,height,grid) - Initialise the objects package and set defaults
8# update_canvas(width,height,grid) - update the attributes (on layout load or canvas re-size)
9# set_bbox - Common function to create/update the boundary box for a schematic object
10# find_initial_canvas_position - common function to return the next 'free' position (x,y)
11# new_item_id - Common function - common function to return the next 'free' item ID
12#
13# section_exists (item_id:int) - Common function to see if a given item exists #####################
14# line_exists (item_id:int) - Common function to see if a given item exists ########################
15#
16# signal(item_id:int) - helper function to find the object Id by Item ID
17# point(item_id:int) - helper function to find the object Id by Item ID
18# section(item_id:int) - helper function to find the object Id by Item ID
19# instrument(item_id:int) - helper function to find the object Id by Item ID
20# line(item_id:int) - helper function to find the object Id by Item ID
21# track_sensor(item_id:int) - helper function to find the object Id by Item ID
22#
23# Objects intended to be accessed directly by other editor modules:
24#
25# canvas - global reference to the Tkinter drawing object
26# object_type - Enumeration type for the supported objects
27# schematic_objects - for accessing/editing the configuration of an object
28# canvas_width, canvas_height, canvas_grid - for creating/pasting objects
29# canvas - global reference to the Tkinter drawing object
30#
31# signal_index - for iterating through all the signal objects
32# point_index - for iterating through all the point objects
33# instrument_index - for iterating through all the instrument objects
34# section_index - for iterating through all the section objects
35# line_index - for iterating through all the line objects
36# track_sensor_index - for iterating through all the sensor objects
37#
38# Makes the following external API calls to other editor modules:
39#
40# run_layout.initialise(canvas) - Initialise the run_layout module with the canvas reference
41#
42#------------------------------------------------------------------------------------
44from .. import run_layout
46#------------------------------------------------------------------------------------
47# Global class used for the object_type - we use normal strings rather than enumeration
48# types so we can easily serialise/deserialise to/from json for save and load
49#------------------------------------------------------------------------------------
51class object_type():
52 none:str = "none"
53 textbox:str = "textbox"
54 line:str = "line"
55 point:str = "point"
56 signal:str = "signal"
57 section:str = "section"
58 instrument:str = "instrument"
59 track_sensor:str = "tracksensor"
61#------------------------------------------------------------------------------------
62# All Objects we create (and their configuration) are stored in a global dictionary
63# and are indexed by their UUID (object_id) - which is assigned at creation time
64#------------------------------------------------------------------------------------
66schematic_objects:dict={}
68#------------------------------------------------------------------------------------
69# We also maintain seperate indexes for each of the object types to enable the unique
70# object_id to be indexed by the item_id (unique only for each object_type)
71#------------------------------------------------------------------------------------
73signal_index:dict={}
74point_index:dict={}
75instrument_index:dict={}
76section_index:dict={}
77line_index:dict={}
78track_sensor_index:dict={}
80#------------------------------------------------------------------------------------
81# Helper functions to get the main dictionary index (the object_id) from the item_id
82#------------------------------------------------------------------------------------
84def signal(ID:int): return (signal_index[str(ID)])
85def point(ID:int): return (point_index[str(ID)])
86def instrument(ID:int): return (instrument_index[str(ID)])
87def section(ID:int): return (section_index[str(ID)])
88def line(ID:int): return (line_index[str(ID)]) 88 ↛ exitline 88 didn't return from function 'line', because the return on line 88 wasn't executed
89def track_sensor(ID:int): return (track_sensor_index[str(ID)])
91#------------------------------------------------------------------------------------
92# Simple functions to test if a particular item_id already exists (for an item_type)
93#------------------------------------------------------------------------------------
95def section_exists(ID:int): return (str(ID) in section_index.keys()) ####################
96def line_exists(ID:int): return (str(ID) in line_index.keys()) ##########################
98#------------------------------------------------------------------------------------
99# Common parameters for a Default Layout Object (i.e. state at creation)
100# These elements are common to all schematic layout objects and are primarily
101# used to support the schematic editor functions (move, select, etc)
102#------------------------------------------------------------------------------------
104default_object = {}
105default_object["item"] = object_type.none
106default_object["posx"] = 0
107default_object["posy"] = 0
108default_object["itemid"] = 0
109default_object["bbox"] = None # Tkinter canvas object for the boundary box
110default_object["tags"] = "" # Canvas Tags (for moving/deleting objects)
112#------------------------------------------------------------------------------------
113# Function to set the required defaults for the Objects package at application start
114# The Tkinter Canvas Object and default canvas attributes (dimentions and grid size)
115# are saved as global variables for easy referencing. The Canvas width, height and grid
116# are used for optimising the positioning of objects on creation or 'paste'
117# Also calls the run_layout.initialise function to set the tkinter canvas object
118#------------------------------------------------------------------------------------
120canvas = None
121canvas_width = 0
122canvas_height = 0
123canvas_grid = 0
125def initialise (canvas_object, width:int, height:int, grid:int):
126 global canvas
127 canvas = canvas_object
128 update_canvas(canvas_width, canvas_height, grid)
129 run_layout.initialise(canvas)
130 return()
132#------------------------------------------------------------------------------------
133# Function to update the Canvas Attributes (following layout load or canvas resize)
134#------------------------------------------------------------------------------------
136def update_canvas(width:int, height:int, grid:int):
137 global canvas_width, canvas_height, canvas_grid
138 canvas_width = width
139 canvas_height = height
140 canvas_grid = grid
141 return()
143#------------------------------------------------------------------------------------
144# Internal function to create/update the boundary box rectangle for an object.
145# Note that we create the boundary box slightly bigger than the object itself
146#------------------------------------------------------------------------------------
148def set_bbox(object_id:str,bbox:[int,int,int,int]):
149 global schematic_objects
150 x1, y1 = bbox[0] - 2, bbox[1] - 2
151 x2, y2 = bbox[2] + 2, bbox[3] + 2
152 # If the tkinter object exists we leave it in its current selected/unselected state
153 # If it doesn't exist then we create it (in the default unselected state)
154 if schematic_objects[object_id]["bbox"]:
155 canvas.coords(schematic_objects[object_id]["bbox"],x1,y1,x2,y2)
156 else:
157 schematic_objects[object_id]["bbox"] = canvas.create_rectangle(x1,y1,x2,y2,state='hidden')
158 return()
160#------------------------------------------------------------------------------------
161# Internal function to find an initial canvas position for the created object.
162# This is used by all the object type-specific creation functions (below).
163#------------------------------------------------------------------------------------
165def find_initial_canvas_position():
166 global schematic_objects
167 # Default position (top left) to try first and Deltas to use for object spacing
168 startx, starty = 75, 50
169 deltax, deltay = 25, 50
170 # Find an intial position not taken up with an existing object
171 x, y = startx, starty
172 while True:
173 posfree = True
174 for object_id in schematic_objects:
175 # See if another object already exists at this position
176 if (schematic_objects[object_id]["posx"] == x and
177 schematic_objects[object_id]["posy"] == y):
178 posfree = False
179 break
180 # If the current x/y position is "free" now have iterated through all other
181 # schematic objects then we can use this position to create the new object
182 if posfree: break
183 # Else, apply the deltas and try again
184 x, y = x + deltax, y + deltay
185 # Take into account the size of the canvas (so nothing gets created "off scene"
186 if y > canvas_height - 50: y = starty
187 return(x, y)
189#------------------------------------------------------------------------------------
190# Internal function to assign a unique type-specific id for a newly created object
191# This function is called on object creation or object copy/paste and takes in the
192# function to call to see if the Item_ID already exists for a specific item type
193# This is used by all the object type-specific creation functions.
194#------------------------------------------------------------------------------------
196def new_item_id(exists_function):
197 item_id = 1
198 while True:
199 if not exists_function(item_id): break
200 item_id += 1
201 return(item_id)
203####################################################################################