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

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#------------------------------------------------------------------------------------ 

43 

44from .. import run_layout 

45 

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#------------------------------------------------------------------------------------ 

50 

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" 

60 

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#------------------------------------------------------------------------------------ 

65 

66schematic_objects:dict={} 

67 

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#------------------------------------------------------------------------------------ 

72 

73signal_index:dict={} 

74point_index:dict={} 

75instrument_index:dict={} 

76section_index:dict={} 

77line_index:dict={} 

78track_sensor_index:dict={} 

79 

80#------------------------------------------------------------------------------------ 

81# Helper functions to get the main dictionary index (the object_id) from the item_id 

82#------------------------------------------------------------------------------------ 

83 

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)]) 

90 

91#------------------------------------------------------------------------------------ 

92# Simple functions to test if a particular item_id already exists (for an item_type) 

93#------------------------------------------------------------------------------------ 

94 

95def section_exists(ID:int): return (str(ID) in section_index.keys()) #################### 

96def line_exists(ID:int): return (str(ID) in line_index.keys()) ########################## 

97 

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#------------------------------------------------------------------------------------ 

103 

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) 

111 

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#------------------------------------------------------------------------------------ 

119 

120canvas = None 

121canvas_width = 0 

122canvas_height = 0 

123canvas_grid = 0 

124 

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() 

131 

132#------------------------------------------------------------------------------------ 

133# Function to update the Canvas Attributes (following layout load or canvas resize) 

134#------------------------------------------------------------------------------------ 

135 

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() 

142 

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#------------------------------------------------------------------------------------ 

147 

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() 

159 

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#------------------------------------------------------------------------------------ 

164 

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) 

188 

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#------------------------------------------------------------------------------------ 

195 

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) 

202 

203####################################################################################