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
+
+
+
+ False
+ True
+
+