Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/software/thunderscope/gl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ py_library(
"//software/thunderscope/gl/layers:gl_measure_layer",
"//software/thunderscope/gl/widgets:gl_field_toolbar",
"//software/thunderscope/gl/widgets:gl_gamecontroller_toolbar",
"//software/thunderscope/gl/widgets:gl_simulated_test_toolbar",
"//software/thunderscope/replay:replay_controls",
requirement("pyqtgraph"),
],
Expand Down
38 changes: 24 additions & 14 deletions src/software/thunderscope/gl/gl_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from software.thunderscope.gl.widgets.gl_gamecontroller_toolbar import (
GLGamecontrollerToolbar,
)
from software.thunderscope.gl.widgets.gl_simulated_test_toolbar import (
GLSimulatedTestToolbar,
)
from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer
from proto.world_pb2 import SimulationState
from proto.replay_bookmark_pb2 import ReplayBookmark
Expand Down Expand Up @@ -113,7 +116,12 @@ def __init__(
friendly_color_yellow=friendly_color_yellow,
)

self.__add_toolbar_toggle(self.gamecontroller_toolbar, "Gamecontroller")
self.simulated_test_toolbar = GLSimulatedTestToolbar(parent=self.gl_view_widget)

self.__add_toolbar_select(self.gamecontroller_toolbar, "Gamecontroller")
self.__add_toolbar_select(self.simulated_test_toolbar, "Tests")
self.current_toolbar = self.gamecontroller_toolbar
self.simulated_test_toolbar.setVisible(False)

# Setup replay controls if player is provided and the log has some size
self.player = player
Expand Down Expand Up @@ -269,6 +277,7 @@ def refresh(self) -> None:
if self.simulation_control_toolbar:
self.simulation_control_toolbar.refresh()
self.gamecontroller_toolbar.refresh()
self.simulated_test_toolbar.refresh()

simulation_state = self.simulation_state_buffer.get(block=False)
# Don't refresh the layers if the simulation is paused
Expand Down Expand Up @@ -322,22 +331,23 @@ def toggle_measure_mode(self) -> None:
else:
self.remove_layer(self.measure_layer)

def __add_toolbar_toggle(self, toolbar: QWidget, name: str) -> None:
"""Adds a button to the toolbar menu to toggle the given toolbar
def __select_toolbar(self, toolbar: QWidget) -> None:
"""Sets the currently selected toolbar to be only one visible

:param toolbar: the toolbar to add the toggle button for
:param name: the display name of the toolbar
:param toolbar: the toolbar to select
"""
# Add a menu item for the Gamecontroller toolbar
(toolbar_checkbox, toolbar_action) = self.__setup_menu_checkbox(
name, self.toolbars_menu
)
self.toolbars_menu.addAction(toolbar_action)
self.gamecontroller_toolbar.setVisible(False)
self.simulated_test_toolbar.setVisible(False)
toolbar.setVisible(True)
self.current_toolbar = toolbar

# Connect visibility of the toolbar to the menu item
toolbar_checkbox.stateChanged.connect(
lambda: toolbar.setVisible(toolbar_checkbox.isChecked())
)
def __add_toolbar_select(self, toolbar: QWidget, name: str) -> None:
"""Adds a button to the toolbar menu to select the given toolbar

:param toolbar: the toolbar to add the select button for
:param name: the display name of the toolbar
"""
self.toolbars_menu.addAction(name, lambda: self.__select_toolbar(toolbar))

def __setup_menu_checkbox(
self, name: str, parent: QWidget, checked: bool = True
Expand Down
11 changes: 11 additions & 0 deletions src/software/thunderscope/gl/widgets/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ py_library(
requirement("qtawesome"),
],
)

py_library(
name = "gl_simulated_test_toolbar",
srcs = ["gl_simulated_test_toolbar.py"],
deps = [
":gl_toolbar",
"//software/thunderscope/common:common_widgets",
requirement("pyqtgraph"),
requirement("qtawesome"),
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pyqtgraph.Qt import QtGui
from proto.import_all_protos import *
from proto.ssl_gc_common_pb2 import Team as SslTeam
from typing import Callable, override
from typing import override
import webbrowser
from software.thunderscope.gl.widgets.gl_runtime_selector import GLRuntimeSelectorDialog
from software.thunderscope.gl.widgets.gl_toolbar import GLToolbar
Expand Down Expand Up @@ -52,28 +52,28 @@ def __init__(
self.friendly_color_yellow = friendly_color_yellow

# Setup Stop button for sending the STOP gamecontroller command
self.stop_button = self.__setup_icon_button(
self.stop_button = self.setup_icon_button(
qta.icon("fa6s.pause"),
"Stops gameplay, robots form circle around ball",
self.__send_stop_command,
)

# Setup Force Start button for sending the FORCE_START gamecontroller command
self.force_start_button = self.__setup_icon_button(
self.force_start_button = self.setup_icon_button(
qta.icon("ph.arrow-u-up-right-fill"),
"Force Start, restarts the game",
self.__send_force_start_command,
)

# Setup Halt button for sending the HALT gamecontroller command
self.halt_button = self.__setup_icon_button(
self.halt_button = self.setup_icon_button(
qta.icon("fa5s.stop"),
"Halt, stops all robots immediately",
self.__send_halt_command,
)

# Setup Normal Start button for sending the NORMAL_START gamecontroller command
self.normal_start_button = self.__setup_icon_button(
self.normal_start_button = self.setup_icon_button(
qta.icon("fa5s.play"),
"Normal Start, resumes game from a set play (disabled when no play selected)",
self.__send_normal_start_command,
Expand All @@ -92,7 +92,7 @@ def __init__(
self.plays_menu.addSeparator()
self.__add_plays_menu_items(is_blue=False)

self.gc_browser_button = self.__setup_icon_button(
self.gc_browser_button = self.setup_icon_button(
qta.icon("mdi6.open-in-new"),
"Opens the SSL Gamecontroller in a browser window",
lambda: webbrowser.open(self.GAME_CONTROLLER_URL, new=0, autoraise=True),
Expand All @@ -118,14 +118,14 @@ def __init__(
self.__toggle_normal_start_button()

self.layout().addWidget(QLabel("<b>Gamecontroller</b>"))
self.__add_separator(self.layout())
self.add_separator(self.layout())
self.layout().addWidget(self.stop_button)
self.layout().addWidget(self.halt_button)
self.layout().addWidget(self.force_start_button)
self.__add_separator(self.layout())
self.add_separator(self.layout())
self.layout().addWidget(self.plays_menu_button)
self.layout().addWidget(self.normal_start_button)
self.__add_separator(self.layout())
self.add_separator(self.layout())
self.layout().addWidget(self.gc_browser_button)
self.layout().addStretch()
self.__add_separator(self.layout())
Expand All @@ -137,15 +137,6 @@ def refresh(self) -> None:
"""Refreshes the UI to update toolbar position"""
self.move(0, self.parentWidget().geometry().bottom() - self.height())

def __add_separator(self, layout: QBoxLayout) -> None:
"""Adds a separator line with enough spacing to the given layout

:param layout: the layout to add the separator to
"""
layout.addSpacing(10)
layout.addWidget(QLabel("<b>|</b>"))
layout.addSpacing(10)

def __add_plays_menu_items(self, is_blue: bool) -> None:
"""Initializes the plays menu with the available plays for the given team

Expand Down Expand Up @@ -210,31 +201,6 @@ def __toggle_normal_start_button(self) -> None:
)
)

def __setup_icon_button(
self,
icon: QtGui.QPixmap,
tooltip: str,
callback: Callable[[], None],
display_text: str = None,
) -> QPushButton:
"""Sets up a button with the given name and callback

:param icon: the icon displayed on the button
:param tooltip: the tooltip displayed when hovering over the button
:param callback: the callback for the button click
:param display_text: optional param if button needs both text and an icon
:return: the button
"""
button = QPushButton()
button.setIcon(icon)
button.setToolTip(tooltip)
button.setStyleSheet(self.get_button_style())
button.clicked.connect(callback)

if display_text:
button.setText(display_text)
return button

def __send_stop_command(self) -> None:
"""Sends a STOP command to the gamecontroller"""
self.__send_gc_command(Command.Type.STOP, SslTeam.UNKNOWN)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pyqtgraph.Qt import QtWidgets
from software.thunderscope.gl.widgets.gl_toolbar import GLToolbar
import qtawesome as qta
from typing import override


class GLSimulatedTestToolbar(GLToolbar):
"""A toolbar with controls to run simulated tests within Thunderscope"""

def __init__(self, parent: QtWidgets.QWidget):
"""Initializes the toolbar and constructs its layout

:param parent: the parent to overlay this toolbar over
"""
super(GLSimulatedTestToolbar, self).__init__(parent=parent)

self.run_test_button = self.setup_icon_button(
qta.icon("fa5s.play"),
"Runs simluated test",
self.__run_test,
)

self.layout().addWidget(QtWidgets.QLabel("<b>Simulated Tests</b>"))
self.add_separator(self.layout())
self.layout().addWidget(self.run_test_button)

@override
def refresh(self) -> None:
"""Refreshes the UI to update toolbar position"""
self.move(0, self.parentWidget().geometry().bottom() - self.height())

def __run_test(self):
print("RUN TEST")
37 changes: 36 additions & 1 deletion src/software/thunderscope/gl/widgets/gl_toolbar.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import textwrap
from pyqtgraph.Qt import QtCore
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.Qt.QtWidgets import *
from typing import Callable


class GLToolbar(QWidget):
Expand Down Expand Up @@ -54,3 +55,37 @@ def get_button_style(self, is_enabled: bool = True) -> str:
}}
"""
)

def setup_icon_button(
self,
icon: QtGui.QPixmap,
tooltip: str,
callback: Callable[[], None],
display_text: str = None,
) -> QPushButton:
"""Sets up a button with the given name and callback

:param icon: the icon displayed on the button
:param tooltip: the tooltip displayed when hovering over the button
:param callback: the callback for the button click
:param display_text: optional param if button needs both text and an icon
:return: the button
"""
button = QPushButton()
button.setIcon(icon)
button.setToolTip(tooltip)
button.setStyleSheet(self.get_button_style())
button.clicked.connect(callback)

if display_text:
button.setText(display_text)
return button

def add_separator(self, layout: QBoxLayout) -> None:
"""Adds a separator line with enough spacing to the given layout

:param layout: the layout to add the separator to
"""
layout.addSpacing(10)
layout.addWidget(QLabel("<b>|</b>"))
layout.addSpacing(10)
Loading