From c800214535c518f102c44e3655311081035eb8b3 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 2 Jul 2026 12:47:06 -0700 Subject: [PATCH 1/9] busy cursor wrapper in utils --- src/badger/gui/utils.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/badger/gui/utils.py b/src/badger/gui/utils.py index ef5f03a9..c3d977ef 100644 --- a/src/badger/gui/utils.py +++ b/src/badger/gui/utils.py @@ -5,10 +5,12 @@ from importlib import resources from typing import Any from PyQt5.QtWidgets import QAbstractSpinBox, QPushButton, QComboBox, QToolButton -from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel +from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QApplication from PyQt5.QtCore import Qt, QObject, QEvent, QSize from PyQt5.QtGui import QIcon import copy +from functools import wraps +from typing import Callable def preventAnnoyingSpinboxScrollBehaviour(self, control: QAbstractSpinBox) -> None: @@ -119,3 +121,23 @@ def __init__(self, parent=None, info=None): self.setLayout(layout) # Semi-transparent background self.setStyleSheet("background-color: rgba(0, 0, 0, 80);") + + +def set_busy_cursor() -> None: + QApplication.setOverrideCursor(Qt.BusyCursor) + + +def unset_busy_cursor() -> None: + QApplication.restoreOverrideCursor() + + +def with_busy_cursor(func: Callable) -> Callable: + @wraps(func) + def wrapped(*args, **kwargs): + set_busy_cursor() + try: + return func(*args, **kwargs) + finally: + unset_busy_cursor() + + return wrapped From 1641a3904e4b1cdcd100162fe8553bd74584aaf4 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 2 Jul 2026 12:47:56 -0700 Subject: [PATCH 2/9] set busy cursor while env is loaded --- src/badger/gui/mini/pages/routine_page.py | 3 ++- src/badger/gui/windows/expandable_message_box.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index f7ffaaff..50523b7d 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -58,7 +58,7 @@ from badger.gui.windows.review_dialog import BadgerReviewDialog from badger.gui.windows.add_random_dialog import BadgerAddRandomDialog from badger.gui.windows.message_dialog import BadgerScrollableMessageBox -from badger.gui.utils import filter_generator_config +from badger.gui.utils import filter_generator_config, with_busy_cursor from badger.environment import instantiate_env from badger.errors import ( BadgerEnvNotFoundError, @@ -1067,6 +1067,7 @@ def refresh_params_generator(self): except Exception as e: QMessageBox.warning(self, "Invalid script!", str(e)) + @with_busy_cursor def select_env(self, i: int): logger.info(f"Environment selected: {self.env_box.env_name} (index={i})") # Reset the initial table actions and ratio var ranges diff --git a/src/badger/gui/windows/expandable_message_box.py b/src/badger/gui/windows/expandable_message_box.py index cc887257..52b34eeb 100644 --- a/src/badger/gui/windows/expandable_message_box.py +++ b/src/badger/gui/windows/expandable_message_box.py @@ -12,6 +12,7 @@ ) from PyQt5.QtGui import QTextOption, QFont, QFontDatabase from PyQt5.QtCore import Qt +from badger.gui.utils import unset_busy_cursor class ExpandableMessageBox(QDialog): @@ -21,6 +22,8 @@ def __init__( super().__init__(parent) self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint) + unset_busy_cursor() + # Main layout mainLayout = QVBoxLayout(self) From c39f6c2d62094dd3bc240b025ecef569232c5049 Mon Sep 17 00:00:00 2001 From: Ryan Roussel Date: Wed, 17 Jun 2026 14:35:35 -0500 Subject: [PATCH 3/9] bugfix for select_best --- src/badger/gui/components/bo_visualizer/bo_widget.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/badger/gui/components/bo_visualizer/bo_widget.py b/src/badger/gui/components/bo_visualizer/bo_widget.py index 933aaeb3..44502ddd 100644 --- a/src/badger/gui/components/bo_visualizer/bo_widget.py +++ b/src/badger/gui/components/bo_visualizer/bo_widget.py @@ -31,6 +31,7 @@ from badger.gui.components.bo_visualizer.plotting_area import PlottingArea from PyQt5.QtCore import Qt from xopt.generators.bayesian.bayesian_generator import BayesianGenerator +from xopt.vocs import select_best from badger.gui.components.analysis_widget import AnalysisWidget import logging @@ -546,8 +547,8 @@ def set_best_reference_points( "No data available in generator for selecting best reference points", ) - index_arr, value_arr, input_params = self.routine.vocs.select_best( - self.generator.data + index_arr, value_arr, input_params = select_best( + self.routine.vocs, self.generator.data ) if not index_arr or not value_arr: From ca6e2389ac588572b786b7b293d0e14c68b7ddee Mon Sep 17 00:00:00 2001 From: Ryan Roussel Date: Mon, 29 Jun 2026 09:48:45 -0500 Subject: [PATCH 4/9] update xopt call to get_local_region --- src/badger/gui/components/routine_page.py | 4 ++-- src/badger/routine.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/badger/gui/components/routine_page.py b/src/badger/gui/components/routine_page.py index 86c8ac43..c7215d0c 100644 --- a/src/badger/gui/components/routine_page.py +++ b/src/badger/gui/components/routine_page.py @@ -48,7 +48,7 @@ all_generator_names, get_generator_dynamic, ) -from xopt.utils import get_local_region +from xopt.vocs import get_local_region from gest_api.vocs import ( BaseConstraint, BaseObjective, @@ -1311,7 +1311,7 @@ def add_rand_in_init_table(self, add_rand_config=None, record=True): n_point = add_rand_config["n_points"] fraction = add_rand_config["fraction"] - random_sample_region = get_local_region(var_curr, vocs, fraction=fraction) + random_sample_region = get_local_region(vocs, var_curr, fraction=fraction) with warnings.catch_warnings(record=True) as caught_warnings: try: random_points = random_inputs( diff --git a/src/badger/routine.py b/src/badger/routine.py index f6e29942..0ab501ec 100644 --- a/src/badger/routine.py +++ b/src/badger/routine.py @@ -28,7 +28,7 @@ ) from xopt import Evaluator, VOCS, Xopt from xopt.generators import get_generator -from xopt.utils import get_local_region +from xopt.vocs import get_local_region from xopt.generators.sequential import SequentialGenerator from badger.utils import curr_ts from badger.environment import BaseEnvironment, instantiate_env @@ -259,7 +259,7 @@ def calculate_initial_points(init_actions, vocs, env): var_curr = env.get_variables(vnames) n_point = action["config"]["n_points"] fraction = action["config"]["fraction"] - random_sample_region = get_local_region(var_curr, vocs, fraction=fraction) + random_sample_region = get_local_region(vocs, var_curr, fraction=fraction) random_points = vocs.random_inputs( n_point, custom_bounds=random_sample_region ) From accd7fc73f9f6e05196a5d04503c403b7718ec47 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 2 Jul 2026 13:16:49 -0700 Subject: [PATCH 5/9] update for xopt get_local_region --- src/badger/gui/mini/pages/routine_page.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index 50523b7d..7791caac 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -32,7 +32,7 @@ all_generator_names, get_generator_dynamic, ) -from xopt.utils import get_local_region +from xopt.vocs import get_local_region from gest_api.vocs import ( BaseObjective, GreaterThanConstraint, @@ -1253,7 +1253,7 @@ def add_rand_in_init_table(self, add_rand_config=None, record=True): n_point = add_rand_config["n_points"] fraction = add_rand_config["fraction"] - random_sample_region = get_local_region(var_curr, vocs, fraction=fraction) + random_sample_region = get_local_region(vocs, var_curr, fraction=fraction) with warnings.catch_warnings(record=True) as caught_warnings: try: random_points = random_inputs( From 9d7c297025d4dcb179af4ae796450522f0402ab4 Mon Sep 17 00:00:00 2001 From: nstelter Date: Fri, 11 Sep 2026 10:44:04 -0700 Subject: [PATCH 6/9] make gui bottom-bar txt say "loading variables" and "done loading variables" during env loading --- src/badger/gui/components/routine_page.py | 12 +++++++++++- src/badger/gui/mini/pages/home_page.py | 2 ++ src/badger/gui/mini/pages/routine_page.py | 9 +++++++++ src/badger/gui/pages/home_page.py | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/badger/gui/components/routine_page.py b/src/badger/gui/components/routine_page.py index c7215d0c..87bee62a 100644 --- a/src/badger/gui/components/routine_page.py +++ b/src/badger/gui/components/routine_page.py @@ -40,6 +40,7 @@ from PyQt5.QtWidgets import QMessageBox, QWidget, QTabWidget from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QScrollArea from PyQt5.QtWidgets import QTableWidgetItem, QPlainTextEdit +from PyQt5.QtWidgets import QApplication from coolname import generate_slug from xopt import VOCS from xopt.vocs import random_inputs @@ -77,7 +78,7 @@ from badger.gui.windows.review_dialog import BadgerReviewDialog from badger.gui.windows.add_random_dialog import BadgerAddRandomDialog from badger.gui.windows.message_dialog import BadgerScrollableMessageBox -from badger.gui.utils import filter_generator_config +from badger.gui.utils import filter_generator_config, with_busy_cursor from badger.gui.components.archive_search import ArchiveSearchWidget from badger.archive import update_run from badger.environment import instantiate_env @@ -149,6 +150,7 @@ class BadgerRoutinePage(QWidget): sig_updated = pyqtSignal(str, str) # routine name, routine description sig_load_template = pyqtSignal(str) # template path sig_save_template = pyqtSignal(str) # template path + sig_status = pyqtSignal(str) def __init__(self): logger.info("Initializing BadgerRoutinePage.") @@ -1114,8 +1116,13 @@ def refresh_params_generator(self): except Exception as e: QMessageBox.warning(self, "Invalid script!", str(e)) + @with_busy_cursor def select_env(self, i: int): logger.info(f"Environment selected: {self.env_box.cb.itemText(i)} (index={i})") + + self.sig_status.emit("Loading variables...") + QApplication.processEvents() + # Reset the initial table actions and ratio var ranges self.init_table_actions = [] self.ratio_var_ranges = {} @@ -1241,6 +1248,9 @@ def select_env(self, i: int): # Update the docs self.window_env_docs.update_docs(env.name, "environment") + self.sig_status.emit("Variables loaded.") + QApplication.processEvents() + def get_init_table_header(self): table = self.env_box.init_table header_list = [] diff --git a/src/badger/gui/mini/pages/home_page.py b/src/badger/gui/mini/pages/home_page.py index e9b7989b..1153fddf 100644 --- a/src/badger/gui/mini/pages/home_page.py +++ b/src/badger/gui/mini/pages/home_page.py @@ -230,6 +230,8 @@ def config_logic(self): self.routine_editor.sig_load_template.connect(self.update_status) self.routine_editor.sig_save_template.connect(self.update_status) self.routine_editor.sig_go_run.connect(self.go_run) + self.routine_editor.sig_status.connect(self.update_status) + self.routine_editor.env_box.var_table.sig_status.connect(self.update_status) self.run_monitor.sig_inspect.connect(self.inspect_solution) self.run_monitor.sig_lock.connect(self.toggle_lock) diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index 7791caac..ee621bd2 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -23,6 +23,7 @@ from PyQt5.QtWidgets import QMessageBox, QWidget, QTabWidget from PyQt5.QtWidgets import QVBoxLayout, QScrollArea from PyQt5.QtWidgets import QTableWidgetItem, QPlainTextEdit +from PyQt5.QtWidgets import QApplication from badger.gui.components.navigators import HistoryNavigator from coolname import generate_slug from xopt import VOCS @@ -126,6 +127,7 @@ class BadgerRoutinePage(QWidget): sig_save_template = pyqtSignal(str) # template path sig_go_run = pyqtSignal() sig_select_env = pyqtSignal(str) + sig_status = pyqtSignal(str) def __init__(self): logger.info("Initializing BadgerRoutinePage.") @@ -1070,6 +1072,10 @@ def refresh_params_generator(self): @with_busy_cursor def select_env(self, i: int): logger.info(f"Environment selected: {self.env_box.env_name} (index={i})") + + self.sig_status.emit("Loading variables...") + QApplication.processEvents() + # Reset the initial table actions and ratio var ranges self.init_table_actions = [] self.ratio_var_ranges = {} @@ -1185,6 +1191,9 @@ def select_env(self, i: int): # Update the docs self.window_env_docs.update_docs(env.name, "environment") + self.sig_status.emit("Variables loaded.") + QApplication.processEvents() + def get_init_table_header(self): table = self.env_box.init_table header_list = [] diff --git a/src/badger/gui/pages/home_page.py b/src/badger/gui/pages/home_page.py index 7dfe8f6f..e55dc465 100644 --- a/src/badger/gui/pages/home_page.py +++ b/src/badger/gui/pages/home_page.py @@ -235,6 +235,7 @@ def config_logic(self): self.routine_editor.sig_load_template.connect(self.update_status) self.routine_editor.sig_save_template.connect(self.update_status) + self.routine_editor.sig_status.connect(self.update_status) self.run_monitor.sig_inspect.connect(self.inspect_solution) self.run_monitor.sig_lock.connect(self.toggle_lock) From f68512683801b847f4f78e3aeeacd0262c8753d5 Mon Sep 17 00:00:00 2001 From: nstelter Date: Wed, 16 Sep 2026 15:55:06 -0700 Subject: [PATCH 7/9] Remove line causing error in mini-mode related to loading-bar msg --- src/badger/gui/mini/pages/home_page.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/badger/gui/mini/pages/home_page.py b/src/badger/gui/mini/pages/home_page.py index 1153fddf..883ef03a 100644 --- a/src/badger/gui/mini/pages/home_page.py +++ b/src/badger/gui/mini/pages/home_page.py @@ -231,7 +231,6 @@ def config_logic(self): self.routine_editor.sig_save_template.connect(self.update_status) self.routine_editor.sig_go_run.connect(self.go_run) self.routine_editor.sig_status.connect(self.update_status) - self.routine_editor.env_box.var_table.sig_status.connect(self.update_status) self.run_monitor.sig_inspect.connect(self.inspect_solution) self.run_monitor.sig_lock.connect(self.toggle_lock) From 2d1540e8b592fc920aa4e87a45da100d0e841ba3 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 17 Sep 2026 10:32:48 -0700 Subject: [PATCH 8/9] update status message after select_env --- src/badger/gui/components/routine_page.py | 2 +- src/badger/gui/mini/pages/routine_page.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/badger/gui/components/routine_page.py b/src/badger/gui/components/routine_page.py index c08e492d..0bb31188 100644 --- a/src/badger/gui/components/routine_page.py +++ b/src/badger/gui/components/routine_page.py @@ -1251,7 +1251,7 @@ def select_env(self, i: int): # Update the docs self.window_env_docs.update_docs(env.name, "environment") - self.sig_status.emit("Variables loaded.") + self.sig_status.emit(f"Badger Environment '{env.name}' loaded") QApplication.processEvents() def get_init_table_header(self): diff --git a/src/badger/gui/mini/pages/routine_page.py b/src/badger/gui/mini/pages/routine_page.py index 20d76353..066d5b4b 100644 --- a/src/badger/gui/mini/pages/routine_page.py +++ b/src/badger/gui/mini/pages/routine_page.py @@ -1196,7 +1196,7 @@ def select_env(self, i: int): self.check_for_nan_vars() - self.sig_status.emit("Variables loaded.") + self.sig_status.emit(f"Badger Environment '{env.name}' loaded") QApplication.processEvents() def check_for_nan_vars(self) -> None: From 77278fc0ad08e138a5dabf4ef1bfa3ce33e37f42 Mon Sep 17 00:00:00 2001 From: michaellans Date: Thu, 17 Sep 2026 11:57:01 -0700 Subject: [PATCH 9/9] fix import merge conflict --- src/badger/gui/components/routine_page.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/badger/gui/components/routine_page.py b/src/badger/gui/components/routine_page.py index 2a0579a3..7d065139 100644 --- a/src/badger/gui/components/routine_page.py +++ b/src/badger/gui/components/routine_page.py @@ -49,6 +49,7 @@ from pydantic import ValidationError from PyQt5.QtCore import Qt, QTimer, pyqtSignal from PyQt5.QtWidgets import ( + QApplication, QFileDialog, QHBoxLayout, QLabel, @@ -99,18 +100,7 @@ ) from badger.gui.windows.lim_vrange_dialog import BadgerLimitVariableRangeDialog from badger.gui.windows.message_dialog import BadgerScrollableMessageBox -from badger.gui.utils import filter_generator_config, with_busy_cursor -from badger.gui.components.archive_search import ArchiveSearchWidget -from badger.archive import update_run -from badger.environment import instantiate_env -from badger.errors import ( - BadgerEnvNotFoundError, - BadgerRoutineError, - BadgerEnvVarError, - BadgerEnvInstantiationError, - VariableRangeError, -) -from badger.factory import list_generators, list_env, get_env +from badger.gui.utils import with_busy_cursor from badger.gui.windows.review_dialog import BadgerReviewDialog from badger.routine import Routine from badger.settings import init_settings