Merge pull request 'Hdfreader' (#31) from hdfreader into main
Reviewed-on: #31
This commit was merged in pull request #31.
This commit is contained in:
@@ -1160,26 +1160,66 @@ def read_from_hdf(hdf_node):
|
|||||||
earliest_time=None
|
earliest_time=None
|
||||||
if "earliest_time" in dir(hdf_node._v_attrs):
|
if "earliest_time" in dir(hdf_node._v_attrs):
|
||||||
timestring=hdf_node._v_attrs.__getattr__("earliest_time")
|
timestring=hdf_node._v_attrs.__getattr__("earliest_time")
|
||||||
earliest_time=datetime.datetime(int(timestring[:4]), # year
|
# Handle both formats: "20260705 19:13:59.162" and "2026-07-05 19:13:59.162"
|
||||||
int(timestring[4:6]), # month
|
if '-' in timestring:
|
||||||
int(timestring[6:8]), # day
|
# New format: "2026-07-05 19:13:59.162"
|
||||||
int(timestring[9:11]), # hour
|
try:
|
||||||
int(timestring[12:14]), # minute
|
earliest_time=datetime.datetime.strptime(str(timestring), '%Y-%m-%d %H:%M:%S.%f')
|
||||||
int(timestring[15:17]), # second
|
except ValueError:
|
||||||
int(timestring[18:21])*1000 # microsecond
|
# Fallback to manual parsing
|
||||||
)
|
parts = timestring.split()
|
||||||
|
if len(parts) >= 2:
|
||||||
|
date_parts = parts[0].split('-')
|
||||||
|
time_parts = parts[1].split(':')
|
||||||
|
if len(date_parts) == 3 and len(time_parts) >= 2:
|
||||||
|
year, month, day = int(date_parts[0]), int(date_parts[1]), int(date_parts[2])
|
||||||
|
hour, minute = int(time_parts[0]), int(time_parts[1])
|
||||||
|
second_parts = time_parts[2].split('.') if len(time_parts) > 2 else ['0']
|
||||||
|
second = int(second_parts[0])
|
||||||
|
microsecond = int(second_parts[1].ljust(6, '0')[:6]) if len(second_parts) > 1 else 0
|
||||||
|
earliest_time=datetime.datetime(year, month, day, hour, minute, second, microsecond)
|
||||||
|
else:
|
||||||
|
# Old format: "20260705 19:13:59.162"
|
||||||
|
earliest_time=datetime.datetime(int(timestring[:4]), # year
|
||||||
|
int(timestring[4:6]), # month
|
||||||
|
int(timestring[6:8]), # day
|
||||||
|
int(timestring[9:11]), # hour
|
||||||
|
int(timestring[12:14]), # minute
|
||||||
|
int(timestring[15:17]), # second
|
||||||
|
int(timestring[18:21])*1000 # microsecond
|
||||||
|
)
|
||||||
|
|
||||||
oldest_time=None
|
oldest_time=None
|
||||||
if "oldest_time" in dir(hdf_node._v_attrs):
|
if "oldest_time" in dir(hdf_node._v_attrs):
|
||||||
timestring=hdf_node._v_attrs.__getattr__("oldest_time")
|
timestring=hdf_node._v_attrs.__getattr__("oldest_time")
|
||||||
oldest_time=datetime.datetime(int(timestring[:4]), # year
|
# Handle both formats: "20260705 19:13:59.162" and "2026-07-05 19:13:59.162"
|
||||||
int(timestring[4:6]), # month
|
if '-' in timestring:
|
||||||
int(timestring[6:8]), # day
|
# New format: "2026-07-05 19:13:59.162"
|
||||||
int(timestring[9:11]), # hour
|
try:
|
||||||
int(timestring[12:14]), # minute
|
oldest_time=datetime.datetime.strptime(str(timestring), '%Y-%m-%d %H:%M:%S.%f')
|
||||||
int(timestring[15:17]), # second
|
except ValueError:
|
||||||
int(timestring[18:21])*1000 # microsecond
|
# Fallback to manual parsing
|
||||||
)
|
parts = timestring.split()
|
||||||
|
if len(parts) >= 2:
|
||||||
|
date_parts = parts[0].split('-')
|
||||||
|
time_parts = parts[1].split(':')
|
||||||
|
if len(date_parts) == 3 and len(time_parts) >= 2:
|
||||||
|
year, month, day = int(date_parts[0]), int(date_parts[1]), int(date_parts[2])
|
||||||
|
hour, minute = int(time_parts[0]), int(time_parts[1])
|
||||||
|
second_parts = time_parts[2].split('.') if len(time_parts) > 2 else ['0']
|
||||||
|
second = int(second_parts[0])
|
||||||
|
microsecond = int(second_parts[1].ljust(6, '0')[:6]) if len(second_parts) > 1 else 0
|
||||||
|
oldest_time=datetime.datetime(year, month, day, hour, minute, second, microsecond)
|
||||||
|
else:
|
||||||
|
# Old format: "20260705 19:13:59.162"
|
||||||
|
oldest_time=datetime.datetime(int(timestring[:4]), # year
|
||||||
|
int(timestring[4:6]), # month
|
||||||
|
int(timestring[6:8]), # day
|
||||||
|
int(timestring[9:11]), # hour
|
||||||
|
int(timestring[12:14]), # minute
|
||||||
|
int(timestring[15:17]), # second
|
||||||
|
int(timestring[18:21])*1000 # microsecond
|
||||||
|
)
|
||||||
|
|
||||||
if oldest_time is None or earliest_time is None:
|
if oldest_time is None or earliest_time is None:
|
||||||
accu.time_period=None
|
accu.time_period=None
|
||||||
@@ -1212,7 +1252,7 @@ def read_from_hdf(hdf_node):
|
|||||||
accu.y_square=[]
|
accu.y_square=[]
|
||||||
accu.use_error=False
|
accu.use_error=False
|
||||||
|
|
||||||
for ch in range(accu_data.shape[1]/2):
|
for ch in range(accu_data.shape[1] // 2):
|
||||||
accu.y.append(accu_data[:,ch*2]*accu.n)
|
accu.y.append(accu_data[:,ch*2]*accu.n)
|
||||||
if accu.n<2 or numpy.all(accu_data[:,ch*2+1]==0.0):
|
if accu.n<2 or numpy.all(accu_data[:,ch*2+1]==0.0):
|
||||||
accu.y_square.append(numpy.zeros((accu_data.shape[0]) ,dtype="float32"))
|
accu.y_square.append(numpy.zeros((accu_data.shape[0]) ,dtype="float32"))
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import collections
|
|||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import io
|
import io
|
||||||
|
import numpy
|
||||||
|
|
||||||
class DataPool(collections.abc.MutableMapping):
|
class DataPool(collections.abc.MutableMapping):
|
||||||
"""
|
"""
|
||||||
@@ -107,7 +108,7 @@ class DataPool(collections.abc.MutableMapping):
|
|||||||
self.__registered_listeners=None
|
self.__registered_listeners=None
|
||||||
|
|
||||||
def write_hdf5(self,hdffile,where="/",name="data_pool", complib=None, complevel=None):
|
def write_hdf5(self,hdffile,where="/",name="data_pool", complib=None, complevel=None):
|
||||||
if type(hdffile) is bytes:
|
if isinstance(hdffile, (bytes, str)):
|
||||||
dump_file=tables.open_file(hdffile, mode="a")
|
dump_file=tables.open_file(hdffile, mode="a")
|
||||||
elif isinstance(hdffile,tables.File):
|
elif isinstance(hdffile,tables.File):
|
||||||
dump_file=hdffile
|
dump_file=hdffile
|
||||||
@@ -156,7 +157,7 @@ class DataPool(collections.abc.MutableMapping):
|
|||||||
value=self.__mydict[key]
|
value=self.__mydict[key]
|
||||||
self.__dictlock.release()
|
self.__dictlock.release()
|
||||||
# now write data, assuming, the object is constant during write operation
|
# now write data, assuming, the object is constant during write operation
|
||||||
if "write_to_hdf" in dir(value):
|
if hasattr(value, "write_to_hdf"):
|
||||||
try:
|
try:
|
||||||
value.write_to_hdf(hdffile=dump_file,
|
value.write_to_hdf(hdffile=dump_file,
|
||||||
where=dump_dir,
|
where=dump_dir,
|
||||||
@@ -170,16 +171,255 @@ class DataPool(collections.abc.MutableMapping):
|
|||||||
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
|
traceback.print_tb(sys.exc_info()[2], None, traceback_file)
|
||||||
print("detailed traceback: %s\n"%str(e)+traceback_file.getvalue())
|
print("detailed traceback: %s\n"%str(e)+traceback_file.getvalue())
|
||||||
traceback_file=None
|
traceback_file=None
|
||||||
|
elif isinstance(value, (numpy.ndarray, int, float, str, bytes)):
|
||||||
|
try:
|
||||||
|
obj_to_save = value
|
||||||
|
if isinstance(value, str):
|
||||||
|
obj_to_save = value.encode('utf-8')
|
||||||
|
dump_file.create_array(dump_dir,
|
||||||
|
name=group_keyname,
|
||||||
|
obj=obj_to_save,
|
||||||
|
title=key)
|
||||||
|
except Exception as e:
|
||||||
|
print("failed to write data_pool[\"%s\"]: %s"%(key,str(e)))
|
||||||
else:
|
else:
|
||||||
print("don't know how to store data_pool[\"%s\"]"%key)
|
print("don't know how to store data_pool[\"%s\"] (type: %s)"%(key, type(value)))
|
||||||
value=None
|
value=None
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
dump_group=None
|
dump_group=None
|
||||||
if type(hdffile) is bytes:
|
if isinstance(hdffile, (bytes, str)):
|
||||||
dump_file.close()
|
dump_file.close()
|
||||||
dump_file=None
|
dump_file=None
|
||||||
|
|
||||||
|
def read_from_hdf(self, hdffile, where="/data_pool"):
|
||||||
|
"""
|
||||||
|
Read data from HDF5 file and populate the DataPool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- hdffile: HDF5 file object or filename (bytes/str)
|
||||||
|
- where: Location within HDF5 file to read from (default: "/data_pool")
|
||||||
|
"""
|
||||||
|
# Handle both filename and file object
|
||||||
|
if type(hdffile) is bytes or isinstance(hdffile, str):
|
||||||
|
hdf_file = tables.open_file(hdffile, mode="r")
|
||||||
|
close_file = True
|
||||||
|
elif isinstance(hdffile, tables.File):
|
||||||
|
hdf_file = hdffile
|
||||||
|
close_file = False
|
||||||
|
else:
|
||||||
|
raise Exception("expecting hdffile or string")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Navigate to the data pool group
|
||||||
|
if where not in hdf_file:
|
||||||
|
raise Exception(f"HDF5 location '{where}' not found")
|
||||||
|
|
||||||
|
data_pool_group = hdf_file.get_node(where)
|
||||||
|
|
||||||
|
# Recursively process the group structure
|
||||||
|
self._read_hdf_group(data_pool_group, "")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if close_file:
|
||||||
|
hdf_file.close()
|
||||||
|
|
||||||
|
def _read_hdf_group(self, group, path_prefix):
|
||||||
|
"""
|
||||||
|
Recursively read HDF5 group and populate DataPool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- group: HDF5 group object
|
||||||
|
- path_prefix: Current path in DataPool hierarchy
|
||||||
|
"""
|
||||||
|
# Process all nodes in this group
|
||||||
|
for node in group:
|
||||||
|
# Get the node name (last part of the path)
|
||||||
|
node_name = node._v_name
|
||||||
|
|
||||||
|
# Try to get the original name from title
|
||||||
|
title = node._v_title
|
||||||
|
|
||||||
|
if title:
|
||||||
|
# For DAMARIS objects, the title is usually the full path
|
||||||
|
# For intermediate groups, it's just the part name
|
||||||
|
if "/" in title:
|
||||||
|
full_path = title
|
||||||
|
else:
|
||||||
|
if path_prefix:
|
||||||
|
full_path = f"{path_prefix}/{title}"
|
||||||
|
else:
|
||||||
|
full_path = title
|
||||||
|
else:
|
||||||
|
# Strip dir_ and dict_ prefixes from node name
|
||||||
|
clean_name = self._clean_hdf_name(node_name)
|
||||||
|
|
||||||
|
# Build the full path in DataPool hierarchy
|
||||||
|
if path_prefix:
|
||||||
|
full_path = f"{path_prefix}/{clean_name}"
|
||||||
|
else:
|
||||||
|
full_path = clean_name
|
||||||
|
|
||||||
|
full_path = full_path.replace("//", "/")
|
||||||
|
|
||||||
|
if isinstance(node, (tables.Group, tables.Table)):
|
||||||
|
# Check if this is a known DAMARIS object type
|
||||||
|
if hasattr(node._v_attrs, 'damaris_type'):
|
||||||
|
damaris_type = node._v_attrs.damaris_type
|
||||||
|
|
||||||
|
# Handle different DAMARIS object types
|
||||||
|
if damaris_type == "Accumulation":
|
||||||
|
from damaris.data.Accumulation import read_from_hdf
|
||||||
|
obj = read_from_hdf(node)
|
||||||
|
if obj is not None:
|
||||||
|
self[full_path] = obj
|
||||||
|
|
||||||
|
elif damaris_type == "ADC_Result":
|
||||||
|
from damaris.data.ADC_Result import read_from_hdf
|
||||||
|
obj = read_from_hdf(node)
|
||||||
|
if obj is not None:
|
||||||
|
self[full_path] = obj
|
||||||
|
|
||||||
|
elif damaris_type == "MeasurementResult":
|
||||||
|
from damaris.data.MeasurementResult import read_from_hdf
|
||||||
|
obj = read_from_hdf(node)
|
||||||
|
if obj is not None:
|
||||||
|
self[full_path] = obj
|
||||||
|
|
||||||
|
# Skip further processing of this group since we handled it
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Recursively process sub-groups
|
||||||
|
if isinstance(node, tables.Group):
|
||||||
|
self._read_hdf_group(node, full_path)
|
||||||
|
|
||||||
|
elif isinstance(node, (tables.Array, tables.CArray, tables.EArray, tables.Table)):
|
||||||
|
# Handle simple array data
|
||||||
|
try:
|
||||||
|
data = node.read()
|
||||||
|
# If it's a scalar array of bytes, decode it to string
|
||||||
|
if hasattr(data, 'decode'):
|
||||||
|
try:
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif isinstance(data, numpy.ndarray) and data.dtype.kind in ('S', 'V'):
|
||||||
|
# For numpy byte arrays, try to decode if they are scalar or small
|
||||||
|
if data.size == 1:
|
||||||
|
try:
|
||||||
|
decoded = data.item().decode('utf-8')
|
||||||
|
data = decoded
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self[full_path] = data
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not read array {full_path}: {e}")
|
||||||
|
|
||||||
|
# Note: Other node types (Table, etc.) would be handled here as needed
|
||||||
|
|
||||||
|
def _clean_hdf_name(self, name):
|
||||||
|
"""
|
||||||
|
Clean HDF5 node name by removing dir_ and dict_ prefixes.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- name: HDF5 node name (may have dir_ or dict_ prefix)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- Cleaned name with prefixes removed
|
||||||
|
"""
|
||||||
|
if name.startswith("dir_"):
|
||||||
|
return name[4:] # Remove "dir_" prefix
|
||||||
|
elif name.startswith("dict_"):
|
||||||
|
return name[5:] # Remove "dict_" prefix
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_hdf5(cls, filename):
|
||||||
|
"""
|
||||||
|
Load a DAMARIS HDF5 file and return DataPool and metadata.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- filename: Path to HDF5 file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- tuple: (DataPool instance, metadata dict)
|
||||||
|
|
||||||
|
metadata dict contains:
|
||||||
|
- 'scripts': dict with experiment scripts and spool info
|
||||||
|
- 'log': log data if available
|
||||||
|
- 'timeline': timeline data if available
|
||||||
|
"""
|
||||||
|
# Validate file
|
||||||
|
if not tables.is_pytables_file(filename):
|
||||||
|
raise Exception(f"File {filename} is not a valid PyTables HDF5 file")
|
||||||
|
|
||||||
|
# Create DataPool and load data
|
||||||
|
data_pool = cls()
|
||||||
|
|
||||||
|
try:
|
||||||
|
hdf_file = tables.open_file(filename, mode="r")
|
||||||
|
|
||||||
|
# Load main data pool
|
||||||
|
if "/data_pool" in hdf_file:
|
||||||
|
data_pool.read_from_hdf(hdf_file, where="/data_pool")
|
||||||
|
|
||||||
|
# Extract metadata
|
||||||
|
metadata = {}
|
||||||
|
|
||||||
|
# Load scripts
|
||||||
|
if "/scripts" in hdf_file:
|
||||||
|
scripts_group = hdf_file.get_node("/scripts")
|
||||||
|
scripts_data = {}
|
||||||
|
# Use _v_children to get node names safely
|
||||||
|
if hasattr(scripts_group, '_v_children'):
|
||||||
|
for node_name, node in scripts_group._v_children.items():
|
||||||
|
try:
|
||||||
|
if hasattr(node, 'read'):
|
||||||
|
data = node.read()
|
||||||
|
# Handle numpy string arrays and other types
|
||||||
|
if hasattr(data, 'decode'):
|
||||||
|
# bytes or numpy bytes
|
||||||
|
scripts_data[node_name] = data.decode('utf-8', errors='replace')
|
||||||
|
elif hasattr(data, 'tolist'):
|
||||||
|
# numpy array
|
||||||
|
scripts_data[node_name] = str(data.tolist())
|
||||||
|
elif isinstance(data, (str, bytes)):
|
||||||
|
# string or bytes
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
scripts_data[node_name] = data.decode('utf-8', errors='replace')
|
||||||
|
else:
|
||||||
|
scripts_data[node_name] = data
|
||||||
|
else:
|
||||||
|
scripts_data[node_name] = str(data)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not read script {node_name}: {e}")
|
||||||
|
metadata['scripts'] = scripts_data
|
||||||
|
|
||||||
|
# Load log
|
||||||
|
if "/log" in hdf_file:
|
||||||
|
try:
|
||||||
|
log_node = hdf_file.get_node("/log")
|
||||||
|
metadata['log'] = log_node.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not read log: {e}")
|
||||||
|
|
||||||
|
# Load timeline
|
||||||
|
if "/timeline" in hdf_file:
|
||||||
|
try:
|
||||||
|
timeline_node = hdf_file.get_node("/timeline")
|
||||||
|
metadata['timeline'] = timeline_node.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not read timeline: {e}")
|
||||||
|
|
||||||
|
return data_pool, metadata
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Failed to load HDF5 file {filename}: {e}")
|
||||||
|
finally:
|
||||||
|
if 'hdf_file' in locals():
|
||||||
|
hdf_file.close()
|
||||||
|
|
||||||
def register_listener(self, listening_function):
|
def register_listener(self, listening_function):
|
||||||
self.__registered_listeners.append(listening_function)
|
self.__registered_listeners.append(listening_function)
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,10 @@ class Experiment:
|
|||||||
|
|
||||||
#for tracking the experiment length:
|
#for tracking the experiment length:
|
||||||
#because loops are possible we need to track the length for each loop level
|
#because loops are possible we need to track the length for each loop level
|
||||||
self.total_time=[]
|
self.total_time=[0.0]
|
||||||
self.total_time.append(0.0)
|
|
||||||
|
|
||||||
#and we need to know the number of iterations of the loops to multiply the state.
|
#and we need to know the number of iterations of the loops to multiply the state.
|
||||||
self.loop_iterations=[]
|
self.loop_iterations=[1]
|
||||||
self.loop_iterations.append(1)
|
|
||||||
|
|
||||||
# Commands -------------------------------------------------------------------------------------
|
# Commands -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
+160
-28
@@ -169,7 +169,7 @@ class DamarisGUI:
|
|||||||
|
|
||||||
self.log = LogWindow( self.xml_gui , self)
|
self.log = LogWindow( self.xml_gui , self)
|
||||||
|
|
||||||
self.sw = ScriptWidgets( self.xml_gui )
|
self.sw = ScriptWidgets( self.xml_gui, self )
|
||||||
|
|
||||||
self.toolbar_init( )
|
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_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_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_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_new_button_clicked": self.sw.new_file,
|
||||||
"on_toolbar_save_as_button_clicked": self.sw.save_file_as,
|
"on_toolbar_save_as_button_clicked": self.sw.save_file_as,
|
||||||
"on_toolbar_save_file_button_clicked": self.sw.save_file,
|
"on_toolbar_save_file_button_clicked": self.sw.save_file,
|
||||||
@@ -297,6 +298,14 @@ class DamarisGUI:
|
|||||||
self.config = None
|
self.config = None
|
||||||
self.xml_gui = 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
|
# event handling: the real acitons in gui programming
|
||||||
|
|
||||||
# first global events
|
# first global events
|
||||||
@@ -1103,11 +1112,12 @@ class LogWindow:
|
|||||||
|
|
||||||
|
|
||||||
class ScriptWidgets:
|
class ScriptWidgets:
|
||||||
def __init__( self, xml_gui ):
|
def __init__( self, xml_gui, outer_space ):
|
||||||
"""
|
"""
|
||||||
initialize text widgets with text
|
initialize text widgets with text
|
||||||
"""
|
"""
|
||||||
self.xml_gui = xml_gui
|
self.xml_gui = xml_gui
|
||||||
|
self.outer_space = outer_space
|
||||||
|
|
||||||
# my states
|
# my states
|
||||||
# editing enabled/disabled
|
# editing enabled/disabled
|
||||||
@@ -1572,7 +1582,7 @@ get_ylabel set_ylabel"""
|
|||||||
elif response == gtk.ResponseType.CANCEL:
|
elif response == gtk.ResponseType.CANCEL:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def response( self, response_id, script_widget ):
|
def response( dialog, response_id, script_widget ):
|
||||||
if response_id == gtk.ResponseType.OK:
|
if response_id == gtk.ResponseType.OK:
|
||||||
file_name = dialog.get_filename( )
|
file_name = dialog.get_filename( )
|
||||||
if file_name is None:
|
if file_name is None:
|
||||||
@@ -1580,7 +1590,13 @@ get_ylabel set_ylabel"""
|
|||||||
|
|
||||||
script_filename = os.path.abspath( file_name )
|
script_filename = os.path.abspath( file_name )
|
||||||
if not os.access( script_filename, os.R_OK ):
|
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
|
return True
|
||||||
|
|
||||||
script_string = script_widget.load_file_as_unicode( script_filename )
|
script_string = script_widget.load_file_as_unicode( script_filename )
|
||||||
@@ -1613,6 +1629,103 @@ get_ylabel set_ylabel"""
|
|||||||
|
|
||||||
return True
|
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 ):
|
def save_file( self, widget=None, Data=None ):
|
||||||
"""
|
"""
|
||||||
save file to associated filename
|
save file to associated filename
|
||||||
@@ -2411,7 +2524,7 @@ class MonitorWidgets:
|
|||||||
def source_list_find_one( self, model, iter, what ):
|
def source_list_find_one( self, model, iter, what ):
|
||||||
"""find node in subcategory"""
|
"""find node in subcategory"""
|
||||||
while iter is not None:
|
while iter is not None:
|
||||||
if model.get( iter, 0 )[ 0 ] == what:
|
if str( model.get( iter, 0 )[ 0 ] ) == what:
|
||||||
return iter
|
return iter
|
||||||
iter = model.iter_next( iter )
|
iter = model.iter_next( iter )
|
||||||
return iter
|
return iter
|
||||||
@@ -2482,31 +2595,50 @@ class MonitorWidgets:
|
|||||||
register a listener and save reference to data
|
register a listener and save reference to data
|
||||||
assume to be in gtk/gdk lock
|
assume to be in gtk/gdk lock
|
||||||
"""
|
"""
|
||||||
if self.data_pool is not None:
|
gdk.threads_enter( )
|
||||||
# maybe some extra cleanup needed
|
try:
|
||||||
print("ToDo: cleanup widgets")
|
if self.data_pool is not None:
|
||||||
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
|
# maybe some extra cleanup needed
|
||||||
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
|
print("ToDo: cleanup widgets")
|
||||||
self.displayed_data[ 1 ] = None
|
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
|
||||||
self.displayed_data = [ None, None ]
|
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
|
||||||
self.data_pool.unregister_listener( self.datapool_listener )
|
self.displayed_data[ 1 ] = None
|
||||||
self.data_pool = 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.source_list_reset( )
|
||||||
self.update_counter_lock.acquire( )
|
self.__rescale = True
|
||||||
self.update_counter = 0
|
self.displayed_data = [ None, None ]
|
||||||
self.update_counter_lock.release( )
|
self.display_source_path_label.set_label( "" )
|
||||||
|
self.clear_display( )
|
||||||
|
|
||||||
# display states
|
if data_pool is not None:
|
||||||
self.__rescale = True
|
# keep track of data
|
||||||
self.displayed_data = [ None, None ]
|
self.data_pool = data_pool
|
||||||
self.display_source_path_label.set_label( "" )
|
self.data_pool.register_listener( self.datapool_listener )
|
||||||
self.clear_display( )
|
|
||||||
|
# Populate source list with existing data since the listener
|
||||||
if data_pool is not None:
|
# was registered after the data was loaded
|
||||||
# keep track of data
|
for key in sorted(data_pool.keys()):
|
||||||
self.data_pool = data_pool
|
if key != "__builtins__": # Skip internal keys
|
||||||
self.data_pool.register_listener( self.datapool_listener )
|
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
|
#################### observing data structures and produce idle events
|
||||||
|
|
||||||
@@ -2643,7 +2775,7 @@ class MonitorWidgets:
|
|||||||
gdk.threads_leave( )
|
gdk.threads_leave( )
|
||||||
elif event.what == DataPool.Event.destroy:
|
elif event.what == DataPool.Event.destroy:
|
||||||
gdk.threads_enter( )
|
gdk.threads_enter( )
|
||||||
self.source_list_reset( 'None' )
|
self.source_list_reset( )
|
||||||
self.displayed_data = [ None, None ]
|
self.displayed_data = [ None, None ]
|
||||||
self.clear_display( )
|
self.clear_display( )
|
||||||
gdk.threads_leave( )
|
gdk.threads_leave( )
|
||||||
|
|||||||
@@ -491,6 +491,21 @@ Public License instead of this License.
|
|||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="toolbar_open_hdf5_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Open DAMARIS HDF5 file</property>
|
||||||
|
<property name="label">Open HDF5</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
<property name="icon-name">document-open</property>
|
||||||
|
<signal name="clicked" handler="on_toolbar_open_hdf5_button_clicked" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton" id="toolbar_save_file_button">
|
<object class="GtkToolButton" id="toolbar_save_file_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|||||||
Reference in New Issue
Block a user