8 Commits
Author SHA1 Message Date
markusro b46debad69 Fixed #13 and #14
Build Debian Packages / build (trixie, debian13) (push) Successful in 14m2s
Build Debian Packages / build (bookworm, debian12) (push) Successful in 14m11s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-05 14:07:57 +02:00
markusro 4250039b3c can now set ADC_Result bit depth in the config window (Bug #16)
Build Debian Packages / build (trixie, debian13) (push) Successful in 14m3s
Build Debian Packages / build (bookworm, debian12) (push) Successful in 14m4s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-05 13:59:09 +02:00
markusro d3b1a9e3fd Show message if ADC_Data is clipped (solves #16)
Build Debian Packages / build (bookworm, debian12) (push) Successful in 14m38s
Build Debian Packages / build (trixie, debian13) (push) Successful in 14m40s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-05 13:44:20 +02:00
markusro 06867ec17b Missing search input field fixed (Fixes #25)
Build Debian Packages / build (bookworm, debian12) (push) Successful in 14m5s
Build Debian Packages / build (trixie, debian13) (push) Successful in 14m7s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-05 13:24:20 +02:00
markusro 80d8f1e78d fixed stop button, no can interrupt running experiment
Build Debian Packages / build (trixie, debian13) (push) Successful in 15m23s
Build Debian Packages / build (bookworm, debian12) (push) Successful in 15m46s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-04 21:03:23 +02:00
markusro 58e7846bbe description is plotted better now 2026-07-04 20:50:57 +02:00
markusro 1c4f8f5344 Improved interruptable sleep and warn user when using time.sleep.
Build Debian Packages / build (trixie, debian13) (push) Successful in 13m4s
Build Debian Packages / build (bookworm, debian12) (push) Successful in 13m9s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-04 20:26:14 +02:00
markusro a3738025b7 fixed autocomplete in the editor windows
Build Debian Packages / build (trixie, debian13) (push) Successful in 12m59s
Build Debian Packages / build (bookworm, debian12) (push) Successful in 13m9s
Build Debian Packages / build (bullseye, debian11) (push) Has been cancelled
2026-07-04 20:01:34 +02:00
10 changed files with 256 additions and 34 deletions

No files matched your search

+6
View File
@@ -1,3 +1,9 @@
python3-damaris (0.21) UNRELEASED; urgency=medium
* fixed autocomplete in the editor tabs
-- Markus Rosenstihl <markus.rosenstihl@pkm.tu-darmstadt.de> Sat, 04 Jul 2026 20:00:13 +0200
python3-damaris (0.20) stable; urgency=medium
* Unified version management across Python app, GUI and documentation
+1 -1
View File
@@ -15,5 +15,5 @@
## module contents
#
__version__ = "0.20"
__version__ = "0.21"
__all__=["experiments", "data", "gui"]
+44 -1
View File
@@ -19,6 +19,7 @@ import tables
#############################################################################
class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
default_bit_depth = 14
"""
Represents the result of an ADC, encapsulating data and metadata
for processing, visualization, and export.
@@ -50,6 +51,7 @@ class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
self.ylabel = "Samples [Digits]"
self.lock=threading.RLock()
self.nChannels = 0
self.is_clipped = False
# using no argument for initialization
if (x is None) and (y is None) and (index is None) and (sampl_freq is None) and (desc is None) and (job_id is None) and (job_date is None):
@@ -107,6 +109,44 @@ class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
return self.cont_data
def check_clipping(self, bit_depth=None):
"""
Check if any data in y is clipped.
For a 14-bit ADC, clipping occurs at 2**13 (8192) or 2**13-1 (8191).
In general, for a bit_depth-bit signed ADC, limits are 2**(bit_depth-1) and -(2**(bit_depth-1)).
"""
if not self.contains_data():
return False
if bit_depth is None:
# try to get bit_depth from description
if hasattr(self, "description") and self.description is not None:
if "adc_bit_depth" in self.description:
try:
bit_depth = int(self.description["adc_bit_depth"])
except (ValueError, TypeError):
bit_depth = ADC_Result.default_bit_depth
else:
bit_depth = ADC_Result.default_bit_depth
else:
bit_depth = ADC_Result.default_bit_depth
limit_pos = 2**(bit_depth-1) - 1
limit_neg = -2**(bit_depth-1)
self.lock.acquire()
try:
self.is_clipped = False
for ch_data in self.y:
if numpy.any(ch_data >= limit_pos) or numpy.any(ch_data <= limit_neg):
self.is_clipped = True
break
finally:
self.lock.release()
return self.is_clipped
def add_sample_space(self, samples):
"Adds space for n samples, where n can also be negative (deletes space). New space is filled up with \"0\""
@@ -143,7 +183,7 @@ class ADC_Result(Resultable, Drawable, DamarisFFT, Signalpath):
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i][start:end+1].copy())
r = ADC_Result(x = tmp_x, y = tmp_y, index = [(0,len(tmp_y[0])-1)], sampl_freq = self.sampling_rate, desc = self.description, job_id = self.job_id, job_date = self.job_date)
r = ADC_Result(x = tmp_x, y = tmp_y, index = [(0,len(tmp_y[0])-1)], sampl_freq = self.sampling_rate, desc = self.description.copy() if self.description is not None else None, job_id = self.job_id, job_date = self.job_date)
self.lock.release()
return r
@@ -536,6 +576,8 @@ def read_from_hdf(hdf_node):
# job id and x,y titles are missing
adc=ADC_Result()
if hdf_node._v_title:
adc.set_title(hdf_node._v_title)
# populate description dictionary
adc.description={}
for attrname in hdf_node._v_attrs._v_attrnamesuser:
@@ -573,4 +615,5 @@ def read_from_hdf(hdf_node):
for ch in range(adc_data.shape[1]):
adc.y.append(adc_data[:,ch])
adc.check_clipping()
return adc
+34 -3
View File
@@ -61,6 +61,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
self.xlabel = "Time / s"
self.ylabel = "Avg. Samples [Digits]"
self.lock=threading.RLock()
self.is_clipped = False
self.common_descriptions=None
self.time_period=[]
@@ -117,9 +118,23 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
tmp_y.append(self.y[i][start:end+1])
r = Accumulation(x = tmp_x, y = tmp_y, n = self.n, index = [(0,len(tmp_y[0])-1)], sampl_freq = self.sampling_rate, error = self.use_error)
if self.uses_statistics():
r.y_square = []
for i in range(self.get_number_of_channels()):
r.y_square.append(self.y_square[i][start:end+1])
if self.common_descriptions is not None:
r.common_descriptions = self.common_descriptions.copy()
r.time_period = self.time_period[:]
r.job_ids = self.job_ids.copy()
self.lock.release()
return r
def get_result_by_index(self, index):
return self.get_accu_by_index(index)
def get_Result_by_index(self, index):
return self.get_accu_by_index(index)
def get_ysquare(self, channel):
if self.uses_statistics():
try:
@@ -616,6 +631,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
else:
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n, index = self.index, sampl_freq = self.sampling_rate, error = self.use_error)
r.is_clipped = self.is_clipped
r.job_id = self.job_id
r.job_ids = self.job_ids.copy()
r.time_period = self.time_period[:] if self.time_period is not None else None
@@ -645,6 +661,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = 1, index = other.index, sampl_freq = other.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(other.x, dtype="float32"), y = tmp_y, index = other.index, sampl_freq = other.sampling_rate, n = 1, error = False)
if hasattr(other, "is_clipped"):
r.is_clipped = other.is_clipped
r.time_period=[other.job_date,other.job_date]
r.job_id = other.job_id
r.common_descriptions=other.description.copy()
@@ -678,6 +696,7 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = self.n + 1, index = self.index, sampl_freq = self.sampling_rate, error = False)
r.is_clipped = self.is_clipped or (hasattr(other, "is_clipped") and other.is_clipped)
r.time_period=[min(self.time_period[0],other.job_date),
max(self.time_period[1],other.job_date)]
r.job_id = other.job_id
@@ -744,13 +763,14 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
for i in range(self.get_number_of_channels()):
tmp_y.append(self.y[i] + other.y[i])
if self.uses_statistics():
tmp_ysquare.append(self.y_square[i] + other.y_square[i])
if self.uses_statistics():
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, y_2 = tmp_ysquare, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = True)
else:
r = Accumulation(x = numpy.array(self.x, dtype="float32"), y = tmp_y, n = other.n + self.n, index = self.index, sampl_freq = self.sampling_rate, error = False)
r.is_clipped = self.is_clipped or (hasattr(other, "is_clipped") and other.is_clipped)
r.time_period=[min(self.time_period[0],other.time_period[0]),
max(self.time_period[1],other.time_period[1])]
r.job_id = other.job_id
@@ -954,6 +974,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
self.set_title(self.__title_pattern % self.n)
self.lock.release()
if hasattr(other, "is_clipped"):
self.is_clipped = other.is_clipped
self.time_period=[other.job_date,other.job_date]
self.job_id = other.job_id # added by Oleg Petrov
self.job_ids[other.job_id] = other.get_job_date()
@@ -982,6 +1004,8 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
self.y_square[i] += numpy.array(other.y[i], dtype="float32") ** 2
self.n += 1
if hasattr(other, "is_clipped"):
self.is_clipped = self.is_clipped or other.is_clipped
self.time_period=[min(self.time_period[0],other.job_date),
max(self.time_period[1],other.job_date)]
@@ -1020,10 +1044,12 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
self.y_square.append(self.y[i] ** 2)
self.set_title(self.__title_pattern % self.n)
self.common_descriptions=other.common_desriptions.copy()
if hasattr(other, "is_clipped"):
self.is_clipped = other.is_clipped
self.common_descriptions=other.common_descriptions.copy()
self.time_period=other.time_period[:]
self.job_id = other.job_id # added by Oleg Petrov
self.job_ids += other.job_ids
self.job_ids.update(other.job_ids)
self.lock.release()
return self
@@ -1049,9 +1075,12 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath):
self.y_square[i] += other.y_square[i]
self.n += other.n
if hasattr(other, "is_clipped"):
self.is_clipped = self.is_clipped or other.is_clipped
self.time_period=[min(self.time_period[0],other.time_period[0]),
max(self.time_period[1],other.time_period[1])]
self.job_id = other.job_id
self.job_ids.update(other.job_ids)
# Removes mismatched common description keys
if self.common_descriptions is not None and other.common_descriptions is not None:
# Get all keys that exist in both dictionaries
@@ -1168,6 +1197,8 @@ def read_from_hdf(hdf_node):
accu.n=r["number"]
accu.sampling_rate=1.0/r["dwelltime"]
accu.set_title("Accumulation: n = %d" % accu.n)
# now really belief there are no data
if len(accu.index)==0 or accu.n==0:
accu.cont_data=False
+106 -28
View File
@@ -471,9 +471,10 @@ class DamarisGUI:
self.id = self.lockfile.add_experiment()
while not self.lockfile.am_i_next():
self.stop_experiment_flag.wait(interval)
while gtk.events_pending():
gtk.main_iteration_do(False)
if self.stop_experiment_flag.wait(interval):
break
if loop_run > 1:
self.experiment_script_statusbar_label.set_text("Waiting for other experiment to finish")
loop_run = 0
@@ -524,7 +525,10 @@ class DamarisGUI:
print("waiting for threads stoping...", end=' ')
still_running = [x for x in [ self.si.exp_handling, self.si.res_handling, self.si.back_driver ] if x is not None and x.is_alive( )]
for t in still_running:
t.join( )
while t.is_alive():
while gtk.events_pending():
gtk.main_iteration_do(False)
t.join( 0.05 )
print("done")
# cleanup
@@ -1186,7 +1190,6 @@ get_ylabel set_ylabel"""
compexp.add_provider(self.compwexp)
compres.add_provider(self.compwres)
# script fonts are atlered by configuration
# clipboard
self.main_clipboard = gtk.Clipboard( )
@@ -1194,13 +1197,12 @@ get_ylabel set_ylabel"""
self.experiment_script_statusbar_label = self.xml_gui.get_object( "statusbar_experiment_script_label" )
self.data_handling_statusbar_label = self.xml_gui.get_object( "statusbar_data_handling_label" )
# footline with line and col indicators
# footer with line and col indicators
self.experiment_script_line_indicator = self.xml_gui.get_object( "experiment_script_line_textfield" )
self.experiment_script_column_indicator = self.xml_gui.get_object( "experiment_script_column_textfield" )
self.data_handling_line_indicator = self.xml_gui.get_object( "data_handling_line_textfield" )
self.data_handling_column_indicator = self.xml_gui.get_object( "data_handling_column_textfield" )
# some event handlers
self.experiment_script_textbuffer.connect( "modified-changed", self.textviews_modified )
self.experiment_script_textview.connect_after( "move-cursor", self.textviews_moved )
@@ -1211,8 +1213,7 @@ get_ylabel set_ylabel"""
self.data_handling_textview.connect( "key-press-event", self.textviews_keypress )
self.data_handling_textview.connect( "button-release-event", self.textviews_clicked )
# and the editing toolbar part
# buttons
# and the editing toolbar buttons
self.toolbar_new_button = self.xml_gui.get_object( "toolbar_new_button" )
self.toolbar_open_button = self.xml_gui.get_object( "toolbar_open_file_button" )
self.toolbar_save_button = self.xml_gui.get_object( "toolbar_save_file_button" )
@@ -1223,8 +1224,9 @@ get_ylabel set_ylabel"""
self.toolbar_redo_button = self.xml_gui.get_object( "toolbar_redo_button" )
self.toolbar_search_button = self.xml_gui.get_object("toolbar_search_button")
# my notebook
# main notebook
self.main_notebook = self.xml_gui.get_object( "main_notebook" )
# config toolbar
self.main_notebook.connect_after( "switch_page", self.notebook_page_switched )
@@ -1798,9 +1800,9 @@ get_ylabel set_ylabel"""
try:
if response_id == -1:
match_start, match_end = startiter.backward_search(searchstring, gtk.TEXT_SEARCH_TEXT_ONLY)
match_start, match_end = startiter.backward_search(searchstring, gtk.TextSearchFlags.TEXT_ONLY)
elif response_id == 1:
match_start, match_end = enditer.forward_search(searchstring, gtk.TEXT_SEARCH_TEXT_ONLY)
match_start, match_end = enditer.forward_search(searchstring, gtk.TextSearchFlags.TEXT_ONLY)
except:
pass
@@ -1857,7 +1859,7 @@ get_ylabel set_ylabel"""
dialog.connect("response", response, self, textbox)
dialog.set_resizable(False)
dialog.show()
dialog.show_all()
if startit is not None and endit is not None:
if current_page == 0:
@@ -1951,6 +1953,20 @@ class ConfigTab:
self.config_info_textview = self.xml_gui.get_object( "info_textview" )
self.config_script_font_button = self.xml_gui.get_object( "script_fontbutton" )
# ADC bit depth setting
self.config_adc_bit_depth_hbox = gtk.HBox(homogeneous=False, spacing=6)
self.config_adc_bit_depth_label = gtk.Label(label="ADC bit depth:")
self.config_adc_bit_depth_spinbutton = gtk.SpinButton.new_with_range(8, 32, 1)
self.config_adc_bit_depth_spinbutton.set_value(ADC_Result.default_bit_depth)
self.config_adc_bit_depth_spinbutton.set_tooltip_text("Set the default bit depth for clipping detection (e.g., 14 for 14-bit ADCs).")
self.config_adc_bit_depth_spinbutton.connect("value-changed", self.on_adc_bit_depth_changed)
self.config_adc_bit_depth_hbox.pack_start(self.config_adc_bit_depth_label, False, False, 0)
self.config_adc_bit_depth_hbox.pack_start(self.config_adc_bit_depth_spinbutton, False, False, 0)
backend_vbox = self.xml_gui.get_object("vbox4")
if backend_vbox:
backend_vbox.pack_start(self.config_adc_bit_depth_hbox, False, False, 0)
self.config_adc_bit_depth_hbox.show_all()
# insert version informations
components_text = """
operating system %(os)s
@@ -2044,7 +2060,7 @@ pygobject version %(pygobject)s
"""
complib_iter = self.config_data_pool_complib.get_active_iter( )
complib = self.config_data_pool_complib.get_model( ).get_value( complib_iter, 0 )
return {
actual_config = {
"start_backend": self.config_start_backend_checkbutton.get_active( ),
"start_result_script": self.config_start_result_script_checkbutton.get_active( ),
"start_experiment_script": self.config_start_experiment_script_checkbutton.get_active( ),
@@ -2056,8 +2072,11 @@ pygobject version %(pygobject)s
"data_pool_write_interval": self.config_data_pool_write_interval_entry.get_text( ),
"data_pool_complib": complib,
"data_pool_comprate": self.config_data_pool_comprate.get_value_as_int( ),
"script_font": self.config_script_font_button.get_font_name( )
"script_font": self.config_script_font_button.get_font_name( ),
"adc_bit_depth": self.config_adc_bit_depth_spinbutton.get_value_as_int()
}
ADC_Result.default_bit_depth = actual_config["adc_bit_depth"]
return actual_config
def set( self, config ):
if "start_backend" in config:
@@ -2083,6 +2102,12 @@ pygobject version %(pygobject)s
if "script_font" in config:
self.config_script_font_button.set_font_name( config[ "script_font" ] )
self.set_script_font_handler( None )
if "adc_bit_depth" in config:
try:
self.config_adc_bit_depth_spinbutton.set_value(float(config["adc_bit_depth"]))
ADC_Result.default_bit_depth = int(config["adc_bit_depth"])
except (ValueError, TypeError):
pass
if "data_pool_complib" in config:
# find combo-box entry and make it active...
model = self.config_data_pool_complib.get_model( )
@@ -2096,6 +2121,9 @@ pygobject version %(pygobject)s
if iter is None:
print("compression method %s is not supported" % config[ "data_pool_complib" ])
def on_adc_bit_depth_changed(self, widget):
ADC_Result.default_bit_depth = widget.get_value_as_int()
def load_config_handler( self, widget ):
if self.system_default_filename:
self.load_config( self.system_default_filename )
@@ -2270,6 +2298,13 @@ class MonitorWidgets:
self.display_autoscaling_checkbutton = self.xml_gui.get_object( "display_autoscaling_checkbutton" )
self.display_statistics_checkbutton = self.xml_gui.get_object( "display_statistics_checkbutton" )
self.display_descriptions_checkbutton = gtk.CheckButton(label="Show Desc.")
self.display_descriptions_checkbutton.set_active(True)
self.display_descriptions_checkbutton.show()
self.display_settings_frame.set_property("n-columns", 5)
self.display_settings_frame.attach(self.display_descriptions_checkbutton, 4, 5, 0, 1, gtk.AttachOptions.FILL, 0, 0, 0)
self.display_descriptions_checkbutton.connect("toggled", self.display_descriptions_toggled)
# Matplot (Display_Table, 1st Row) --------------------------------------------------------
# create new plot
@@ -2363,6 +2398,8 @@ class MonitorWidgets:
self.__rescale = True
self.update_counter = 0
self.update_counter_lock = threading.Lock( )
self.description_text = None
self.clipping_marker = None
def source_list_reset( self ):
self.display_source_treestore.clear( )
@@ -2685,6 +2722,10 @@ class MonitorWidgets:
if self.displayed_data[ 0 ] is not None:
self.update_display( self.displayed_data[ 0 ][ : ] )
def display_descriptions_toggled( self, widget, data=None ):
if self.displayed_data[ 0 ] is not None:
self.update_display( self.displayed_data[ 0 ][ : ] )
def save_display_data_as_text( self, widget, data=None ):
"""
copy data to tmp file and show save dialog
@@ -2935,6 +2976,18 @@ class MonitorWidgets:
self.graphen = [ ]
self.matplot_axes.clear( )
self.matplot_axes.grid( True )
if self.description_text is not None:
try:
self.description_text.remove()
except:
pass
self.description_text = None
if self.clipping_marker is not None:
try:
self.clipping_marker.remove()
except:
pass
self.clipping_marker = None
self.matplot_canvas.draw_idle( )
def update_display( self, subject=None ):
@@ -3093,25 +3146,48 @@ class MonitorWidgets:
self.matplot_axes.set_ylabel( "" )
# Any variables to be drawn on plot
if False:
descriptions = None
description_lines = []
if self.display_descriptions_checkbutton.get_active():
if isinstance(in_result, Accumulation):
descriptions = in_result.common_descriptions
description_lines.append("n = %d" % in_result.n)
if in_result.common_descriptions is not None:
description_lines.extend(["%s = %s" % (key, in_result.common_descriptions[key]) for key in in_result.common_descriptions.keys()])
elif isinstance(in_result, ADC_Result):
descriptions = in_result.description
else:
pass
#actual_config = self.config.get()
if (descriptions is not None):
#print(actual_config['pretty_descriptions'])
description_string = ""
for key in descriptions.keys():
description_string += "%s = %s\n" % (key,descriptions[key])
self.matplot_axes.text(0.8,0.95, description_string[:-1],
if hasattr(in_result, "job_id") and in_result.job_id is not None:
description_lines.append("job_id = %s" % str(in_result.job_id))
if hasattr(in_result, "description") and in_result.description is not None:
description_lines.extend(["%s = %s" % (key, in_result.description[key]) for key in in_result.description.keys()])
if description_lines:
description_string = "\n".join(description_lines)
if self.description_text is None:
self.description_text = self.matplot_axes.text(0.98, 0.95, description_string,
size=10,
transform=self.matplot_axes.transAxes,
va='top',
backgroundcolor='white')
va='top', ha='right', ma='left',
bbox=dict(facecolor='lightgray', alpha=0.5, boxstyle='round,pad=0.2'))
else:
self.description_text.set_text(description_string)
elif self.description_text is not None:
try:
self.description_text.remove()
except:
pass
self.description_text = None
if getattr(in_result, "is_clipped", False):
if self.clipping_marker is None:
self.clipping_marker = self.matplot_axes.text(0.5, 0.95, "ADC OVERFLOW",
transform=self.matplot_axes.transAxes,
color="red", fontsize=20, fontweight="bold",
ha="center", va="top",
bbox=dict(facecolor='white', alpha=0.7, edgecolor='red'))
elif self.clipping_marker is not None:
try:
self.clipping_marker.remove()
except:
pass
self.clipping_marker = None
# Draw it!
self.matplot_canvas.draw_idle( )
@@ -3287,6 +3363,8 @@ class ScriptInterface:
self.back_driver.start( )
while (not self.back_driver.quit_flag.isSet( ) and \
(self.back_driver.core_pid is None or self.back_driver.core_pid <= 0)):
while gtk.events_pending():
gtk.main_iteration_do(False)
self.back_driver.quit_flag.wait( 0.1 )
if self.exp_handling:
self.exp_handling.start( )
+27
View File
@@ -3,6 +3,7 @@ import io
import traceback
import sys
import time
import ast
from damaris.experiments.Experiment import Quit
from damaris.experiments import Experiment
@@ -48,8 +49,32 @@ class ExperimentHandling(threading.Thread):
self.raised_exception = None
self.location = None
exp_iterator=None
# check for time.sleep()
try:
tree = ast.parse(self.script)
for node in ast.walk(tree):
found_blocking_sleep = False
if isinstance(node, ast.Call):
func = node.func
if isinstance(func, ast.Attribute) and func.attr == 'sleep':
if isinstance(func.value, ast.Name) and func.value.id == 'time':
found_blocking_sleep = True
elif isinstance(node, ast.ImportFrom) and node.module == 'time':
for alias in node.names:
if alias.name == 'sleep':
found_blocking_sleep = True
if found_blocking_sleep:
print("Warning: time.sleep() detected in experiment script. This is blocking and not interruptible. Please use sleep() instead.")
break
except Exception:
pass
try:
exec(self.script, dataspace)
except StopExperiment:
return
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
@@ -61,6 +86,8 @@ class ExperimentHandling(threading.Thread):
if "experiment" in dataspace:
try:
exp_iterator=dataspace["experiment"]()
except StopExperiment:
return
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
+34
View File
@@ -4,7 +4,9 @@ import sys
import os
import os.path
import traceback
import ast
from damaris.data import Resultable
from damaris.gui.ExperimentHandling import StopExperiment
class ResultHandling(threading.Thread):
"""
@@ -30,10 +32,35 @@ class ResultHandling(threading.Thread):
del data_classes
dataspace["results"]=self
dataspace["data"]=self.data_space
dataspace["sleep"]=self.sleep
self.raised_exception=None
self.location = None
# check for time.sleep()
try:
tree = ast.parse(self.script)
for node in ast.walk(tree):
found_blocking_sleep = False
if isinstance(node, ast.Call):
func = node.func
if isinstance(func, ast.Attribute) and func.attr == 'sleep':
if isinstance(func.value, ast.Name) and func.value.id == 'time':
found_blocking_sleep = True
elif isinstance(node, ast.ImportFrom) and node.module == 'time':
for alias in node.names:
if alias.name == 'sleep':
found_blocking_sleep = True
if found_blocking_sleep:
print("Warning: time.sleep() detected in result script. This is blocking and not interruptible. Please use sleep() instead.")
break
except Exception:
pass
try:
exec(self.script, dataspace)
except StopExperiment:
return
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
@@ -47,6 +74,8 @@ class ResultHandling(threading.Thread):
return
try:
dataspace["result"]()
except StopExperiment:
pass
except Exception as e:
self.raised_exception=e
self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
@@ -56,6 +85,11 @@ class ResultHandling(threading.Thread):
traceback_file=None
dataspace=None
def sleep(self, seconds):
self.quit_flag.wait(seconds)
if self.quit_flag.isSet():
raise StopExperiment
def __iter__(self):
if self.quit_flag.isSet():
self.results=None
+2
View File
@@ -239,6 +239,7 @@ class ResultReader:
self.result.index = [(0,tmp_size-1)]
tmp_index += tmp_size
self.result.cont_data=True
self.result.check_clipping()
tmp_part = None
def __parseFile_expat(self, in_file):
@@ -310,6 +311,7 @@ class ResultReader:
self.result.index.append((tmp_sample_counter,tmp_sample_counter+tmp_size-1))
tmp_sample_counter+=tmp_size
self.result.cont_data=True
self.result.check_clipping()
# Callback when a xml start tag is found
def __xmlStartTagFound(self, in_name, in_attribute):
+1
View File
@@ -1,4 +1,5 @@
import tables
import numpy
r = MeasurementResult("test")
+1 -1
View File
@@ -105,7 +105,7 @@ class TestAccumulation(unittest.TestCase):
self.assertTrue(np.array_equal(sub_result.y[0], np.array([20, 30])))
self.assertTrue(np.array_equal(sub_result.y[1], np.array([25, 35])))
self.assertEqual(sub_result.index, [(0, 1)])
# TODO Fix #13: self.assertEqual(sub_result.common_descriptions, {"key": "value"})
self.assertEqual(sub_result.common_descriptions, {"key": "value"})
def test_write_to_csv_without_error(self):
"""Test the functionality of writing to CSV."""