added HDF5 read capability. Closes #27
Build Debian Packages / build (trixie, debian13) (pull_request) Successful in 14m37s
Build Debian Packages / build (bookworm, debian12) (pull_request) Successful in 14m50s
Build Debian Packages / build (bullseye, debian11) (pull_request) Has been cancelled

This commit is contained in:
2026-07-06 20:42:04 +02:00
parent c181e15aa8
commit 7ba5c7d56a
3 changed files with 419 additions and 32 deletions
+160 -28
View File
@@ -169,7 +169,7 @@ class DamarisGUI:
self.log = LogWindow( self.xml_gui , self)
self.sw = ScriptWidgets( self.xml_gui )
self.sw = ScriptWidgets( self.xml_gui, self )
self.toolbar_init( )
@@ -218,6 +218,7 @@ class DamarisGUI:
"on_experiment_script_line_textfield_value_changed": self.sw.column_line_widgets_changed_event,
"on_experiment_script_column_textfield_value_changed": self.sw.column_line_widgets_changed_event,
"on_toolbar_open_file_button_clicked": self.sw.open_file,
"on_toolbar_open_hdf5_button_clicked": self.sw.open_hdf5,
"on_toolbar_new_button_clicked": self.sw.new_file,
"on_toolbar_save_as_button_clicked": self.sw.save_file_as,
"on_toolbar_save_file_button_clicked": self.sw.save_file,
@@ -297,6 +298,14 @@ class DamarisGUI:
self.config = None
self.xml_gui = None
def observe_data_pool( self, data_pool ):
"""
Delegate to monitor widgets
"""
self.data = data_pool
if hasattr(self, "monitor"):
self.monitor.observe_data_pool(data_pool)
# event handling: the real acitons in gui programming
# first global events
@@ -1103,11 +1112,12 @@ class LogWindow:
class ScriptWidgets:
def __init__( self, xml_gui ):
def __init__( self, xml_gui, outer_space ):
"""
initialize text widgets with text
"""
self.xml_gui = xml_gui
self.outer_space = outer_space
# my states
# editing enabled/disabled
@@ -1572,7 +1582,7 @@ get_ylabel set_ylabel"""
elif response == gtk.ResponseType.CANCEL:
return 0
def response( self, response_id, script_widget ):
def response( dialog, response_id, script_widget ):
if response_id == gtk.ResponseType.OK:
file_name = dialog.get_filename( )
if file_name is None:
@@ -1580,7 +1590,13 @@ get_ylabel set_ylabel"""
script_filename = os.path.abspath( file_name )
if not os.access( script_filename, os.R_OK ):
self.outer_space.show_error_dialog( "File I/O Error", "Cannot read from file %s" % script_filename )
error_dialog = gtk.MessageDialog(parent=script_widget.xml_gui.get_object( "main_window"),
type=gtk.MessageType.ERROR,
flags=gtk.DialogFlags.MODAL,
buttons=("_OK", gtk.ResponseType.OK))
error_dialog.set_markup("Cannot read from file %s" % script_filename)
error_dialog.run()
error_dialog.destroy()
return True
script_string = script_widget.load_file_as_unicode( script_filename )
@@ -1613,6 +1629,103 @@ get_ylabel set_ylabel"""
return True
def open_hdf5( self, widget, Data=None ):
"""
Open and load an HDF5 file written by DAMARIS.
"""
# ignore
if not self.editing_state:
return 0
parent_window = self.xml_gui.get_object( "main_window" )
dialog = gtk.FileChooserDialog( title="Open HDF5 File...",
parent=parent_window,
action=gtk.FileChooserAction.OPEN,
buttons=("_Open", gtk.ResponseType.OK, "_Cancel", gtk.ResponseType.CANCEL)
)
dialog.set_default_response( gtk.ResponseType.OK )
dialog.set_select_multiple( False )
# Add filter for HDF5 files
filter_hdf5 = gtk.FileFilter()
filter_hdf5.set_name("HDF5 files")
filter_hdf5.add_pattern("*.h5")
filter_hdf5.add_pattern("*.hdf5")
filter_hdf5.add_pattern("*.HDF5")
filter_hdf5.add_pattern("*.H5")
dialog.add_filter(filter_hdf5)
# Also allow all files
filter_all = gtk.FileFilter()
filter_all.set_name("All files")
filter_all.add_pattern("*")
dialog.add_filter(filter_all)
def response( dialog, response_id, script_widget ):
if response_id == gtk.ResponseType.OK:
file_name = dialog.get_filename( )
if file_name is None:
return
file_name = os.path.abspath( file_name )
if not os.access( file_name, os.R_OK ):
error_dialog = gtk.MessageDialog(parent=script_widget.xml_gui.get_object( "main_window"),
type=gtk.MessageType.ERROR,
flags=gtk.DialogFlags.MODAL,
buttons=("_OK", gtk.ResponseType.OK))
error_dialog.set_markup("Cannot read from file %s" % file_name)
error_dialog.run()
error_dialog.destroy()
return True
try:
# Load DataPool and metadata from HDF5
data_pool, metadata = DataPool.load_hdf5(file_name)
# Set the loaded data pool in the outer space (main application)
if hasattr(script_widget.outer_space, 'observe_data_pool'):
script_widget.outer_space.observe_data_pool(data_pool)
elif hasattr(script_widget.outer_space, 'monitor'):
# Fallback if delegate not present
script_widget.outer_space.data = data_pool
script_widget.outer_space.monitor.observe_data_pool(data_pool)
# If there are scripts in metadata, load them
if 'scripts' in metadata:
scripts = metadata['scripts']
exp_script = scripts.get('experiment_script', None)
res_script = scripts.get('result_script', None)
if exp_script or res_script:
script_widget.set_scripts(exp_script, res_script)
# Show success message
success_dialog = gtk.MessageDialog(parent=script_widget.xml_gui.get_object( "main_window"),
type=gtk.MessageType.INFO,
flags=gtk.DialogFlags.MODAL,
buttons=("_OK", gtk.ResponseType.OK))
success_dialog.set_markup("Successfully loaded data from %s" % os.path.basename(file_name))
success_dialog.run()
success_dialog.destroy()
except Exception as e:
error_dialog = gtk.MessageDialog(parent=script_widget.xml_gui.get_object( "main_window"),
type=gtk.MessageType.ERROR,
flags=gtk.DialogFlags.MODAL,
buttons=("_OK", gtk.ResponseType.OK))
error_dialog.set_markup("Failed to load HDF5 file: %s" % str(e))
error_dialog.run()
error_dialog.destroy()
return True
# Event-Handler for response-signal (when one of the button is pressed)
dialog.connect( "response", response, self )
dialog.run( )
dialog.destroy( )
return True
def save_file( self, widget=None, Data=None ):
"""
save file to associated filename
@@ -2411,7 +2524,7 @@ class MonitorWidgets:
def source_list_find_one( self, model, iter, what ):
"""find node in subcategory"""
while iter is not None:
if model.get( iter, 0 )[ 0 ] == what:
if str( model.get( iter, 0 )[ 0 ] ) == what:
return iter
iter = model.iter_next( iter )
return iter
@@ -2482,31 +2595,50 @@ class MonitorWidgets:
register a listener and save reference to data
assume to be in gtk/gdk lock
"""
if self.data_pool is not None:
# maybe some extra cleanup needed
print("ToDo: cleanup widgets")
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = None
self.displayed_data = [ None, None ]
self.data_pool.unregister_listener( self.datapool_listener )
self.data_pool = None
gdk.threads_enter( )
try:
if self.data_pool is not None:
# maybe some extra cleanup needed
print("ToDo: cleanup widgets")
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = None
self.displayed_data = [ None, None ]
self.data_pool.unregister_listener( self.datapool_listener )
self.data_pool = None
self.update_counter_lock.acquire( )
self.update_counter = 0
self.update_counter_lock.release( )
# display states
self.source_list_reset( )
self.update_counter_lock.acquire( )
self.update_counter = 0
self.update_counter_lock.release( )
self.__rescale = True
self.displayed_data = [ None, None ]
self.display_source_path_label.set_label( "" )
self.clear_display( )
# display states
self.__rescale = True
self.displayed_data = [ None, None ]
self.display_source_path_label.set_label( "" )
self.clear_display( )
if data_pool is not None:
# keep track of data
self.data_pool = data_pool
self.data_pool.register_listener( self.datapool_listener )
if data_pool is not None:
# keep track of data
self.data_pool = data_pool
self.data_pool.register_listener( self.datapool_listener )
# Populate source list with existing data since the listener
# was registered after the data was loaded
for key in sorted(data_pool.keys()):
if key != "__builtins__": # Skip internal keys
self.source_list_add(key)
# Select first data item if available
keys = [k for k in data_pool.keys() if not k.startswith("__")]
if keys:
keys.sort()
first_key = keys[0]
first_iter = self.source_list_find(first_key.split("/"))
if first_iter:
self.display_source_combobox.set_active_iter(first_iter)
finally:
gdk.threads_leave( )
#################### observing data structures and produce idle events
@@ -2643,7 +2775,7 @@ class MonitorWidgets:
gdk.threads_leave( )
elif event.what == DataPool.Event.destroy:
gdk.threads_enter( )
self.source_list_reset( 'None' )
self.source_list_reset( )
self.displayed_data = [ None, None ]
self.clear_display( )
gdk.threads_leave( )