add a way to wait for something with notification after 0.1s
use this for moving between screens, removing some crummy code from subquity/core.py
This commit is contained in:
parent
6c258d6da4
commit
a7bcc7faf0
|
@ -29,6 +29,10 @@ from subiquitycore.tuicontroller import (
|
|||
log = logging.getLogger("subiquity.controller")
|
||||
|
||||
|
||||
class Confirm(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SubiquityController(BaseController):
|
||||
|
||||
autoinstall_key = None
|
||||
|
|
|
@ -46,6 +46,7 @@ from subiquity.common.errorreport import (
|
|||
ErrorReporter,
|
||||
ErrorReportKind,
|
||||
)
|
||||
from subiquity.controller import Confirm
|
||||
from subiquity.journald import journald_listen
|
||||
from subiquity.keycodes import (
|
||||
DummyKeycodesFilter,
|
||||
|
@ -331,38 +332,6 @@ class Subiquity(TuiApplication):
|
|||
self.show_progress_handle.cancel()
|
||||
self.show_progress_handle = None
|
||||
|
||||
def next_screen(self):
|
||||
can_install = all(e.is_set() for e in self.base_model.install_events)
|
||||
if can_install and not self.install_confirmed:
|
||||
if self.interactive():
|
||||
log.debug("showing InstallConfirmation over %s", self.ui.body)
|
||||
from subiquity.ui.views.installprogress import (
|
||||
InstallConfirmation,
|
||||
)
|
||||
self._cancel_show_progress()
|
||||
self.add_global_overlay(
|
||||
InstallConfirmation(self.ui.body, self))
|
||||
else:
|
||||
yes = _('yes')
|
||||
no = _('no')
|
||||
answer = no
|
||||
if 'autoinstall' in self.kernel_cmdline:
|
||||
answer = yes
|
||||
else:
|
||||
print(_("Confirmation is required to continue."))
|
||||
print(_("Add 'autoinstall' to your kernel command line to"
|
||||
" avoid this"))
|
||||
print()
|
||||
prompt = "\n\n{} ({}|{})".format(
|
||||
_("Continue with autoinstall?"), yes, no)
|
||||
while answer != yes:
|
||||
print(prompt)
|
||||
answer = input()
|
||||
self.confirm_install()
|
||||
super().next_screen()
|
||||
else:
|
||||
super().next_screen()
|
||||
|
||||
def interactive(self):
|
||||
if not self.autoinstall_config:
|
||||
return True
|
||||
|
@ -386,42 +355,56 @@ class Subiquity(TuiApplication):
|
|||
break
|
||||
super().select_initial_screen(index)
|
||||
|
||||
async def select_screen(self, new):
|
||||
async def move_screen(self, increment, coro):
|
||||
try:
|
||||
await super().move_screen(increment, coro)
|
||||
except Confirm:
|
||||
if self.interactive():
|
||||
log.debug("showing InstallConfirmation over %s", self.ui.body)
|
||||
from subiquity.ui.views.installprogress import (
|
||||
InstallConfirmation,
|
||||
)
|
||||
self.add_global_overlay(
|
||||
InstallConfirmation(self.ui.body, self))
|
||||
else:
|
||||
yes = _('yes')
|
||||
no = _('no')
|
||||
answer = no
|
||||
if 'autoinstall' in self.kernel_cmdline:
|
||||
answer = yes
|
||||
else:
|
||||
print(_("Confirmation is required to continue."))
|
||||
print(_("Add 'autoinstall' to your kernel command line to"
|
||||
" avoid this"))
|
||||
print()
|
||||
prompt = "\n\n{} ({}|{})".format(
|
||||
_("Continue with autoinstall?"), yes, no)
|
||||
while answer != yes:
|
||||
print(prompt)
|
||||
answer = input()
|
||||
self.confirm_install()
|
||||
self.next_screen()
|
||||
|
||||
async def make_view_for_controller(self, new):
|
||||
can_install = all(e.is_set() for e in self.base_model.install_events)
|
||||
if can_install and not self.install_confirmed:
|
||||
if new.model_name:
|
||||
if not self.base_model.is_configured(new.model_name):
|
||||
raise Confirm
|
||||
if new.interactive():
|
||||
self._cancel_show_progress()
|
||||
if self.progress_showing:
|
||||
shown_for = self.aio_loop.time() - self.progress_shown_time
|
||||
remaining = 1.0 - shown_for
|
||||
if remaining > 0.0:
|
||||
self.aio_loop.call_later(
|
||||
remaining, self.select_screen, new)
|
||||
return
|
||||
self.progress_showing = False
|
||||
await super().select_screen(new)
|
||||
if new.answers:
|
||||
new.run_answers()
|
||||
elif self.autoinstall_config and not new.autoinstall_applied:
|
||||
if self.interactive() and self.show_progress_handle is None:
|
||||
self.ui.block_input = True
|
||||
self.show_progress_handle = self.aio_loop.call_later(
|
||||
0.1, self._show_progress)
|
||||
schedule_task(self._apply(new))
|
||||
self.aio_loop.call_soon(new.run_answers)
|
||||
return await super().make_view_for_controller(new)
|
||||
else:
|
||||
if self.autoinstall_config and not new.autoinstall_applied:
|
||||
await new.apply_autoinstall_config()
|
||||
new.autoinstall_applied = True
|
||||
new.configured()
|
||||
raise Skip
|
||||
|
||||
def _show_progress(self):
|
||||
self.ui.block_input = False
|
||||
self.progress_shown_time = self.aio_loop.time()
|
||||
self.progress_showing = True
|
||||
def show_progress(self):
|
||||
self.ui.set_body(self.controllers.InstallProgress.progress_view)
|
||||
|
||||
async def _apply(self, controller):
|
||||
await controller.apply_autoinstall_config()
|
||||
controller.autoinstall_applied = True
|
||||
controller.configured()
|
||||
self.next_screen()
|
||||
|
||||
def _network_change(self):
|
||||
self.signal.emit_signal('snapd-network-change')
|
||||
|
||||
|
|
|
@ -130,8 +130,21 @@ class SubiquityModel:
|
|||
}
|
||||
|
||||
def configured(self, model_name):
|
||||
log.debug("model %s is configured", model_name)
|
||||
self._events[model_name].set()
|
||||
if model_name in INSTALL_MODEL_NAMES:
|
||||
unconfigured = {
|
||||
mn for mn in INSTALL_MODEL_NAMES
|
||||
if not self.is_configured(mn)
|
||||
}
|
||||
elif model_name in POSTINSTALL_MODEL_NAMES:
|
||||
unconfigured = {
|
||||
mn for mn in POSTINSTALL_MODEL_NAMES
|
||||
if not self.is_configured(mn)
|
||||
}
|
||||
log.debug("model %s is configured, to go %s", model_name, unconfigured)
|
||||
|
||||
def is_configured(self, model_name):
|
||||
return self._events[model_name].is_set()
|
||||
|
||||
def get_target_groups(self):
|
||||
command = ['chroot', self.target, 'getent', 'group']
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
|
@ -49,6 +50,14 @@ def extend_dec_special_charmap():
|
|||
})
|
||||
|
||||
|
||||
# When waiting for something of unknown duration, block the UI for at
|
||||
# most this long before showing an indication of progress.
|
||||
MAX_BLOCK_TIME = 0.1
|
||||
# If an indication of progress is shown, show it for at least this
|
||||
# long to avoid excessive flicker in the UI.
|
||||
MIN_SHOW_PROGRESS_TIME = 1.0
|
||||
|
||||
|
||||
class TuiApplication(Application):
|
||||
|
||||
make_ui = SubiquityCoreUI
|
||||
|
@ -97,7 +106,7 @@ class TuiApplication(Application):
|
|||
before_hook()
|
||||
schedule_task(_run())
|
||||
|
||||
async def select_screen(self, new):
|
||||
async def make_view_for_controller(self, new):
|
||||
new.context.enter("starting UI")
|
||||
if self.opts.screens and new.name not in self.opts.screens:
|
||||
raise Skip
|
||||
|
@ -111,10 +120,46 @@ class TuiApplication(Application):
|
|||
new.context.exit("(skipped)")
|
||||
raise
|
||||
else:
|
||||
self.ui.set_body(view)
|
||||
self.cur_screen = new
|
||||
with open(self.state_path('last-screen'), 'w') as fp:
|
||||
fp.write(new.name)
|
||||
with open(self.state_path('last-screen'), 'w') as fp:
|
||||
fp.write(new.name)
|
||||
return view
|
||||
|
||||
async def _wait_with_indication(self, awaitable, show, hide=None):
|
||||
"""Wait for something but tell the user if it takes a while.
|
||||
|
||||
When waiting for something that can take an unknown length of
|
||||
time, we want to tell the user if it takes more than a moment
|
||||
(defined as MAX_BLOCK_TIME) but make sure that we display any
|
||||
indication for long enough that the UI is not flickering
|
||||
incomprehensively (MIN_SHOW_PROGRESS_TIME).
|
||||
"""
|
||||
min_show_task = None
|
||||
|
||||
def _show():
|
||||
self.ui.block_input = False
|
||||
nonlocal min_show_task
|
||||
min_show_task = self.aio_loop.create_task(
|
||||
asyncio.sleep(MIN_SHOW_PROGRESS_TIME))
|
||||
show()
|
||||
|
||||
self.ui.block_input = True
|
||||
show_handle = self.aio_loop.call_later(MAX_BLOCK_TIME, _show)
|
||||
try:
|
||||
result = await awaitable
|
||||
finally:
|
||||
if min_show_task:
|
||||
await min_show_task
|
||||
if hide is not None:
|
||||
hide()
|
||||
else:
|
||||
self.ui.block_input = False
|
||||
show_handle.cancel()
|
||||
|
||||
return result
|
||||
|
||||
def show_progress(self):
|
||||
raise NotImplementedError
|
||||
|
||||
async def _move_screen(self, increment, coro):
|
||||
if coro is not None:
|
||||
|
@ -129,24 +174,33 @@ class TuiApplication(Application):
|
|||
self.controllers.index += increment
|
||||
if self.controllers.index < 0:
|
||||
self.controllers.index = cur_index
|
||||
return
|
||||
return None
|
||||
if self.controllers.index >= len(self.controllers.instances):
|
||||
self.exit()
|
||||
return
|
||||
return None
|
||||
new = self.controllers.cur
|
||||
try:
|
||||
await self.select_screen(new)
|
||||
return await self.make_view_for_controller(new)
|
||||
except Skip:
|
||||
log.debug("skipping screen %s", new.name)
|
||||
continue
|
||||
except Exception:
|
||||
self.controllers.index = cur_index
|
||||
raise
|
||||
else:
|
||||
return
|
||||
|
||||
async def move_screen(self, increment, coro):
|
||||
view = await self._wait_with_indication(
|
||||
self._move_screen(increment, coro), self.show_progress)
|
||||
if view is not None:
|
||||
self.ui.set_body(view)
|
||||
|
||||
def next_screen(self, coro=None):
|
||||
self.aio_loop.create_task(self._move_screen(1, coro))
|
||||
self.aio_loop.create_task(self.move_screen(1, coro))
|
||||
|
||||
def prev_screen(self):
|
||||
self.aio_loop.create_task(self._move_screen(-1, None))
|
||||
self.aio_loop.create_task(self.move_screen(-1, None))
|
||||
|
||||
def select_initial_screen(self, controller_index):
|
||||
for controller in self.controllers.instances[:controller_index]:
|
||||
|
|
Loading…
Reference in New Issue