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

Window that runs a networking test suite to help diagnose issues.

NetTestingWindow(transition: str = 'in_right')
 29    def __init__(self, transition: str = 'in_right'):
 30        self._width = 820
 31        self._height = 500
 32        self._printed_lines: list[str] = []
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        super().__init__(
 36            root_widget=bui.containerwidget(
 37                size=(self._width, self._height),
 38                scale=(
 39                    1.56
 40                    if uiscale is bui.UIScale.SMALL
 41                    else 1.2 if uiscale is bui.UIScale.MEDIUM else 0.8
 42                ),
 43                stack_offset=(0.0, -7 if uiscale is bui.UIScale.SMALL else 0.0),
 44                transition=transition,
 45            )
 46        )
 47        self._done_button = bui.buttonwidget(
 48            parent=self._root_widget,
 49            position=(40, self._height - 77),
 50            size=(120, 60),
 51            scale=0.8,
 52            autoselect=True,
 53            label=bui.Lstr(resource='doneText'),
 54            on_activate_call=self._done,
 55        )
 56
 57        self._copy_button = bui.buttonwidget(
 58            parent=self._root_widget,
 59            position=(self._width - 200, self._height - 77),
 60            size=(100, 60),
 61            scale=0.8,
 62            autoselect=True,
 63            label=bui.Lstr(resource='copyText'),
 64            on_activate_call=self._copy,
 65        )
 66
 67        self._settings_button = bui.buttonwidget(
 68            parent=self._root_widget,
 69            position=(self._width - 100, self._height - 77),
 70            size=(60, 60),
 71            scale=0.8,
 72            autoselect=True,
 73            label=bui.Lstr(value='...'),
 74            on_activate_call=self._show_val_testing,
 75        )
 76
 77        twidth = self._width - 450
 78        bui.textwidget(
 79            parent=self._root_widget,
 80            position=(self._width * 0.5, self._height - 55),
 81            size=(0, 0),
 82            text=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'),
 83            color=(0.8, 0.8, 0.8, 1.0),
 84            h_align='center',
 85            v_align='center',
 86            maxwidth=twidth,
 87        )
 88
 89        self._scroll = bui.scrollwidget(
 90            parent=self._root_widget,
 91            position=(50, 50),
 92            size=(self._width - 100, self._height - 140),
 93            capture_arrows=True,
 94            autoselect=True,
 95        )
 96        self._rows = bui.columnwidget(parent=self._scroll)
 97
 98        bui.containerwidget(
 99            edit=self._root_widget, cancel_button=self._done_button
100        )
101
102        # Now kick off the tests.
103        # Pass a weak-ref to this window so we don't keep it alive
104        # if we back out before it completes. Also set is as daemon
105        # so it doesn't keep the app running if the user is trying to quit.
106        Thread(
107            daemon=True,
108            target=bui.Call(_run_diagnostics, weakref.ref(self)),
109        ).start()
def print(self, text: str, color: tuple[float, float, float]) -> None:
111    def print(self, text: str, color: tuple[float, float, float]) -> None:
112        """Print text to our console thingie."""
113        for line in text.splitlines():
114            txt = bui.textwidget(
115                parent=self._rows,
116                color=color,
117                text=line,
118                scale=0.75,
119                flatness=1.0,
120                shadow=0.0,
121                size=(0, 20),
122            )
123            bui.containerwidget(edit=self._rows, visible_child=txt)
124            self._printed_lines.append(line)

Print text to our console thingie.

Inherited Members
bauiv1._uitypes.Window
get_root_widget
class NetValTestingWindow(bauiv1lib.settings.testing.TestingWindow):
453class NetValTestingWindow(TestingWindow):
454    """Window to test network related settings."""
455
456    def __init__(self, transition: str = 'in_right'):
457        entries = [
458            {'name': 'bufferTime', 'label': 'Buffer Time', 'increment': 1.0},
459            {
460                'name': 'delaySampling',
461                'label': 'Delay Sampling',
462                'increment': 1.0,
463            },
464            {
465                'name': 'dynamicsSyncTime',
466                'label': 'Dynamics Sync Time',
467                'increment': 10,
468            },
469            {'name': 'showNetInfo', 'label': 'Show Net Info', 'increment': 1},
470        ]
471        super().__init__(
472            title=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'),
473            entries=entries,
474            transition=transition,
475            back_call=lambda: NetTestingWindow(transition='in_left'),
476        )

Window to test network related settings.

NetValTestingWindow(transition: str = 'in_right')
456    def __init__(self, transition: str = 'in_right'):
457        entries = [
458            {'name': 'bufferTime', 'label': 'Buffer Time', 'increment': 1.0},
459            {
460                'name': 'delaySampling',
461                'label': 'Delay Sampling',
462                'increment': 1.0,
463            },
464            {
465                'name': 'dynamicsSyncTime',
466                'label': 'Dynamics Sync Time',
467                'increment': 10,
468            },
469            {'name': 'showNetInfo', 'label': 'Show Net Info', 'increment': 1},
470        ]
471        super().__init__(
472            title=bui.Lstr(resource='settingsWindowAdvanced.netTestingText'),
473            entries=entries,
474            transition=transition,
475            back_call=lambda: NetTestingWindow(transition='in_left'),
476        )
Inherited Members
bauiv1._uitypes.Window
get_root_widget