Skip to content

Commit

Permalink
WIP: Rewrote extension entry point to separate initialization from up…
Browse files Browse the repository at this point in the history
…dates to window
  • Loading branch information
MitchellAV committed Nov 26, 2024
1 parent 7554948 commit ecc6efa
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 25 deletions.
36 changes: 36 additions & 0 deletions Notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Extension Flow

## Start of Extension

There is only 1 function that handles both initialization and updates to the extension

extensions_palette.py

```python
def add_child_window_to_monitor(self, child_window: AnalysisExtension):
"""
Add a child window to the run monitor.
Parameters
----------
child_window : AnalysisExtension
The child window (extension) to add to the run monitor.
"""
child_window.window_closed.connect(self.run_monitor.extension_window_closed)
self.run_monitor.active_extensions.append(child_window)

try:
if self.run_monitor.routine is not None:
child_window.update_window(self.run_monitor.routine)
except ValueError:
QMessageBox.warning(
self, "Extension is not applicable!", traceback.format_exc()
)
self.run_monitor.active_extensions.remove(child_window)
return

child_window.show()

self.update_palette()
```
56 changes: 47 additions & 9 deletions src/badger/gui/default/components/analysis_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,60 @@ def update_window(self, routine: Routine):


class BOVisualizer(AnalysisExtension):
df_length = 0

def __init__(self, parent: Optional[AnalysisExtension] = None):
logger.debug("Initializing BOVisualizer")
super().__init__(parent=parent)
self.setWindowTitle("BO Visualizer")
self.bo_plot_widget: Optional[BOPlotWidget] = None

def requires_update(self, routine: Routine):
if routine.data is None:
logger.debug("Reset - No data available")
return True

if self.bo_plot_widget.model_logic.xopt_obj is None:
logger.debug("Reset - xopt_obj is None")
return True

# Initialize BOPlotWidget without an xopt_obj
self.bo_plot_widget = BOPlotWidget()
if len(routine.data) <= self.df_length:
logger.debug("Reset - Data length is the same or smaller")
self.df_length = len(routine.data)
return True

bo_layout = QVBoxLayout()
bo_layout.addWidget(self.bo_plot_widget)
self.setLayout(bo_layout)
return False

def update_window(self, routine: Routine):
# Update the BOPlotWidget with the new routine
logger.debug("Updating BOVisualizer window with new routine")
logger.debug(f"isVisible: {self.bo_plot_widget.isVisible()}")
# Issue Identified: When continuing an optimization run, the BOVisualizer will update continuously and cause an infinite loop
# if not self.bo_plot_widget.isVisible():
self.bo_plot_widget.initialize_plot(routine)
self.df_length = len(routine.data)
# logger.debug(f"Routine {routine.data}")

# Initialize the BOPlotWidget if it is not already initialized
if self.bo_plot_widget is None:
# Initialize BOPlotWidget without an xopt_obj
self.bo_plot_widget = BOPlotWidget()

logger.debug("Initialized BOPlotWidget")

bo_layout = QVBoxLayout()
bo_layout.addWidget(self.bo_plot_widget)
self.setLayout(bo_layout)
logger.debug("Set layout for BOVisualizer")

else:
logger.debug("BOPlotWidget already initialized")

# If there is no data available, then initialize the plot
# This needs to happen when starting a new optimization run

if self.requires_update(routine):
self.bo_plot_widget.initialize_plot(routine)
logger.debug(f"Data: {self.bo_plot_widget.model_logic.xopt_obj.data}")
else:
logger.debug("BOPlotWidget already has data")

# Update the plot with every call to update_window
# This is necessary when continuing an optimization run
self.bo_plot_widget.update_plot(100)
24 changes: 9 additions & 15 deletions src/badger/gui/default/components/bo_visualizer/bo_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ def __init__(

self.setLayout(main_layout)

if xopt_obj:
self.initialize_plot(xopt_obj)
else:
logger.debug("xopt_obj is None. Defer setup until xopt_obj is available")
# Defer setup until xopt_obj is available
pass

self.setSizePolicy(self.sizePolicy().Expanding, self.sizePolicy().Expanding)
self.resize(1250, 720)

Expand All @@ -72,9 +65,9 @@ def initialize_plot(self, xopt_obj: Routine):
logger.debug("Triggering axis selection changed")
self.on_axis_selection_changed()

# Now it's safe to call update_plot
logger.debug("Calling update_plot")
self.update_plot()
# # Now it's safe to call update_plot
# logger.debug("Calling update_plot")
# self.update_plot()

def setup_connections(self):
# Disconnect existing connections
Expand Down Expand Up @@ -153,7 +146,7 @@ def on_axis_selection_changed(self):
# Only update plot if the selection has changed
self.update_plot()

def update_plot(self):
def update_plot(self, interval: Optional[float] = None):
logger.debug("Updating plot in BOPlotWidget")
if not self.model_logic.xopt_obj or not self.model_logic.vocs:
print("Cannot update plot: xopt_obj or vocs is not available.")
Expand Down Expand Up @@ -227,6 +220,7 @@ def update_plot(self):
self.ui_components.show_prior_mean_checkbox.isChecked(),
self.ui_components.show_feasibility_checkbox.isChecked(),
self.ui_components.n_grid.value(),
interval,
)

def update_reference_point_table(self, selected_variables: list[str]):
Expand All @@ -253,7 +247,7 @@ def update_reference_point_table(self, selected_variables: list[str]):
# Force the table to refresh and update its view
self.ui_components.reference_table.viewport().update()

def update_routine(self, xopt_obj: Routine):
logger.debug("Updating routine in BOPlotWidget")
logger.debug(f"xopt_obj: {xopt_obj}")
self.initialize_plot(xopt_obj)
# def update_routine(self, xopt_obj: Routine):
# logger.debug("Updating routine in BOPlotWidget")
# logger.debug(f"xopt_obj: {xopt_obj}")
# self.initialize_plot(xopt_obj)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


class ModelLogic:
def __init__(self, xopt_obj: Routine, vocs: VOCS):
def __init__(self, xopt_obj: Optional[Routine], vocs: Optional[VOCS]):
self.xopt_obj = xopt_obj
self.vocs = vocs

Expand Down
32 changes: 32 additions & 0 deletions src/badger/gui/default/components/bo_visualizer/plotting_area.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
from typing import Optional
from matplotlib import pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QMessageBox
from xopt.generators.bayesian.visualize import visualize_generator_model

from badger.routine import Routine
import time


import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


class PlottingArea(QWidget):
last_updated: float = None

def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)

# Create a layout for the plot area without pre-filling it with a plot
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.last_updated = time.time()

def update_plot(
self,
Expand All @@ -25,7 +36,22 @@ def update_plot(
show_prior_mean: bool,
show_feasibility: bool,
n_grid: int,
interval: Optional[float] = 1000.0, # Interval in milliseconds
):
logger.debug("Updating plot in PlottingArea")

# Check if the plot was updated recently
if self.last_updated is not None and interval is not None:
logger.debug(f"Time since last update: {time.time() - self.last_updated}")

time_diff = time.time() - self.last_updated
# If the plot was updated less than 1 second ago, skip this update
if time_diff < interval / 1000:
logger.debug("Skipping update")
return

logger.debug(f"layouts: {self.layout.count()}")

# Clear the existing layout (remove previous plot if any)
for i in reversed(range(self.layout.count())):
widget_to_remove = self.layout.itemAt(i).widget()
Expand Down Expand Up @@ -85,3 +111,9 @@ def update_plot(

# Ensure the layout is updated
self.updateGeometry()

# Close the old figure to prevent memory leaks
plt.close(fig)

# Update the last updated time
self.last_updated = time.time()

0 comments on commit ecc6efa

Please sign in to comment.