bauiv1lib.settings.nettesting
Provides ui for network related testing.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides ui for network related testing.""" 4 5from __future__ import annotations 6 7import time 8import copy 9import weakref 10from threading import Thread 11from typing import TYPE_CHECKING, override 12 13from efro.error import CleanError 14from bauiv1lib.settings.testing import TestingWindow 15import bauiv1 as bui 16 17if TYPE_CHECKING: 18 from typing import Callable, Any 19 20# We generally want all net tests to timeout on their own, but we add 21# sort of sane max in case they don't. 22MAX_TEST_SECONDS = 60 * 2 23 24 25class NetTestingWindow(bui.MainWindow): 26 """Window that runs a networking test suite to help diagnose issues.""" 27 28 def __init__( 29 self, 30 transition: str | None = 'in_right', 31 origin_widget: bui.Widget | None = None, 32 ): 33 uiscale = bui.app.ui_v1.uiscale 34 self._width = 820 35 self._height = 400 if uiscale is bui.UIScale.SMALL else 500 36 self._printed_lines: list[str] = [] 37 assert bui.app.classic is not None 38 super().__init__( 39 root_widget=bui.containerwidget( 40 size=(self._width, self._height), 41 scale=( 42 1.75 43 if uiscale is bui.UIScale.SMALL 44 else 1.2 if uiscale is bui.UIScale.MEDIUM else 0.8 45 ), 46 stack_offset=(0, -4 if uiscale is bui.UIScale.SMALL else 0.0), 47 toolbar_visibility=( 48 'menu_minimal' 49 if uiscale is bui.UIScale.SMALL 50 else 'menu_full' 51 ), 52 ), 53 transition=transition, 54 origin_widget=origin_widget, 55 ) 56 self._done_button: bui.Widget | None = bui.buttonwidget( 57 parent=self._root_widget, 58 position=(46, self._height - 77), 59 size=(120, 60), 60 scale=0.8, 61 autoselect=True, 62 label=bui.Lstr(resource='doneText'), 63 on_activate_call=self.main_window_back, 64 ) 65 66 # Avoid squads button on small mode. 67 xinset = -50 if uiscale is bui.UIScale.SMALL else 0 68 69 self._copy_button = bui.buttonwidget( 70 parent=self._root_widget, 71 position=(self._width - 200 + xinset, self._height - 77), 72 size=(100, 60), 73 scale=0.8, 74 autoselect=True, 75 label=bui.Lstr(resource='copyText'), 76 on_activate_call=self._copy, 77 ) 78 79 self._settings_button = bui.buttonwidget( 80 parent=self._root_widget, 81 position=(self._width - 100 + xinset, self._height - 77), 82 size=(60, 60), 83 scale=0.8, 84 autoselect=True, 85 label=bui.Lstr(value='...'), 86 on_activate_call=self._show_val_testing, 87 ) 88 89 twidth = self._width - 540 90 bui.textwidget( 91 parent=self._root_widget, 92 position=(self._width * 0.5, self._height - 55), 93 size=(0, 0), 94 text=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 95 color=(0.8, 0.8, 0.8, 1.0), 96 h_align='center', 97 v_align='center', 98 maxwidth=twidth, 99 ) 100 101 self._scroll = bui.scrollwidget( 102 parent=self._root_widget, 103 position=(50, 50), 104 size=(self._width - 100, self._height - 140), 105 capture_arrows=True, 106 autoselect=True, 107 ) 108 self._rows = bui.columnwidget(parent=self._scroll) 109 110 if uiscale is bui.UIScale.SMALL: 111 bui.containerwidget( 112 edit=self._root_widget, on_cancel_call=self.main_window_back 113 ) 114 self._done_button.delete() 115 self._done_button = None 116 else: 117 bui.containerwidget( 118 edit=self._root_widget, cancel_button=self._done_button 119 ) 120 121 # Now kick off the tests. 122 # Pass a weak-ref to this window so we don't keep it alive 123 # if we back out before it completes. Also set is as daemon 124 # so it doesn't keep the app running if the user is trying to quit. 125 Thread( 126 daemon=True, 127 target=bui.Call(_run_diagnostics, weakref.ref(self)), 128 ).start() 129 130 @override 131 def get_main_window_state(self) -> bui.MainWindowState: 132 # Support recreating our window for back/refresh purposes. 133 cls = type(self) 134 return bui.BasicMainWindowState( 135 create_call=lambda transition, origin_widget: cls( 136 transition=transition, origin_widget=origin_widget 137 ) 138 ) 139 140 def print(self, text: str, color: tuple[float, float, float]) -> None: 141 """Print text to our console thingie.""" 142 for line in text.splitlines(): 143 txt = bui.textwidget( 144 parent=self._rows, 145 color=color, 146 text=line, 147 scale=0.75, 148 flatness=1.0, 149 shadow=0.0, 150 size=(0, 20), 151 ) 152 bui.containerwidget(edit=self._rows, visible_child=txt) 153 self._printed_lines.append(line) 154 155 def _copy(self) -> None: 156 if not bui.clipboard_is_supported(): 157 bui.screenmessage( 158 'Clipboard not supported on this platform.', color=(1, 0, 0) 159 ) 160 return 161 bui.clipboard_set_text('\n'.join(self._printed_lines)) 162 bui.screenmessage(f'{len(self._printed_lines)} lines copied.') 163 164 def _show_val_testing(self) -> None: 165 assert bui.app.classic is not None 166 167 # no-op if our underlying widget is dead or on its way out. 168 if not self._root_widget or self._root_widget.transitioning_out: 169 return 170 171 bui.app.ui_v1.set_main_window(NetValTestingWindow(), from_window=self) 172 bui.containerwidget(edit=self._root_widget, transition='out_left') 173 174 # def _done(self) -> None: 175 # # pylint: disable=cyclic-import 176 # from bauiv1lib.settings.advanced import AdvancedSettingsWindow 177 178 # # no-op if our underlying widget is dead or on its way out. 179 # if not self._root_widget or self._root_widget.transitioning_out: 180 # return 181 182 # assert bui.app.classic is not None 183 # bui.app.ui_v1.set_main_window( 184 # AdvancedSettingsWindow(transition='in_left'), 185 # from_window=self, 186 # is_back=True, 187 # ) 188 # bui.containerwidget(edit=self._root_widget, transition='out_right') 189 190 191def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None: 192 # pylint: disable=too-many-statements 193 # pylint: disable=too-many-branches 194 195 from efro.util import utc_now 196 197 have_error = [False] 198 199 # We're running in a background thread but UI stuff needs to run 200 # in the logic thread; give ourself a way to pass stuff to it. 201 def _print( 202 text: str, color: tuple[float, float, float] | None = None 203 ) -> None: 204 def _print_in_logic_thread() -> None: 205 win = weakwin() 206 if win is not None: 207 win.print(text, (1.0, 1.0, 1.0) if color is None else color) 208 209 bui.pushcall(_print_in_logic_thread, from_other_thread=True) 210 211 def _print_test_results(call: Callable[[], Any]) -> bool: 212 """Run the provided call, print result, & return success.""" 213 starttime = time.monotonic() 214 try: 215 call() 216 duration = time.monotonic() - starttime 217 _print(f'Succeeded in {duration:.2f}s.', color=(0, 1, 0)) 218 return True 219 except Exception as exc: 220 import traceback 221 222 duration = time.monotonic() - starttime 223 msg = ( 224 str(exc) 225 if isinstance(exc, CleanError) 226 else traceback.format_exc() 227 ) 228 _print(msg, color=(1.0, 1.0, 0.3)) 229 _print(f'Failed in {duration:.2f}s.', color=(1, 0, 0)) 230 have_error[0] = True 231 return False 232 233 try: 234 plus = bui.app.plus 235 assert plus is not None 236 237 assert bui.app.classic is not None 238 239 _print( 240 f'Running network diagnostics...\n' 241 f'ua: {bui.app.classic.legacy_user_agent_string}\n' 242 f'time: {utc_now()}.' 243 ) 244 245 if bool(False): 246 _print('\nRunning dummy success test...') 247 _print_test_results(_dummy_success) 248 249 _print('\nRunning dummy fail test...') 250 _print_test_results(_dummy_fail) 251 252 # V1 ping 253 baseaddr = plus.get_master_server_address(source=0, version=1) 254 _print(f'\nContacting V1 master-server src0 ({baseaddr})...') 255 v1worked = _print_test_results(lambda: _test_fetch(baseaddr)) 256 257 # V1 alternate ping (only if primary fails since this often fails). 258 if v1worked: 259 _print('\nSkipping V1 master-server src1 test since src0 worked.') 260 else: 261 baseaddr = plus.get_master_server_address(source=1, version=1) 262 _print(f'\nContacting V1 master-server src1 ({baseaddr})...') 263 _print_test_results(lambda: _test_fetch(baseaddr)) 264 265 if 'none succeeded' in bui.app.net.v1_test_log: 266 _print( 267 f'\nV1-test-log failed: {bui.app.net.v1_test_log}', 268 color=(1, 0, 0), 269 ) 270 have_error[0] = True 271 else: 272 _print(f'\nV1-test-log ok: {bui.app.net.v1_test_log}') 273 274 for srcid, result in sorted(bui.app.net.v1_ctest_results.items()): 275 _print(f'\nV1 src{srcid} result: {result}') 276 277 curv1addr = plus.get_master_server_address(version=1) 278 _print(f'\nUsing V1 address: {curv1addr}') 279 280 if plus.get_v1_account_state() == 'signed_in': 281 _print('\nRunning V1 transaction...') 282 _print_test_results(_test_v1_transaction) 283 else: 284 _print('\nSkipping V1 transaction (Not signed into V1).') 285 286 # V2 ping 287 baseaddr = plus.get_master_server_address(version=2) 288 _print(f'\nContacting V2 master-server ({baseaddr})...') 289 _print_test_results(lambda: _test_fetch(baseaddr)) 290 291 _print('\nComparing local time to V2 server...') 292 _print_test_results(_test_v2_time) 293 294 # Get V2 nearby zone 295 with bui.app.net.zone_pings_lock: 296 zone_pings = copy.deepcopy(bui.app.net.zone_pings) 297 nearest_zone = ( 298 None 299 if not zone_pings 300 else sorted(zone_pings.items(), key=lambda i: i[1])[0] 301 ) 302 303 if nearest_zone is not None: 304 nearstr = f'{nearest_zone[0]}: {nearest_zone[1]:.0f}ms' 305 else: 306 nearstr = '-' 307 _print(f'\nChecking nearest V2 zone ping ({nearstr})...') 308 _print_test_results(lambda: _test_nearby_zone_ping(nearest_zone)) 309 310 _print('\nSending V2 cloud message...') 311 _print_test_results(_test_v2_cloud_message) 312 313 if have_error[0]: 314 _print( 315 '\nDiagnostics complete. Some diagnostics failed.', 316 color=(10, 0, 0), 317 ) 318 else: 319 _print( 320 '\nDiagnostics complete. Everything looks good!', 321 color=(0, 1, 0), 322 ) 323 except Exception: 324 import traceback 325 326 _print( 327 f'An unexpected error occurred during testing;' 328 f' please report this.\n' 329 f'{traceback.format_exc()}', 330 color=(1, 0, 0), 331 ) 332 333 334def _dummy_success() -> None: 335 """Dummy success test.""" 336 time.sleep(1.2) 337 338 339def _dummy_fail() -> None: 340 """Dummy fail test case.""" 341 raise RuntimeError('fail-test') 342 343 344def _test_v1_transaction() -> None: 345 """Dummy fail test case.""" 346 plus = bui.app.plus 347 assert plus is not None 348 349 if plus.get_v1_account_state() != 'signed_in': 350 raise RuntimeError('Not signed in.') 351 352 starttime = time.monotonic() 353 354 # Gets set to True on success or string on error. 355 results: list[Any] = [False] 356 357 def _cb(cbresults: Any) -> None: 358 # Simply set results here; our other thread acts on them. 359 if not isinstance(cbresults, dict) or 'party_code' not in cbresults: 360 results[0] = 'Unexpected transaction response' 361 return 362 results[0] = True # Success! 363 364 def _do_it() -> None: 365 assert plus is not None 366 # Fire off a transaction with a callback. 367 plus.add_v1_account_transaction( 368 { 369 'type': 'PRIVATE_PARTY_QUERY', 370 'expire_time': time.time() + 20, 371 }, 372 callback=_cb, 373 ) 374 plus.run_v1_account_transactions() 375 376 bui.pushcall(_do_it, from_other_thread=True) 377 378 while results[0] is False: 379 time.sleep(0.01) 380 if time.monotonic() - starttime > MAX_TEST_SECONDS: 381 raise RuntimeError( 382 f'test timed out after {MAX_TEST_SECONDS} seconds' 383 ) 384 385 # If we got left a string, its an error. 386 if isinstance(results[0], str): 387 raise RuntimeError(results[0]) 388 389 390def _test_v2_cloud_message() -> None: 391 from dataclasses import dataclass 392 import bacommon.cloud 393 394 @dataclass 395 class _Results: 396 errstr: str | None = None 397 send_time: float | None = None 398 response_time: float | None = None 399 400 results = _Results() 401 402 def _cb(response: bacommon.cloud.PingResponse | Exception) -> None: 403 # Note: this runs in another thread so need to avoid exceptions. 404 results.response_time = time.monotonic() 405 if isinstance(response, Exception): 406 results.errstr = str(response) 407 if not isinstance(response, bacommon.cloud.PingResponse): 408 results.errstr = f'invalid response type: {type(response)}.' 409 410 def _send() -> None: 411 # Note: this runs in another thread so need to avoid exceptions. 412 results.send_time = time.monotonic() 413 assert bui.app.plus is not None 414 bui.app.plus.cloud.send_message_cb(bacommon.cloud.PingMessage(), _cb) 415 416 # This stuff expects to be run from the logic thread. 417 bui.pushcall(_send, from_other_thread=True) 418 419 wait_start_time = time.monotonic() 420 while True: 421 if results.response_time is not None: 422 break 423 time.sleep(0.01) 424 if time.monotonic() - wait_start_time > MAX_TEST_SECONDS: 425 raise RuntimeError( 426 f'Timeout ({MAX_TEST_SECONDS} seconds)' 427 f' waiting for cloud message response' 428 ) 429 if results.errstr is not None: 430 raise RuntimeError(results.errstr) 431 432 433def _test_v2_time() -> None: 434 offset = bui.app.net.server_time_offset_hours 435 if offset is None: 436 raise RuntimeError( 437 'no time offset found;' 438 ' perhaps unable to communicate with v2 server?' 439 ) 440 if abs(offset) >= 2.0: 441 raise CleanError( 442 f'Your device time is off from world time by {offset:.1f} hours.\n' 443 'This may cause network operations to fail due to your device\n' 444 ' incorrectly treating SSL certificates as not-yet-valid, etc.\n' 445 'Check your device time and time-zone settings to fix this.\n' 446 ) 447 448 449def _test_fetch(baseaddr: str) -> None: 450 # pylint: disable=consider-using-with 451 import urllib.request 452 453 assert bui.app.classic is not None 454 response = urllib.request.urlopen( 455 urllib.request.Request( 456 f'{baseaddr}/ping', 457 None, 458 {'User-Agent': bui.app.classic.legacy_user_agent_string}, 459 ), 460 context=bui.app.net.sslcontext, 461 timeout=MAX_TEST_SECONDS, 462 ) 463 if response.getcode() != 200: 464 raise RuntimeError( 465 f'Got unexpected response code {response.getcode()}.' 466 ) 467 data = response.read() 468 if data != b'pong': 469 raise RuntimeError('Got unexpected response data.') 470 471 472def _test_nearby_zone_ping(nearest_zone: tuple[str, float] | None) -> None: 473 """Try to ping nearest v2 zone.""" 474 if nearest_zone is None: 475 raise RuntimeError('No nearest zone.') 476 if nearest_zone[1] > 500: 477 raise RuntimeError('Ping too high.') 478 479 480class NetValTestingWindow(TestingWindow): 481 """Window to test network related settings.""" 482 483 def __init__(self, transition: str = 'in_right'): 484 entries = [ 485 {'name': 'bufferTime', 'label': 'Buffer Time', 'increment': 1.0}, 486 { 487 'name': 'delaySampling', 488 'label': 'Delay Sampling', 489 'increment': 1.0, 490 }, 491 { 492 'name': 'dynamicsSyncTime', 493 'label': 'Dynamics Sync Time', 494 'increment': 10, 495 }, 496 {'name': 'showNetInfo', 'label': 'Show Net Info', 'increment': 1}, 497 ] 498 super().__init__( 499 title=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 500 entries=entries, 501 transition=transition, 502 )
MAX_TEST_SECONDS =
120
class
NetTestingWindow(bauiv1._uitypes.MainWindow):
26class NetTestingWindow(bui.MainWindow): 27 """Window that runs a networking test suite to help diagnose issues.""" 28 29 def __init__( 30 self, 31 transition: str | None = 'in_right', 32 origin_widget: bui.Widget | None = None, 33 ): 34 uiscale = bui.app.ui_v1.uiscale 35 self._width = 820 36 self._height = 400 if uiscale is bui.UIScale.SMALL else 500 37 self._printed_lines: list[str] = [] 38 assert bui.app.classic is not None 39 super().__init__( 40 root_widget=bui.containerwidget( 41 size=(self._width, self._height), 42 scale=( 43 1.75 44 if uiscale is bui.UIScale.SMALL 45 else 1.2 if uiscale is bui.UIScale.MEDIUM else 0.8 46 ), 47 stack_offset=(0, -4 if uiscale is bui.UIScale.SMALL else 0.0), 48 toolbar_visibility=( 49 'menu_minimal' 50 if uiscale is bui.UIScale.SMALL 51 else 'menu_full' 52 ), 53 ), 54 transition=transition, 55 origin_widget=origin_widget, 56 ) 57 self._done_button: bui.Widget | None = bui.buttonwidget( 58 parent=self._root_widget, 59 position=(46, self._height - 77), 60 size=(120, 60), 61 scale=0.8, 62 autoselect=True, 63 label=bui.Lstr(resource='doneText'), 64 on_activate_call=self.main_window_back, 65 ) 66 67 # Avoid squads button on small mode. 68 xinset = -50 if uiscale is bui.UIScale.SMALL else 0 69 70 self._copy_button = bui.buttonwidget( 71 parent=self._root_widget, 72 position=(self._width - 200 + xinset, self._height - 77), 73 size=(100, 60), 74 scale=0.8, 75 autoselect=True, 76 label=bui.Lstr(resource='copyText'), 77 on_activate_call=self._copy, 78 ) 79 80 self._settings_button = bui.buttonwidget( 81 parent=self._root_widget, 82 position=(self._width - 100 + xinset, self._height - 77), 83 size=(60, 60), 84 scale=0.8, 85 autoselect=True, 86 label=bui.Lstr(value='...'), 87 on_activate_call=self._show_val_testing, 88 ) 89 90 twidth = self._width - 540 91 bui.textwidget( 92 parent=self._root_widget, 93 position=(self._width * 0.5, self._height - 55), 94 size=(0, 0), 95 text=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 96 color=(0.8, 0.8, 0.8, 1.0), 97 h_align='center', 98 v_align='center', 99 maxwidth=twidth, 100 ) 101 102 self._scroll = bui.scrollwidget( 103 parent=self._root_widget, 104 position=(50, 50), 105 size=(self._width - 100, self._height - 140), 106 capture_arrows=True, 107 autoselect=True, 108 ) 109 self._rows = bui.columnwidget(parent=self._scroll) 110 111 if uiscale is bui.UIScale.SMALL: 112 bui.containerwidget( 113 edit=self._root_widget, on_cancel_call=self.main_window_back 114 ) 115 self._done_button.delete() 116 self._done_button = None 117 else: 118 bui.containerwidget( 119 edit=self._root_widget, cancel_button=self._done_button 120 ) 121 122 # Now kick off the tests. 123 # Pass a weak-ref to this window so we don't keep it alive 124 # if we back out before it completes. Also set is as daemon 125 # so it doesn't keep the app running if the user is trying to quit. 126 Thread( 127 daemon=True, 128 target=bui.Call(_run_diagnostics, weakref.ref(self)), 129 ).start() 130 131 @override 132 def get_main_window_state(self) -> bui.MainWindowState: 133 # Support recreating our window for back/refresh purposes. 134 cls = type(self) 135 return bui.BasicMainWindowState( 136 create_call=lambda transition, origin_widget: cls( 137 transition=transition, origin_widget=origin_widget 138 ) 139 ) 140 141 def print(self, text: str, color: tuple[float, float, float]) -> None: 142 """Print text to our console thingie.""" 143 for line in text.splitlines(): 144 txt = bui.textwidget( 145 parent=self._rows, 146 color=color, 147 text=line, 148 scale=0.75, 149 flatness=1.0, 150 shadow=0.0, 151 size=(0, 20), 152 ) 153 bui.containerwidget(edit=self._rows, visible_child=txt) 154 self._printed_lines.append(line) 155 156 def _copy(self) -> None: 157 if not bui.clipboard_is_supported(): 158 bui.screenmessage( 159 'Clipboard not supported on this platform.', color=(1, 0, 0) 160 ) 161 return 162 bui.clipboard_set_text('\n'.join(self._printed_lines)) 163 bui.screenmessage(f'{len(self._printed_lines)} lines copied.') 164 165 def _show_val_testing(self) -> None: 166 assert bui.app.classic is not None 167 168 # no-op if our underlying widget is dead or on its way out. 169 if not self._root_widget or self._root_widget.transitioning_out: 170 return 171 172 bui.app.ui_v1.set_main_window(NetValTestingWindow(), from_window=self) 173 bui.containerwidget(edit=self._root_widget, transition='out_left') 174 175 # def _done(self) -> None: 176 # # pylint: disable=cyclic-import 177 # from bauiv1lib.settings.advanced import AdvancedSettingsWindow 178 179 # # no-op if our underlying widget is dead or on its way out. 180 # if not self._root_widget or self._root_widget.transitioning_out: 181 # return 182 183 # assert bui.app.classic is not None 184 # bui.app.ui_v1.set_main_window( 185 # AdvancedSettingsWindow(transition='in_left'), 186 # from_window=self, 187 # is_back=True, 188 # ) 189 # bui.containerwidget(edit=self._root_widget, transition='out_right')
Window that runs a networking test suite to help diagnose issues.
NetTestingWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
29 def __init__( 30 self, 31 transition: str | None = 'in_right', 32 origin_widget: bui.Widget | None = None, 33 ): 34 uiscale = bui.app.ui_v1.uiscale 35 self._width = 820 36 self._height = 400 if uiscale is bui.UIScale.SMALL else 500 37 self._printed_lines: list[str] = [] 38 assert bui.app.classic is not None 39 super().__init__( 40 root_widget=bui.containerwidget( 41 size=(self._width, self._height), 42 scale=( 43 1.75 44 if uiscale is bui.UIScale.SMALL 45 else 1.2 if uiscale is bui.UIScale.MEDIUM else 0.8 46 ), 47 stack_offset=(0, -4 if uiscale is bui.UIScale.SMALL else 0.0), 48 toolbar_visibility=( 49 'menu_minimal' 50 if uiscale is bui.UIScale.SMALL 51 else 'menu_full' 52 ), 53 ), 54 transition=transition, 55 origin_widget=origin_widget, 56 ) 57 self._done_button: bui.Widget | None = bui.buttonwidget( 58 parent=self._root_widget, 59 position=(46, self._height - 77), 60 size=(120, 60), 61 scale=0.8, 62 autoselect=True, 63 label=bui.Lstr(resource='doneText'), 64 on_activate_call=self.main_window_back, 65 ) 66 67 # Avoid squads button on small mode. 68 xinset = -50 if uiscale is bui.UIScale.SMALL else 0 69 70 self._copy_button = bui.buttonwidget( 71 parent=self._root_widget, 72 position=(self._width - 200 + xinset, self._height - 77), 73 size=(100, 60), 74 scale=0.8, 75 autoselect=True, 76 label=bui.Lstr(resource='copyText'), 77 on_activate_call=self._copy, 78 ) 79 80 self._settings_button = bui.buttonwidget( 81 parent=self._root_widget, 82 position=(self._width - 100 + xinset, self._height - 77), 83 size=(60, 60), 84 scale=0.8, 85 autoselect=True, 86 label=bui.Lstr(value='...'), 87 on_activate_call=self._show_val_testing, 88 ) 89 90 twidth = self._width - 540 91 bui.textwidget( 92 parent=self._root_widget, 93 position=(self._width * 0.5, self._height - 55), 94 size=(0, 0), 95 text=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 96 color=(0.8, 0.8, 0.8, 1.0), 97 h_align='center', 98 v_align='center', 99 maxwidth=twidth, 100 ) 101 102 self._scroll = bui.scrollwidget( 103 parent=self._root_widget, 104 position=(50, 50), 105 size=(self._width - 100, self._height - 140), 106 capture_arrows=True, 107 autoselect=True, 108 ) 109 self._rows = bui.columnwidget(parent=self._scroll) 110 111 if uiscale is bui.UIScale.SMALL: 112 bui.containerwidget( 113 edit=self._root_widget, on_cancel_call=self.main_window_back 114 ) 115 self._done_button.delete() 116 self._done_button = None 117 else: 118 bui.containerwidget( 119 edit=self._root_widget, cancel_button=self._done_button 120 ) 121 122 # Now kick off the tests. 123 # Pass a weak-ref to this window so we don't keep it alive 124 # if we back out before it completes. Also set is as daemon 125 # so it doesn't keep the app running if the user is trying to quit. 126 Thread( 127 daemon=True, 128 target=bui.Call(_run_diagnostics, weakref.ref(self)), 129 ).start()
Create a MainWindow given a root widget and transition info.
Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.
131 @override 132 def get_main_window_state(self) -> bui.MainWindowState: 133 # Support recreating our window for back/refresh purposes. 134 cls = type(self) 135 return bui.BasicMainWindowState( 136 create_call=lambda transition, origin_widget: cls( 137 transition=transition, origin_widget=origin_widget 138 ) 139 )
Return a WindowState to recreate this window, if supported.
def
print(self, text: str, color: tuple[float, float, float]) -> None:
141 def print(self, text: str, color: tuple[float, float, float]) -> None: 142 """Print text to our console thingie.""" 143 for line in text.splitlines(): 144 txt = bui.textwidget( 145 parent=self._rows, 146 color=color, 147 text=line, 148 scale=0.75, 149 flatness=1.0, 150 shadow=0.0, 151 size=(0, 20), 152 ) 153 bui.containerwidget(edit=self._rows, visible_child=txt) 154 self._printed_lines.append(line)
Print text to our console thingie.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_close
- can_change_main_window
- main_window_back
- main_window_replace
- on_main_window_close
- bauiv1._uitypes.Window
- get_root_widget
481class NetValTestingWindow(TestingWindow): 482 """Window to test network related settings.""" 483 484 def __init__(self, transition: str = 'in_right'): 485 entries = [ 486 {'name': 'bufferTime', 'label': 'Buffer Time', 'increment': 1.0}, 487 { 488 'name': 'delaySampling', 489 'label': 'Delay Sampling', 490 'increment': 1.0, 491 }, 492 { 493 'name': 'dynamicsSyncTime', 494 'label': 'Dynamics Sync Time', 495 'increment': 10, 496 }, 497 {'name': 'showNetInfo', 'label': 'Show Net Info', 'increment': 1}, 498 ] 499 super().__init__( 500 title=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 501 entries=entries, 502 transition=transition, 503 )
Window to test network related settings.
NetValTestingWindow(transition: str = 'in_right')
484 def __init__(self, transition: str = 'in_right'): 485 entries = [ 486 {'name': 'bufferTime', 'label': 'Buffer Time', 'increment': 1.0}, 487 { 488 'name': 'delaySampling', 489 'label': 'Delay Sampling', 490 'increment': 1.0, 491 }, 492 { 493 'name': 'dynamicsSyncTime', 494 'label': 'Dynamics Sync Time', 495 'increment': 10, 496 }, 497 {'name': 'showNetInfo', 'label': 'Show Net Info', 'increment': 1}, 498 ] 499 super().__init__( 500 title=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'), 501 entries=entries, 502 transition=transition, 503 )
Create a MainWindow given a root widget and transition info.
Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_close
- can_change_main_window
- main_window_back
- main_window_replace
- on_main_window_close
- get_main_window_state
- bauiv1._uitypes.Window
- get_root_widget