From baaf6b90b635bd9b7c9962282185ff57f2548285 Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Sun, 12 Jul 2026 12:21:35 +0200 Subject: [PATCH] Fix #37: Press button to get experiment duration --- src/damaris/gui/DamarisGUI.py | 112 +++++++++++++++++++++++++++++++++ src/damaris/gui/damaris3.glade | 15 +++++ 2 files changed, 127 insertions(+) diff --git a/src/damaris/gui/DamarisGUI.py b/src/damaris/gui/DamarisGUI.py index 2c1896d..42cee90 100644 --- a/src/damaris/gui/DamarisGUI.py +++ b/src/damaris/gui/DamarisGUI.py @@ -210,6 +210,7 @@ class DamarisGUI: #connect event handlers eventhandlers = {"on_toolbar_run_button_clicked": self.start_experiment, + "on_toolbar_estimate_button_clicked": self.estimate_duration, "on_toolbar_pause_button_toggled": self.pause_experiment, "on_toolbar_stop_button_clicked": self.stop_experiment, "on_doc_menu_activate": self.show_doc_menu, @@ -389,6 +390,117 @@ class DamarisGUI: # toolbar related events: + def estimate_duration( self, widget, data=None ): + """ + Estimate the total experiment duration and show a dialog. + """ + from datetime import datetime, timedelta + from damaris.experiments.DurationEstimator import estimate_duration + + exp_script, _ = self.sw.get_scripts() + + # validate syntax first + try: + compile(exp_script, "Experiment Script", "exec") + except SyntaxError as e: + ln = e.lineno if isinstance(e.lineno, int) else 0 + lo = e.offset if isinstance(e.offset, int) else 0 + msg = "Experiment Script: %s at line %d, col %d" % (e.__class__.__name__, ln, lo) + dialog = gtk.MessageDialog( + parent=self.main_window, + flags=gtk.DialogFlags.MODAL, + type=gtk.MessageType.ERROR, + buttons=gtk.ButtonsType.OK, + message_format=msg + ) + dialog.run() + dialog.destroy() + return + + # run estimator in a background thread to not freeze the GUI + dialog = gtk.MessageDialog( + parent=self.main_window, + flags=gtk.DialogFlags.MODAL, + type=gtk.MessageType.INFO, + buttons=gtk.ButtonsType.CLOSE, + message_format="Estimating duration..." + ) + dialog.set_resizable(False) + dialog.show() + + result = [None] + error = [None] + done = threading.Event() + + def run_estimate(): + try: + result[0] = estimate_duration(exp_script) + except Exception as e: + error[0] = str(e) + done.set() + + t = threading.Thread(target=run_estimate) + t.daemon = True + t.start() + + while not done.is_set(): + while gtk.events_pending(): + gtk.main_iteration_do(False) + if done.wait(0.1): + break + + dialog.hide() + dialog.destroy() + + if error[0] is not None: + dialog = gtk.MessageDialog( + parent=self.main_window, + flags=gtk.DialogFlags.MODAL, + type=gtk.MessageType.ERROR, + buttons=gtk.ButtonsType.OK, + message_format="Estimation failed: " + error[0] + ) + dialog.run() + dialog.destroy() + return + + r = result[0] + total = r["total_time"] + exp_time = r["experiment_time"] + sleep_t = r["sleep_time"] + jobs = r["job_count"] + + # format duration as HH:MM:SS + def fmt(seconds): + h = int(seconds) // 3600 + m = (int(seconds) % 3600) // 60 + s = int(seconds) % 60 + return "%02d:%02d:%02d" % (h, m, s) + + now = datetime.now() + end = now + timedelta(seconds=total) + end_str = end.strftime("%H:%M:%S") + + text = ( + "Jobs: %d\n" + "Pulse program: %s (%.2f s)\n" + "Sleeps: %s (%.2f s)\n" + "---------------------------\n" + "Total: %s\n\n" + "Start now:\n" + "Estimated end: %s" + ) % (jobs, fmt(exp_time), exp_time, fmt(sleep_t), sleep_t, fmt(total), end_str) + + dialog = gtk.MessageDialog( + parent=self.main_window, + flags=gtk.DialogFlags.MODAL, + type=gtk.MessageType.INFO, + buttons=gtk.ButtonsType.OK, + message_format=text + ) + dialog.run() + dialog.destroy() + def start_experiment( self, widget, data=None ): # something running? diff --git a/src/damaris/gui/damaris3.glade b/src/damaris/gui/damaris3.glade index a91e401..4483d3e 100644 --- a/src/damaris/gui/damaris3.glade +++ b/src/damaris/gui/damaris3.glade @@ -641,6 +641,21 @@ Public License instead of this License. True + + + True + False + Estimate experiment duration + Estimate + True + dialog-information + + + + False + True + + True