# Released under the MIT License. See LICENSE for details.
#
"""UIs for debugging purposes."""
from __future__ import annotations
import logging
from typing import cast, override
import bauiv1 as bui
import bascenev1 as bs
[docs]
class BenchmarksAndStressTestsWindow(bui.MainWindow):
"""Window for launching benchmarks or stress tests."""
def __init__(
self,
transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None,
):
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
# pylint: disable=cyclic-import
from bauiv1lib import popup
uiscale = bui.app.ui_v1.uiscale
self._width = width = 1200 if uiscale is bui.UIScale.SMALL else 580
self._height = height = (
900
if uiscale is bui.UIScale.SMALL
else 420 if uiscale is bui.UIScale.MEDIUM else 520
)
self._stress_test_game_type = 'Random'
self._stress_test_playlist = '__default__'
self._stress_test_player_count = 8
self._stress_test_round_duration = 30
# Do some fancy math to fill all available screen area up to the
# size of our backing container. This lets us fit to the exact
# screen shape at small ui scale.
screensize = bui.get_virtual_screen_size()
scale = (
2.32
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
)
# Calc screen size in our local container space and clamp to a
# bit smaller than our container size.
target_width = min(self._width - 70, screensize[0] / scale)
target_height = min(self._height - 70, screensize[1] / scale)
# To get top/left coords, go to the center of our window and
# offset by half the width/height of our target area.
yoffs = 0.5 * self._height + 0.5 * target_height + 30.0
self._scroll_width = target_width
self._scroll_height = target_height - 31
self._scroll_bottom = yoffs - 60 - self._scroll_height
self._sub_width = min(510.0, self._scroll_width)
self._sub_height = 520
self._r = 'debugWindow'
uiscale = bui.app.ui_v1.uiscale
super().__init__(
root_widget=bui.containerwidget(
size=(width, height),
scale=scale,
toolbar_visibility=(
'menu_minimal'
if uiscale is bui.UIScale.SMALL
else 'menu_full'
),
),
transition=transition,
origin_widget=origin_widget,
# We're affected by screen size only at small ui-scale.
refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
)
if bui.app.ui_v1.uiscale is bui.UIScale.SMALL:
bui.containerwidget(
edit=self._root_widget, on_cancel_call=self.main_window_back
)
self._back_button = bui.get_special_widget('back_button')
else:
self._back_button = btn = bui.buttonwidget(
parent=self._root_widget,
position=(40, yoffs - 53),
size=(60, 60),
scale=0.8,
autoselect=True,
label=bui.charstr(bui.SpecialChar.BACK),
button_type='backSmall',
on_activate_call=self.main_window_back,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
bui.textwidget(
parent=self._root_widget,
position=(
self._width * 0.5,
yoffs - (45 if uiscale is bui.UIScale.SMALL else 30),
),
size=(0, 0),
maxwidth=360,
scale=0.8 if uiscale is bui.UIScale.SMALL else 1.0,
text=bui.Lstr(resource=f'{self._r}.titleText'),
h_align='center',
v_align='center',
color=bui.app.ui_v1.title_color,
)
self._scrollwidget = bui.scrollwidget(
parent=self._root_widget,
highlight=False,
size=(self._scroll_width, self._scroll_height),
position=(
self._width * 0.5 - self._scroll_width * 0.5,
self._scroll_bottom,
),
border_opacity=0.4,
center_small_content_horizontally=True,
)
bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
self._subcontainer = bui.containerwidget(
parent=self._scrollwidget,
size=(self._sub_width, self._sub_height),
background=False,
)
v = self._sub_height - 70
button_width = 300
btn = bui.buttonwidget(
parent=self._subcontainer,
position=((self._sub_width - button_width) * 0.5, v),
size=(button_width, 60),
autoselect=True,
label=bui.Lstr(resource=f'{self._r}.runCPUBenchmarkText'),
on_activate_call=self._run_cpu_benchmark_pressed,
)
bui.widget(
edit=btn, up_widget=self._back_button, left_widget=self._back_button
)
v -= 60
bui.buttonwidget(
parent=self._subcontainer,
position=((self._sub_width - button_width) * 0.5, v),
size=(button_width, 60),
autoselect=True,
label=bui.Lstr(resource=f'{self._r}.runMediaReloadBenchmarkText'),
on_activate_call=self._run_media_reload_benchmark_pressed,
)
v -= 60
bui.textwidget(
parent=self._subcontainer,
position=(self._sub_width * 0.5, v + 22),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.stressTestTitleText'),
maxwidth=200,
color=bui.app.ui_v1.heading_color,
scale=0.85,
h_align='center',
v_align='center',
)
v -= 45
x_offs = 165
bui.textwidget(
parent=self._subcontainer,
position=(x_offs - 10, v + 22),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.stressTestPlaylistTypeText'),
maxwidth=130,
color=bui.app.ui_v1.heading_color,
scale=0.65,
h_align='right',
v_align='center',
)
popup.PopupMenu(
parent=self._subcontainer,
position=(x_offs, v),
width=150,
choices=['Random', 'Teams', 'Free-For-All'],
choices_display=[
bui.Lstr(resource=a)
for a in [
'randomText',
'playModes.teamsText',
'playModes.freeForAllText',
]
],
current_choice='Auto',
on_value_change_call=self._stress_test_game_type_selected,
)
v -= 46
bui.textwidget(
parent=self._subcontainer,
position=(x_offs - 10, v + 22),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.stressTestPlaylistNameText'),
maxwidth=130,
color=bui.app.ui_v1.heading_color,
scale=0.65,
h_align='right',
v_align='center',
)
self._stress_test_playlist_name_field = bui.textwidget(
parent=self._subcontainer,
position=(x_offs + 5, v - 5),
size=(250, 46),
text=self._stress_test_playlist,
h_align='left',
v_align='center',
autoselect=True,
color=(0.9, 0.9, 0.9, 1.0),
description=bui.Lstr(
resource=f'{self._r}.stressTestPlaylistDescriptionText'
),
editable=True,
padding=4,
)
v -= 29
x_sub = 60
# Player count.
bui.textwidget(
parent=self._subcontainer,
position=(x_offs - 10, v),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.stressTestPlayerCountText'),
color=(0.8, 0.8, 0.8, 1.0),
h_align='right',
v_align='center',
scale=0.65,
maxwidth=130,
)
self._stress_test_player_count_text = bui.textwidget(
parent=self._subcontainer,
position=(246 - x_sub, v - 14),
size=(60, 28),
editable=False,
color=(0.3, 1.0, 0.3, 1.0),
h_align='right',
v_align='center',
text=str(self._stress_test_player_count),
padding=2,
)
bui.buttonwidget(
parent=self._subcontainer,
position=(330 - x_sub, v - 11),
size=(28, 28),
label='-',
autoselect=True,
on_activate_call=bui.Call(self._stress_test_player_count_decrement),
repeat=True,
enable_sound=True,
)
bui.buttonwidget(
parent=self._subcontainer,
position=(380 - x_sub, v - 11),
size=(28, 28),
label='+',
autoselect=True,
on_activate_call=bui.Call(self._stress_test_player_count_increment),
repeat=True,
enable_sound=True,
)
v -= 42
# Round duration.
bui.textwidget(
parent=self._subcontainer,
position=(x_offs - 10, v),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.stressTestRoundDurationText'),
color=(0.8, 0.8, 0.8, 1.0),
h_align='right',
v_align='center',
scale=0.65,
maxwidth=130,
)
self._stress_test_round_duration_text = bui.textwidget(
parent=self._subcontainer,
position=(246 - x_sub, v - 14),
size=(60, 28),
editable=False,
color=(0.3, 1.0, 0.3, 1.0),
h_align='right',
v_align='center',
text=str(self._stress_test_round_duration),
padding=2,
)
bui.buttonwidget(
parent=self._subcontainer,
position=(330 - x_sub, v - 11),
size=(28, 28),
label='-',
autoselect=True,
on_activate_call=bui.Call(
self._stress_test_round_duration_decrement
),
repeat=True,
enable_sound=True,
)
bui.buttonwidget(
parent=self._subcontainer,
position=(380 - x_sub, v - 11),
size=(28, 28),
label='+',
autoselect=True,
on_activate_call=bui.Call(
self._stress_test_round_duration_increment
),
repeat=True,
enable_sound=True,
)
v -= 82
btn = bui.buttonwidget(
parent=self._subcontainer,
position=((self._sub_width - button_width) * 0.5, v),
size=(button_width, 60),
autoselect=True,
label=bui.Lstr(resource=f'{self._r}.runStressTestText'),
on_activate_call=self._stress_test_pressed,
)
bui.widget(edit=btn, show_buffer_bottom=50)
[docs]
@override
def get_main_window_state(self) -> bui.MainWindowState:
# Support recreating our window for back/refresh purposes.
cls = type(self)
return bui.BasicMainWindowState(
create_call=lambda transition, origin_widget: cls(
transition=transition, origin_widget=origin_widget
)
)
def _stress_test_player_count_decrement(self) -> None:
self._stress_test_player_count = max(
1, self._stress_test_player_count - 1
)
bui.textwidget(
edit=self._stress_test_player_count_text,
text=str(self._stress_test_player_count),
)
def _stress_test_player_count_increment(self) -> None:
self._stress_test_player_count = self._stress_test_player_count + 1
bui.textwidget(
edit=self._stress_test_player_count_text,
text=str(self._stress_test_player_count),
)
def _stress_test_round_duration_decrement(self) -> None:
self._stress_test_round_duration = max(
10, self._stress_test_round_duration - 10
)
bui.textwidget(
edit=self._stress_test_round_duration_text,
text=str(self._stress_test_round_duration),
)
def _stress_test_round_duration_increment(self) -> None:
self._stress_test_round_duration = self._stress_test_round_duration + 10
bui.textwidget(
edit=self._stress_test_round_duration_text,
text=str(self._stress_test_round_duration),
)
def _stress_test_game_type_selected(self, game_type: str) -> None:
self._stress_test_game_type = game_type
def _run_cpu_benchmark_pressed(self) -> None:
if bui.app.classic is None:
logging.warning('run-cpu-benchmark requires classic')
return
bui.app.classic.run_cpu_benchmark()
def _run_media_reload_benchmark_pressed(self) -> None:
if bui.app.classic is None:
logging.warning('run-media-reload-benchmark requires classic')
return
bui.app.classic.run_media_reload_benchmark()
def _stress_test_pressed(self) -> None:
from bascenev1lib.mainmenu import MainMenuActivity
if bui.app.classic is None:
logging.warning('stress-test requires classic')
return
activity = bs.get_foreground_host_activity()
if isinstance(activity, MainMenuActivity):
bui.app.classic.run_stress_test(
playlist_type=self._stress_test_game_type,
playlist_name=cast(
str,
bui.textwidget(query=self._stress_test_playlist_name_field),
),
player_count=self._stress_test_player_count,
round_duration=self._stress_test_round_duration,
)
bui.containerwidget(edit=self._root_widget, transition='out_right')
else:
bui.screenmessage(
bui.Lstr(value='Already present in another activity.')
)
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum