3636class ImportedLayer :
3737 """Mock layer object rich enough to satisfy LayerMetadataDialog"""
3838 def __init__ (self , dataset_entity , entity_map ):
39- self .id = dataset_entity [ "@id" ]
40- self .name = dataset_entity [ "name" ]
39+ self .id = dataset_entity . get ( "@id" , "unknown" )
40+ self .name = dataset_entity . get ( "name" , "Unknown Layer" )
4141 self .clean_name = self .name
42- self .description = dataset_entity [ "description" ]
42+ self .description = dataset_entity . get ( "description" , "" )
4343
44- visible_val = dataset_entity [ "layerVisible" ]
44+ visible_val = dataset_entity . get ( "layerVisible" , True )
4545 self .visible = str (visible_val ).lower () == "true" if isinstance (visible_val , str ) else bool (visible_val )
4646
4747 # Prepare default values
@@ -55,19 +55,19 @@ def __init__(self, dataset_entity, entity_map):
5555
5656 # Find geometry entity which contains technical & external metadata
5757 geometry_entity = None
58- has_parts = dataset_entity [ "hasPart" ]
58+ has_parts = dataset_entity . get ( "hasPart" , [])
5959 if isinstance (has_parts , dict ):
6060 has_parts = [has_parts ]
6161
6262 for part_ref in has_parts :
63- part_id = part_ref if isinstance (part_ref , str ) else part_ref [ "@id" ]
63+ part_id = part_ref if isinstance (part_ref , str ) else part_ref . get ( "@id" , "" )
6464 if part_id in entity_map and "geometry" in part_id .lower ():
6565 geometry_entity = entity_map [part_id ]
6666 break
6767
6868 # Extract metadata from geometry entity
6969 if geometry_entity :
70- self .source = geometry_entity [ "@id" ]
70+ self .source = geometry_entity . get ( "@id" , self . id )
7171
7272 # Extract layer type (fall back to additionalType if layerType is somehow missing)
7373 raw_type = geometry_entity .get ("layerType" , geometry_entity .get ("additionalType" , "Unknown" ))
@@ -96,11 +96,11 @@ class ImportedProcess:
9696 """Mock process object rich enough to satisfy ProcessMetadataDialog"""
9797 def __init__ (self , action_entity , instrument_entity = None ):
9898 self .id = action_entity ["@id" ]
99- self .name = action_entity [ "name" ]
100- self .description = action_entity [ "description" ]
99+ self .name = action_entity . get ( "name" , "Unknown Process" )
100+ self .description = action_entity . get ( "description" , "" )
101101
102102 if instrument_entity :
103- self .algorithm_id = instrument_entity [ "name" ]
103+ self .algorithm_id = instrument_entity . get ( "name" , "Unknown Algorithm" )
104104 else :
105105 self .algorithm_id = "Unknown Algorithm"
106106
@@ -115,9 +115,9 @@ def __init__(self, action_entity, instrument_entity=None):
115115 self .timestamp = "Unknown Time"
116116
117117 # Read custom QGIS properties
118- self .log = action_entity [ "qgisLog" ]
119- raw_params = action_entity [ "qgisParameters" ]
120- raw_results = action_entity [ "qgisResults" ]
118+ self .log = action_entity . get ( "qgisLog" , "" )
119+ raw_params = action_entity . get ( "qgisParameters" , "{}" )
120+ raw_results = action_entity . get ( "qgisResults" , "{}" )
121121
122122 # Convert JSON strings to dictionaries for UI rendering in the metadata dialog
123123 try :
@@ -158,7 +158,8 @@ def setup_ui(self):
158158
159159 instruction_label = QLabel (
160160 "Select an exported RO-Crate (.zip) file to visualize its workflow graph. "
161- "This view is read-only. Right-click any node to inspect its metadata."
161+ "This view is read-only. Right-click any node to inspect its metadata. "
162+ "To zoom in and out of the graph, use the keyboard shortcuts Ctrl & '+' / Ctrl & '-', respectively."
162163 )
163164 instruction_label .setWordWrap (True )
164165 main_layout .addWidget (instruction_label )
@@ -257,11 +258,11 @@ def load_graph_from_crate(self, zip_path):
257258 action_id = action ["@id" ]
258259
259260 # Fetch instrument entity to pass to our Mock Process
260- instrument_refs = action [ "instrument" ]
261+ instrument_refs = action . get ( "instrument" , [])
261262 instrument_entity = None
262263 if instrument_refs :
263264 ref_id = instrument_refs [0 ]["@id" ] if isinstance (instrument_refs , list ) else instrument_refs ["@id" ]
264- instrument_entity = self .entity_map [ ref_id ]
265+ instrument_entity = self .entity_map . get ( ref_id )
265266
266267 # Create Process Node
267268 process_obj = ImportedProcess (action , instrument_entity )
@@ -271,23 +272,23 @@ def load_graph_from_crate(self, zip_path):
271272 self .graph_view .scene .addItem (p_node )
272273
273274 # Process Inputs (Data -> Process)
274- inputs = action [ "object" ]
275+ inputs = action . get ( "object" , [])
275276 if isinstance (inputs , dict ):
276277 inputs = [inputs ]
277278
278279 for input_ref in inputs :
279- in_id = input_ref [ "@id" ]
280+ in_id = input_ref . get ( "@id" )
280281 if in_id :
281282 norm_in_id = self ._ensure_layer_node_exists (in_id )
282283 connections_to_make .append ((norm_in_id , action_id ))
283284
284285 # Process Outputs (Process -> Data)
285- outputs = action [ "result" ]
286+ outputs = action . get ( "result" , [])
286287 if isinstance (outputs , dict ):
287288 outputs = [outputs ]
288289
289290 for output_ref in outputs :
290- out_id = output_ref [ "@id" ]
291+ out_id = output_ref . get ( "@id" )
291292 if out_id :
292293 norm_out_id = self ._ensure_layer_node_exists (out_id )
293294 connections_to_make .append ((action_id , norm_out_id ))
@@ -298,7 +299,7 @@ def load_graph_from_crate(self, zip_path):
298299 target_node = self .nodes_map .get (target_id )
299300
300301 if source_node and target_node :
301- arrow = ConnectionArrow (source_node , target_node )
302+ arrow = ConnectionArrow (source_node , target_node , deletable = False )
302303 self .graph_view .scene .addItem (arrow )
303304
304305 # 6. Apply auto-layout
@@ -407,4 +408,11 @@ def _apply_auto_layout(self, edges):
407408 arrow .update_position ()
408409 elif isinstance (node , ProcessNode ):
409410 for arrow in node .input_arrows + node .output_arrows :
410- arrow .update_position ()
411+ arrow .update_position ()
412+
413+ # Update scene rect to encompass all items
414+ scene_rect = self .graph_view .scene .itemsBoundingRect ()
415+ self .graph_view .scene .setSceneRect (scene_rect )
416+
417+ # Fit the view to show all items within the visible area
418+ self .graph_view .fitInView (scene_rect , Qt .KeepAspectRatio )
0 commit comments