Skip to content

Commit 22ca1ac

Browse files
SIM RFC: AI selector interface (UBC-Thunderbots#3568)
* feat: ai selector interface * refactor: use RuntimeManager Interface * fix: update names * [pre-commit.ci lite] apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 490f19d commit 22ca1ac

4 files changed

Lines changed: 128 additions & 2 deletions

File tree

src/software/thunderscope/binary_context_managers/runtime_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def fetch_installed_runtimes(self) -> list[str]:
2929
"""
3030
return self.runtime_loader.fetch_installed_runtimes()
3131

32-
def load_existing_runtime(self, yellow_runtime: str, blue_runtime: str) -> None:
32+
def load_existing_runtimes(self, yellow_runtime: str, blue_runtime: str) -> None:
3333
"""Loads the runtimes of the specified name or throws an error upon failure.
3434
:param blue_runtime: name of the blue runtime to load
3535
:param yellow_runtime: name of the yellow runtime to load

src/software/thunderscope/gl/widgets/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ py_library(
4343
srcs = ["gl_gamecontroller_toolbar.py"],
4444
deps = [
4545
":gl_runtime_installer",
46+
":gl_runtime_selector",
4647
":gl_toolbar",
4748
"//proto:import_all_protos",
4849
"//software/networking:ssl_proto_communication",
@@ -52,3 +53,11 @@ py_library(
5253
requirement("qtawesome"),
5354
],
5455
)
56+
57+
py_library(
58+
name = "gl_runtime_selector",
59+
srcs = ["gl_runtime_selector.py"],
60+
deps = [
61+
requirement("pyqtgraph"),
62+
],
63+
)

src/software/thunderscope/gl/widgets/gl_gamecontroller_toolbar.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
from proto.ssl_gc_common_pb2 import Team as SslTeam
55
from typing import Callable, override
66
import webbrowser
7+
from software.thunderscope.binary_context_managers.runtime_manager import (
8+
runtime_manager_instance,
9+
)
10+
from software.thunderscope.gl.widgets.gl_runtime_selector import GLRuntimeSelectorDialog
711
from software.thunderscope.gl.widgets.gl_toolbar import GLToolbar
812
from software.thunderscope.proto_unix_io import ProtoUnixIO
913
from software.thunderscope.gl.widgets.gl_runtime_installer import (
@@ -105,6 +109,13 @@ def __init__(
105109
display_text="Install Runtimes",
106110
)
107111

112+
self.runtime_selector_button = self.__setup_icon_button(
113+
qta.icon("mdi6.server"),
114+
"Select runtimes for each team",
115+
self.__open_runtime_selector_dialog,
116+
display_text="Select Runtimes",
117+
)
118+
108119
# disable the normal start button when no play is selected
109120
self.normal_start_enabled = True
110121
self.__toggle_normal_start_button()
@@ -122,6 +133,7 @@ def __init__(
122133
self.layout().addStretch()
123134
self.__add_separator(self.layout())
124135
self.layout().addWidget(self.runtime_installer_button)
136+
self.layout().addWidget(self.runtime_selector_button)
125137

126138
@override
127139
def refresh(self) -> None:
@@ -261,11 +273,24 @@ def __send_gc_command(self, command: Command.Type, team: Team) -> None:
261273
command = ManualGCCommand(manual_command=Command(type=command, for_team=team))
262274
self.proto_unix_io.send_proto(ManualGCCommand, command)
263275

264-
def __open_runtime_installer_dialog(self):
276+
def __open_runtime_installer_dialog(self) -> None:
265277
"""Opens the runtime installer modal, initializing if first time"""
266278
if not hasattr(self, "runtime_installer_dialog"):
267279
self.runtime_installer_dialog = GLRuntimeInstallerDialog(
268280
parent=self.parent()
269281
)
270282

271283
self.runtime_installer_dialog.show()
284+
285+
def __open_runtime_selector_dialog(self) -> None:
286+
"""Opens the runtime selector dialog, initializing if first time"""
287+
if not hasattr(self, "runtime_selector_dialog"):
288+
options = ["Current Commit"]
289+
options.extend(runtime_manager_instance.fetch_installed_runtimes())
290+
self.runtime_selector_dialog = GLRuntimeSelectorDialog(
291+
parent=self.parent(),
292+
runtime_options=options,
293+
on_runtimes_selected=runtime_manager_instance.load_existing_runtimes,
294+
)
295+
296+
self.runtime_selector_dialog.show()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from pyqtgraph.Qt.QtWidgets import (
2+
QDialog,
3+
QWidget,
4+
QVBoxLayout,
5+
QHBoxLayout,
6+
QLabel,
7+
QPushButton,
8+
QComboBox,
9+
)
10+
from typing import Callable
11+
12+
13+
class GLRuntimeSelectorDialog(QDialog):
14+
"""Modal that displays the selectable list of runtimes for yellow and blue teams"""
15+
16+
def __init__(
17+
self,
18+
parent: QWidget,
19+
runtime_options: list[str],
20+
on_runtimes_selected: Callable[[str, str], None],
21+
):
22+
"""Initializes the runtime selector modal, displaying the same list of installed
23+
runtimes for both the blue and yellow teams.
24+
25+
:param parent: the modal's parent
26+
:param runtime_options: the list of runtime options to display in both menus
27+
:param on_runtimes_selected: the callback for runtime selection
28+
"""
29+
super().__init__(parent)
30+
31+
self.on_runtimes_selected = on_runtimes_selected
32+
33+
self.setWindowTitle("Select Runtimes")
34+
self.setModal(True)
35+
self.setMinimumWidth(400)
36+
37+
layout = QVBoxLayout(self)
38+
39+
# Yellow runtime
40+
layout.addWidget(QLabel("<b>Yellow Runtime</b>"))
41+
self.yellow_menu = QComboBox()
42+
self.yellow_menu.addItems(runtime_options)
43+
self.yellow_menu.currentTextChanged.connect(self._on_yellow_changed)
44+
self._yellow_selection = self.yellow_menu.currentText()
45+
layout.addWidget(self.yellow_menu)
46+
47+
# Blue runtime
48+
layout.addSpacing(10)
49+
layout.addWidget(QLabel("<b>Blue Runtime</b>"))
50+
self.blue_menu = QComboBox()
51+
self.blue_menu.addItems(runtime_options)
52+
self.blue_menu.currentTextChanged.connect(self._on_blue_changed)
53+
self._blue_selection = self.blue_menu.currentText()
54+
layout.addWidget(self.blue_menu)
55+
56+
# Restart note
57+
layout.addSpacing(15)
58+
restart_note = QLabel(
59+
"<i>Note: Restart Thunderscope for changes to take effect.</i>"
60+
)
61+
layout.addWidget(restart_note)
62+
63+
# Done button
64+
layout.addSpacing(15)
65+
button_row = QHBoxLayout()
66+
button_row.addStretch()
67+
68+
done_button = QPushButton("Done")
69+
done_button.clicked.connect(self._on_done)
70+
button_row.addWidget(done_button)
71+
72+
layout.addLayout(button_row)
73+
74+
def _on_yellow_changed(self, value: str) -> None:
75+
"""Stores currently selected runtime for yellow team
76+
77+
:param value: the value of the selected option
78+
"""
79+
self._yellow_selection = value
80+
81+
def _on_blue_changed(self, value: str) -> None:
82+
"""Stores currently selected runtime for blue team
83+
84+
:param value: the value of the selected option
85+
"""
86+
self._blue_selection = value
87+
88+
def _on_done(self) -> None:
89+
"""Commits the selected runtimes and closes the modal."""
90+
self.on_runtimes_selected(self._yellow_selection, self._blue_selection)
91+
92+
self.close()

0 commit comments

Comments
 (0)