bauiv1lib.profile.upgrade

UI for player profile upgrades.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""UI for player profile upgrades."""
  4
  5from __future__ import annotations
  6
  7import time
  8import weakref
  9from typing import TYPE_CHECKING
 10
 11import bauiv1 as bui
 12
 13if TYPE_CHECKING:
 14    from typing import Any
 15
 16    from bauiv1lib.profile.edit import EditProfileWindow
 17
 18
 19class ProfileUpgradeWindow(bui.Window):
 20    """Window for player profile upgrades to global."""
 21
 22    def __init__(
 23        self,
 24        edit_profile_window: EditProfileWindow,
 25        transition: str = 'in_right',
 26    ):
 27        if bui.app.classic is None:
 28            raise RuntimeError('This requires classic.')
 29
 30        plus = bui.app.plus
 31        assert plus is not None
 32
 33        self._r = 'editProfileWindow'
 34
 35        uiscale = bui.app.ui_v1.uiscale
 36        self._width = 750 if uiscale is bui.UIScale.SMALL else 680
 37        self._height = 350
 38        assert bui.app.classic is not None
 39        self._base_scale = (
 40            1.9
 41            if uiscale is bui.UIScale.SMALL
 42            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.2
 43        )
 44        self._upgrade_start_time: float | None = None
 45        self._name = edit_profile_window.getname()
 46        self._edit_profile_window = weakref.ref(edit_profile_window)
 47
 48        top_extra = 15 if uiscale is bui.UIScale.SMALL else 15
 49        super().__init__(
 50            root_widget=bui.containerwidget(
 51                size=(self._width, self._height + top_extra),
 52                toolbar_visibility='menu_store_no_back',
 53                transition=transition,
 54                scale=self._base_scale,
 55                stack_offset=(
 56                    (0, -30) if uiscale is bui.UIScale.SMALL else (0, 0)
 57                ),
 58            )
 59        )
 60        cancel_button = bui.buttonwidget(
 61            parent=self._root_widget,
 62            position=(52, 60),
 63            size=(155, 60),
 64            scale=0.8,
 65            autoselect=True,
 66            label=bui.Lstr(resource='cancelText'),
 67            on_activate_call=self._cancel,
 68        )
 69        self._upgrade_button = bui.buttonwidget(
 70            parent=self._root_widget,
 71            position=(self._width - 190, 60),
 72            size=(155, 60),
 73            scale=0.8,
 74            autoselect=True,
 75            label=bui.Lstr(resource='upgradeText'),
 76            on_activate_call=self._on_upgrade_press,
 77        )
 78        bui.containerwidget(
 79            edit=self._root_widget,
 80            cancel_button=cancel_button,
 81            start_button=self._upgrade_button,
 82            selected_child=self._upgrade_button,
 83        )
 84
 85        assert bui.app.classic is not None
 86        bui.textwidget(
 87            parent=self._root_widget,
 88            position=(self._width * 0.5, self._height - 38),
 89            size=(0, 0),
 90            text=bui.Lstr(resource=f'{self._r}.upgradeToGlobalProfileText'),
 91            color=bui.app.ui_v1.title_color,
 92            maxwidth=self._width * 0.45,
 93            scale=1.0,
 94            h_align='center',
 95            v_align='center',
 96        )
 97
 98        assert bui.app.classic is not None
 99        bui.textwidget(
100            parent=self._root_widget,
101            position=(self._width * 0.5, self._height - 100),
102            size=(0, 0),
103            text=bui.Lstr(resource=f'{self._r}.upgradeProfileInfoText'),
104            color=bui.app.ui_v1.infotextcolor,
105            maxwidth=self._width * 0.8,
106            scale=0.7,
107            h_align='center',
108            v_align='center',
109        )
110
111        self._status_text = bui.textwidget(
112            parent=self._root_widget,
113            position=(self._width * 0.5, self._height - 160),
114            size=(0, 0),
115            text=bui.Lstr(
116                resource=f'{self._r}.checkingAvailabilityText',
117                subs=[('${NAME}', self._name)],
118            ),
119            color=(0.8, 0.4, 0.0),
120            maxwidth=self._width * 0.8,
121            scale=0.65,
122            h_align='center',
123            v_align='center',
124        )
125
126        self._price_text = bui.textwidget(
127            parent=self._root_widget,
128            position=(self._width * 0.5, self._height - 230),
129            size=(0, 0),
130            text='',
131            color=(0.2, 1, 0.2),
132            maxwidth=self._width * 0.8,
133            scale=1.5,
134            h_align='center',
135            v_align='center',
136        )
137
138        self._tickets_text: bui.Widget | None
139        # if not bui.app.ui_v1.use_toolbars:
140        #     self._tickets_text = bui.textwidget(
141        #         parent=self._root_widget,
142        #         position=(self._width * 0.9 - 5, self._height - 30),
143        #         size=(0, 0),
144        #         text=bui.charstr(bui.SpecialChar.TICKET) + '123',
145        #         color=(0.2, 1, 0.2),
146        #         maxwidth=100,
147        #         scale=0.5,
148        #         h_align='right',
149        #         v_align='center',
150        #     )
151        # else:
152        self._tickets_text = None
153
154        bui.app.classic.master_server_v1_get(
155            'bsGlobalProfileCheck',
156            {'name': self._name, 'b': bui.app.env.engine_build_number},
157            callback=bui.WeakCall(self._profile_check_result),
158        )
159        self._cost = plus.get_v1_account_misc_read_val(
160            'price.global_profile', 500
161        )
162        self._status: str | None = 'waiting'
163        self._update_timer = bui.AppTimer(
164            1.0, bui.WeakCall(self._update), repeat=True
165        )
166        self._update()
167
168    def _profile_check_result(self, result: dict[str, Any] | None) -> None:
169        if result is None:
170            bui.textwidget(
171                edit=self._status_text,
172                text=bui.Lstr(resource='internal.unavailableNoConnectionText'),
173                color=(1, 0, 0),
174            )
175            self._status = 'error'
176            bui.buttonwidget(
177                edit=self._upgrade_button,
178                color=(0.4, 0.4, 0.4),
179                textcolor=(0.5, 0.5, 0.5),
180            )
181        else:
182            if result['available']:
183                bui.textwidget(
184                    edit=self._status_text,
185                    text=bui.Lstr(
186                        resource=f'{self._r}.availableText',
187                        subs=[('${NAME}', self._name)],
188                    ),
189                    color=(0, 1, 0),
190                )
191                bui.textwidget(
192                    edit=self._price_text,
193                    text=bui.charstr(bui.SpecialChar.TICKET) + str(self._cost),
194                )
195                self._status = None
196            else:
197                bui.textwidget(
198                    edit=self._status_text,
199                    text=bui.Lstr(
200                        resource=f'{self._r}.unavailableText',
201                        subs=[('${NAME}', self._name)],
202                    ),
203                    color=(1, 0, 0),
204                )
205                self._status = 'unavailable'
206                bui.buttonwidget(
207                    edit=self._upgrade_button,
208                    color=(0.4, 0.4, 0.4),
209                    textcolor=(0.5, 0.5, 0.5),
210                )
211
212    def _on_upgrade_press(self) -> None:
213        # from bauiv1lib import gettickets
214
215        if self._status is None:
216            plus = bui.app.plus
217            assert plus is not None
218
219            # If it appears we don't have enough tickets, offer to buy more.
220            tickets = plus.get_v1_account_ticket_count()
221            if tickets < self._cost:
222                bui.getsound('error').play()
223                print('FIXME - show not-enough-tickets msg.')
224                # gettickets.show_get_tickets_prompt()
225                return
226            bui.screenmessage(
227                bui.Lstr(resource='purchasingText'), color=(0, 1, 0)
228            )
229            self._status = 'pre_upgrading'
230
231            # Now we tell the original editor to save the profile, add an
232            # upgrade transaction, and then sit and wait for everything to
233            # go through.
234            edit_profile_window = self._edit_profile_window()
235            if edit_profile_window is None:
236                print('profile upgrade: original edit window gone')
237                return
238            success = edit_profile_window.save(transition_out=False)
239            if not success:
240                print('profile upgrade: error occurred saving profile')
241                bui.screenmessage(
242                    bui.Lstr(resource='errorText'), color=(1, 0, 0)
243                )
244                bui.getsound('error').play()
245                return
246            plus.add_v1_account_transaction(
247                {'type': 'UPGRADE_PROFILE', 'name': self._name}
248            )
249            plus.run_v1_account_transactions()
250            self._status = 'upgrading'
251            self._upgrade_start_time = time.time()
252        else:
253            bui.getsound('error').play()
254
255    def _update(self) -> None:
256        plus = bui.app.plus
257        assert plus is not None
258
259        try:
260            t_str = str(plus.get_v1_account_ticket_count())
261        except Exception:
262            t_str = '?'
263        if self._tickets_text is not None:
264            bui.textwidget(
265                edit=self._tickets_text,
266                text=bui.Lstr(
267                    resource='getTicketsWindow.youHaveShortText',
268                    subs=[
269                        (
270                            '${COUNT}',
271                            bui.charstr(bui.SpecialChar.TICKET) + t_str,
272                        )
273                    ],
274                ),
275            )
276
277        # Once we've kicked off an upgrade attempt and all transactions go
278        # through, we're done.
279        if (
280            self._status == 'upgrading'
281            and not plus.have_outstanding_v1_account_transactions()
282        ):
283            self._status = 'exiting'
284            bui.containerwidget(edit=self._root_widget, transition='out_right')
285            edit_profile_window = self._edit_profile_window()
286            if edit_profile_window is None:
287                print(
288                    'profile upgrade transition out:'
289                    ' original edit window gone'
290                )
291                return
292            bui.getsound('gunCocking').play()
293            edit_profile_window.reload_window()
294
295    def _cancel(self) -> None:
296        # If we recently sent out an upgrade request, disallow canceling
297        # for a bit.
298        if (
299            self._upgrade_start_time is not None
300            and time.time() - self._upgrade_start_time < 10.0
301        ):
302            bui.getsound('error').play()
303            return
304        bui.containerwidget(edit=self._root_widget, transition='out_right')
class ProfileUpgradeWindow(bauiv1._uitypes.Window):
 20class ProfileUpgradeWindow(bui.Window):
 21    """Window for player profile upgrades to global."""
 22
 23    def __init__(
 24        self,
 25        edit_profile_window: EditProfileWindow,
 26        transition: str = 'in_right',
 27    ):
 28        if bui.app.classic is None:
 29            raise RuntimeError('This requires classic.')
 30
 31        plus = bui.app.plus
 32        assert plus is not None
 33
 34        self._r = 'editProfileWindow'
 35
 36        uiscale = bui.app.ui_v1.uiscale
 37        self._width = 750 if uiscale is bui.UIScale.SMALL else 680
 38        self._height = 350
 39        assert bui.app.classic is not None
 40        self._base_scale = (
 41            1.9
 42            if uiscale is bui.UIScale.SMALL
 43            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.2
 44        )
 45        self._upgrade_start_time: float | None = None
 46        self._name = edit_profile_window.getname()
 47        self._edit_profile_window = weakref.ref(edit_profile_window)
 48
 49        top_extra = 15 if uiscale is bui.UIScale.SMALL else 15
 50        super().__init__(
 51            root_widget=bui.containerwidget(
 52                size=(self._width, self._height + top_extra),
 53                toolbar_visibility='menu_store_no_back',
 54                transition=transition,
 55                scale=self._base_scale,
 56                stack_offset=(
 57                    (0, -30) if uiscale is bui.UIScale.SMALL else (0, 0)
 58                ),
 59            )
 60        )
 61        cancel_button = bui.buttonwidget(
 62            parent=self._root_widget,
 63            position=(52, 60),
 64            size=(155, 60),
 65            scale=0.8,
 66            autoselect=True,
 67            label=bui.Lstr(resource='cancelText'),
 68            on_activate_call=self._cancel,
 69        )
 70        self._upgrade_button = bui.buttonwidget(
 71            parent=self._root_widget,
 72            position=(self._width - 190, 60),
 73            size=(155, 60),
 74            scale=0.8,
 75            autoselect=True,
 76            label=bui.Lstr(resource='upgradeText'),
 77            on_activate_call=self._on_upgrade_press,
 78        )
 79        bui.containerwidget(
 80            edit=self._root_widget,
 81            cancel_button=cancel_button,
 82            start_button=self._upgrade_button,
 83            selected_child=self._upgrade_button,
 84        )
 85
 86        assert bui.app.classic is not None
 87        bui.textwidget(
 88            parent=self._root_widget,
 89            position=(self._width * 0.5, self._height - 38),
 90            size=(0, 0),
 91            text=bui.Lstr(resource=f'{self._r}.upgradeToGlobalProfileText'),
 92            color=bui.app.ui_v1.title_color,
 93            maxwidth=self._width * 0.45,
 94            scale=1.0,
 95            h_align='center',
 96            v_align='center',
 97        )
 98
 99        assert bui.app.classic is not None
100        bui.textwidget(
101            parent=self._root_widget,
102            position=(self._width * 0.5, self._height - 100),
103            size=(0, 0),
104            text=bui.Lstr(resource=f'{self._r}.upgradeProfileInfoText'),
105            color=bui.app.ui_v1.infotextcolor,
106            maxwidth=self._width * 0.8,
107            scale=0.7,
108            h_align='center',
109            v_align='center',
110        )
111
112        self._status_text = bui.textwidget(
113            parent=self._root_widget,
114            position=(self._width * 0.5, self._height - 160),
115            size=(0, 0),
116            text=bui.Lstr(
117                resource=f'{self._r}.checkingAvailabilityText',
118                subs=[('${NAME}', self._name)],
119            ),
120            color=(0.8, 0.4, 0.0),
121            maxwidth=self._width * 0.8,
122            scale=0.65,
123            h_align='center',
124            v_align='center',
125        )
126
127        self._price_text = bui.textwidget(
128            parent=self._root_widget,
129            position=(self._width * 0.5, self._height - 230),
130            size=(0, 0),
131            text='',
132            color=(0.2, 1, 0.2),
133            maxwidth=self._width * 0.8,
134            scale=1.5,
135            h_align='center',
136            v_align='center',
137        )
138
139        self._tickets_text: bui.Widget | None
140        # if not bui.app.ui_v1.use_toolbars:
141        #     self._tickets_text = bui.textwidget(
142        #         parent=self._root_widget,
143        #         position=(self._width * 0.9 - 5, self._height - 30),
144        #         size=(0, 0),
145        #         text=bui.charstr(bui.SpecialChar.TICKET) + '123',
146        #         color=(0.2, 1, 0.2),
147        #         maxwidth=100,
148        #         scale=0.5,
149        #         h_align='right',
150        #         v_align='center',
151        #     )
152        # else:
153        self._tickets_text = None
154
155        bui.app.classic.master_server_v1_get(
156            'bsGlobalProfileCheck',
157            {'name': self._name, 'b': bui.app.env.engine_build_number},
158            callback=bui.WeakCall(self._profile_check_result),
159        )
160        self._cost = plus.get_v1_account_misc_read_val(
161            'price.global_profile', 500
162        )
163        self._status: str | None = 'waiting'
164        self._update_timer = bui.AppTimer(
165            1.0, bui.WeakCall(self._update), repeat=True
166        )
167        self._update()
168
169    def _profile_check_result(self, result: dict[str, Any] | None) -> None:
170        if result is None:
171            bui.textwidget(
172                edit=self._status_text,
173                text=bui.Lstr(resource='internal.unavailableNoConnectionText'),
174                color=(1, 0, 0),
175            )
176            self._status = 'error'
177            bui.buttonwidget(
178                edit=self._upgrade_button,
179                color=(0.4, 0.4, 0.4),
180                textcolor=(0.5, 0.5, 0.5),
181            )
182        else:
183            if result['available']:
184                bui.textwidget(
185                    edit=self._status_text,
186                    text=bui.Lstr(
187                        resource=f'{self._r}.availableText',
188                        subs=[('${NAME}', self._name)],
189                    ),
190                    color=(0, 1, 0),
191                )
192                bui.textwidget(
193                    edit=self._price_text,
194                    text=bui.charstr(bui.SpecialChar.TICKET) + str(self._cost),
195                )
196                self._status = None
197            else:
198                bui.textwidget(
199                    edit=self._status_text,
200                    text=bui.Lstr(
201                        resource=f'{self._r}.unavailableText',
202                        subs=[('${NAME}', self._name)],
203                    ),
204                    color=(1, 0, 0),
205                )
206                self._status = 'unavailable'
207                bui.buttonwidget(
208                    edit=self._upgrade_button,
209                    color=(0.4, 0.4, 0.4),
210                    textcolor=(0.5, 0.5, 0.5),
211                )
212
213    def _on_upgrade_press(self) -> None:
214        # from bauiv1lib import gettickets
215
216        if self._status is None:
217            plus = bui.app.plus
218            assert plus is not None
219
220            # If it appears we don't have enough tickets, offer to buy more.
221            tickets = plus.get_v1_account_ticket_count()
222            if tickets < self._cost:
223                bui.getsound('error').play()
224                print('FIXME - show not-enough-tickets msg.')
225                # gettickets.show_get_tickets_prompt()
226                return
227            bui.screenmessage(
228                bui.Lstr(resource='purchasingText'), color=(0, 1, 0)
229            )
230            self._status = 'pre_upgrading'
231
232            # Now we tell the original editor to save the profile, add an
233            # upgrade transaction, and then sit and wait for everything to
234            # go through.
235            edit_profile_window = self._edit_profile_window()
236            if edit_profile_window is None:
237                print('profile upgrade: original edit window gone')
238                return
239            success = edit_profile_window.save(transition_out=False)
240            if not success:
241                print('profile upgrade: error occurred saving profile')
242                bui.screenmessage(
243                    bui.Lstr(resource='errorText'), color=(1, 0, 0)
244                )
245                bui.getsound('error').play()
246                return
247            plus.add_v1_account_transaction(
248                {'type': 'UPGRADE_PROFILE', 'name': self._name}
249            )
250            plus.run_v1_account_transactions()
251            self._status = 'upgrading'
252            self._upgrade_start_time = time.time()
253        else:
254            bui.getsound('error').play()
255
256    def _update(self) -> None:
257        plus = bui.app.plus
258        assert plus is not None
259
260        try:
261            t_str = str(plus.get_v1_account_ticket_count())
262        except Exception:
263            t_str = '?'
264        if self._tickets_text is not None:
265            bui.textwidget(
266                edit=self._tickets_text,
267                text=bui.Lstr(
268                    resource='getTicketsWindow.youHaveShortText',
269                    subs=[
270                        (
271                            '${COUNT}',
272                            bui.charstr(bui.SpecialChar.TICKET) + t_str,
273                        )
274                    ],
275                ),
276            )
277
278        # Once we've kicked off an upgrade attempt and all transactions go
279        # through, we're done.
280        if (
281            self._status == 'upgrading'
282            and not plus.have_outstanding_v1_account_transactions()
283        ):
284            self._status = 'exiting'
285            bui.containerwidget(edit=self._root_widget, transition='out_right')
286            edit_profile_window = self._edit_profile_window()
287            if edit_profile_window is None:
288                print(
289                    'profile upgrade transition out:'
290                    ' original edit window gone'
291                )
292                return
293            bui.getsound('gunCocking').play()
294            edit_profile_window.reload_window()
295
296    def _cancel(self) -> None:
297        # If we recently sent out an upgrade request, disallow canceling
298        # for a bit.
299        if (
300            self._upgrade_start_time is not None
301            and time.time() - self._upgrade_start_time < 10.0
302        ):
303            bui.getsound('error').play()
304            return
305        bui.containerwidget(edit=self._root_widget, transition='out_right')

Window for player profile upgrades to global.

ProfileUpgradeWindow( edit_profile_window: bauiv1lib.profile.edit.EditProfileWindow, transition: str = 'in_right')
 23    def __init__(
 24        self,
 25        edit_profile_window: EditProfileWindow,
 26        transition: str = 'in_right',
 27    ):
 28        if bui.app.classic is None:
 29            raise RuntimeError('This requires classic.')
 30
 31        plus = bui.app.plus
 32        assert plus is not None
 33
 34        self._r = 'editProfileWindow'
 35
 36        uiscale = bui.app.ui_v1.uiscale
 37        self._width = 750 if uiscale is bui.UIScale.SMALL else 680
 38        self._height = 350
 39        assert bui.app.classic is not None
 40        self._base_scale = (
 41            1.9
 42            if uiscale is bui.UIScale.SMALL
 43            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.2
 44        )
 45        self._upgrade_start_time: float | None = None
 46        self._name = edit_profile_window.getname()
 47        self._edit_profile_window = weakref.ref(edit_profile_window)
 48
 49        top_extra = 15 if uiscale is bui.UIScale.SMALL else 15
 50        super().__init__(
 51            root_widget=bui.containerwidget(
 52                size=(self._width, self._height + top_extra),
 53                toolbar_visibility='menu_store_no_back',
 54                transition=transition,
 55                scale=self._base_scale,
 56                stack_offset=(
 57                    (0, -30) if uiscale is bui.UIScale.SMALL else (0, 0)
 58                ),
 59            )
 60        )
 61        cancel_button = bui.buttonwidget(
 62            parent=self._root_widget,
 63            position=(52, 60),
 64            size=(155, 60),
 65            scale=0.8,
 66            autoselect=True,
 67            label=bui.Lstr(resource='cancelText'),
 68            on_activate_call=self._cancel,
 69        )
 70        self._upgrade_button = bui.buttonwidget(
 71            parent=self._root_widget,
 72            position=(self._width - 190, 60),
 73            size=(155, 60),
 74            scale=0.8,
 75            autoselect=True,
 76            label=bui.Lstr(resource='upgradeText'),
 77            on_activate_call=self._on_upgrade_press,
 78        )
 79        bui.containerwidget(
 80            edit=self._root_widget,
 81            cancel_button=cancel_button,
 82            start_button=self._upgrade_button,
 83            selected_child=self._upgrade_button,
 84        )
 85
 86        assert bui.app.classic is not None
 87        bui.textwidget(
 88            parent=self._root_widget,
 89            position=(self._width * 0.5, self._height - 38),
 90            size=(0, 0),
 91            text=bui.Lstr(resource=f'{self._r}.upgradeToGlobalProfileText'),
 92            color=bui.app.ui_v1.title_color,
 93            maxwidth=self._width * 0.45,
 94            scale=1.0,
 95            h_align='center',
 96            v_align='center',
 97        )
 98
 99        assert bui.app.classic is not None
100        bui.textwidget(
101            parent=self._root_widget,
102            position=(self._width * 0.5, self._height - 100),
103            size=(0, 0),
104            text=bui.Lstr(resource=f'{self._r}.upgradeProfileInfoText'),
105            color=bui.app.ui_v1.infotextcolor,
106            maxwidth=self._width * 0.8,
107            scale=0.7,
108            h_align='center',
109            v_align='center',
110        )
111
112        self._status_text = bui.textwidget(
113            parent=self._root_widget,
114            position=(self._width * 0.5, self._height - 160),
115            size=(0, 0),
116            text=bui.Lstr(
117                resource=f'{self._r}.checkingAvailabilityText',
118                subs=[('${NAME}', self._name)],
119            ),
120            color=(0.8, 0.4, 0.0),
121            maxwidth=self._width * 0.8,
122            scale=0.65,
123            h_align='center',
124            v_align='center',
125        )
126
127        self._price_text = bui.textwidget(
128            parent=self._root_widget,
129            position=(self._width * 0.5, self._height - 230),
130            size=(0, 0),
131            text='',
132            color=(0.2, 1, 0.2),
133            maxwidth=self._width * 0.8,
134            scale=1.5,
135            h_align='center',
136            v_align='center',
137        )
138
139        self._tickets_text: bui.Widget | None
140        # if not bui.app.ui_v1.use_toolbars:
141        #     self._tickets_text = bui.textwidget(
142        #         parent=self._root_widget,
143        #         position=(self._width * 0.9 - 5, self._height - 30),
144        #         size=(0, 0),
145        #         text=bui.charstr(bui.SpecialChar.TICKET) + '123',
146        #         color=(0.2, 1, 0.2),
147        #         maxwidth=100,
148        #         scale=0.5,
149        #         h_align='right',
150        #         v_align='center',
151        #     )
152        # else:
153        self._tickets_text = None
154
155        bui.app.classic.master_server_v1_get(
156            'bsGlobalProfileCheck',
157            {'name': self._name, 'b': bui.app.env.engine_build_number},
158            callback=bui.WeakCall(self._profile_check_result),
159        )
160        self._cost = plus.get_v1_account_misc_read_val(
161            'price.global_profile', 500
162        )
163        self._status: str | None = 'waiting'
164        self._update_timer = bui.AppTimer(
165            1.0, bui.WeakCall(self._update), repeat=True
166        )
167        self._update()
Inherited Members
bauiv1._uitypes.Window
get_root_widget