From edc65dbbfbccafefff011115998332a59fc746d6 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 31 Jul 2020 12:14:51 +1200 Subject: [PATCH] make the base controller have no ui, add subclass for one that has ui --- console_conf/controllers/chooser.py | 4 +- console_conf/controllers/identity.py | 4 +- console_conf/controllers/welcome.py | 4 +- subiquity/controller.py | 30 +++--- subiquity/controllers/cmdlist.py | 4 +- subiquity/controllers/debconf.py | 4 +- subiquity/controllers/filesystem.py | 4 +- subiquity/controllers/identity.py | 4 +- subiquity/controllers/installprogress.py | 4 +- subiquity/controllers/keyboard.py | 4 +- subiquity/controllers/mirror.py | 4 +- subiquity/controllers/network.py | 4 +- subiquity/controllers/package.py | 4 +- subiquity/controllers/proxy.py | 4 +- subiquity/controllers/reboot.py | 4 +- subiquity/controllers/refresh.py | 6 +- subiquity/controllers/reporting.py | 4 +- subiquity/controllers/snaplist.py | 6 +- subiquity/controllers/ssh.py | 4 +- subiquity/controllers/userdata.py | 4 +- subiquity/controllers/welcome.py | 4 +- subiquity/controllers/zdev.py | 4 +- subiquity/core.py | 2 +- subiquitycore/controller.py | 91 +---------------- subiquitycore/controllers/network.py | 4 +- subiquitycore/tui.py | 9 +- subiquitycore/tuicontroller.py | 120 +++++++++++++++++++++++ 27 files changed, 187 insertions(+), 157 deletions(-) create mode 100644 subiquitycore/tuicontroller.py diff --git a/console_conf/controllers/chooser.py b/console_conf/controllers/chooser.py index 8cb4e707..6b92175c 100644 --- a/console_conf/controllers/chooser.py +++ b/console_conf/controllers/chooser.py @@ -20,12 +20,12 @@ from console_conf.ui.views import ( ChooserConfirmView, ) -from subiquitycore.controller import BaseController +from subiquitycore.tuicontroller import TuiController log = logging.getLogger("console_conf.controllers.chooser") -class RecoveryChooserBaseController(BaseController): +class RecoveryChooserBaseController(TuiController): def __init__(self, app): super().__init__(app) diff --git a/console_conf/controllers/identity.py b/console_conf/controllers/identity.py index 53888168..cf1b9eb6 100644 --- a/console_conf/controllers/identity.py +++ b/console_conf/controllers/identity.py @@ -20,9 +20,9 @@ import pwd import shlex import sys -from subiquitycore.controller import BaseController from subiquitycore.ssh import host_key_info, get_ips_standalone from subiquitycore.snapd import SnapdConnection +from subiquitycore.tuicontroller import TuiController from subiquitycore.utils import disable_console_conf, run_command from console_conf.ui.views import IdentityView, LoginView @@ -141,7 +141,7 @@ def write_login_details_standalone(): return 0 -class IdentityController(BaseController): +class IdentityController(TuiController): def __init__(self, app): super().__init__(app) diff --git a/console_conf/controllers/welcome.py b/console_conf/controllers/welcome.py index 8144e2fc..03654e14 100644 --- a/console_conf/controllers/welcome.py +++ b/console_conf/controllers/welcome.py @@ -15,10 +15,10 @@ from console_conf.ui.views import WelcomeView, ChooserWelcomeView -from subiquitycore.controller import BaseController +from subiquitycore.tuicontroller import TuiController -class WelcomeController(BaseController): +class WelcomeController(TuiController): welcome_view = WelcomeView diff --git a/subiquity/controller.py b/subiquity/controller.py index 5fb71490..16e9c363 100644 --- a/subiquity/controller.py +++ b/subiquity/controller.py @@ -20,8 +20,10 @@ import jsonschema from subiquitycore.context import with_context from subiquitycore.controller import ( BaseController, + ) +from subiquitycore.tuicontroller import ( RepeatedController, - Skip, + TuiController, ) log = logging.getLogger("subiquity.controller") @@ -39,6 +41,9 @@ class SubiquityController(BaseController): self.context.set('controller', self) self.setup_autoinstall() + def interactive(self): + return False + def setup_autoinstall(self): if self.app.autoinstall_config: with self.context.child("load_autoinstall_data"): @@ -68,15 +73,6 @@ class SubiquityController(BaseController): """ pass - def interactive(self): - if not self.app.autoinstall_config: - return True - i_sections = self.app.autoinstall_config.get( - 'interactive-sections', []) - if '*' in i_sections or self.autoinstall_key in i_sections: - return True - return False - def configured(self): """Let the world know that this controller's model is now configured. """ @@ -90,15 +86,15 @@ class SubiquityController(BaseController): return {} -class NoUIController(SubiquityController): - - def start_ui(self): - raise Skip - - def cancel(self): - pass +class SubiquityTuiController(SubiquityController, TuiController): def interactive(self): + if not self.app.autoinstall_config: + return True + i_sections = self.app.autoinstall_config.get( + 'interactive-sections', []) + if '*' in i_sections or self.autoinstall_key in i_sections: + return True return False diff --git a/subiquity/controllers/cmdlist.py b/subiquity/controllers/cmdlist.py index d00eb940..d73d72dc 100644 --- a/subiquity/controllers/cmdlist.py +++ b/subiquity/controllers/cmdlist.py @@ -18,10 +18,10 @@ import os from subiquitycore.context import with_context from subiquitycore.utils import arun_command -from subiquity.controller import NoUIController +from subiquity.controller import SubiquityController -class CmdListController(NoUIController): +class CmdListController(SubiquityController): autoinstall_default = [] autoinstall_schema = { diff --git a/subiquity/controllers/debconf.py b/subiquity/controllers/debconf.py index 04c2540f..d529295d 100644 --- a/subiquity/controllers/debconf.py +++ b/subiquity/controllers/debconf.py @@ -13,10 +13,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from subiquity.controller import NoUIController +from subiquity.controller import SubiquityController -class DebconfController(NoUIController): +class DebconfController(SubiquityController): model_name = "debconf_selections" autoinstall_key = "debconf-selections" diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index c781b60f..d22f934f 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -34,7 +34,7 @@ from subiquitycore.utils import ( from subiquity.common.errorreport import ErrorReportKind -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.models.filesystem import ( align_up, Bootloader, @@ -61,7 +61,7 @@ PREP_GRUB_SIZE_BYTES = 8 * 1024 * 1024 # 8MiB UEFI_GRUB_SIZE_BYTES = 512 * 1024 * 1024 # 512MiB EFI partition -class FilesystemController(SubiquityController): +class FilesystemController(SubiquityTuiController): autoinstall_key = "storage" autoinstall_schema = {'type': 'object'} # ... diff --git a/subiquity/controllers/identity.py b/subiquity/controllers/identity.py index 176b3146..4eb998a0 100644 --- a/subiquity/controllers/identity.py +++ b/subiquity/controllers/identity.py @@ -19,13 +19,13 @@ import attr from subiquitycore.context import with_context -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views import IdentityView log = logging.getLogger('subiquity.controllers.identity') -class IdentityController(SubiquityController): +class IdentityController(SubiquityTuiController): autoinstall_key = model_name = "identity" autoinstall_schema = { diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py index 9addf6e6..a0484198 100644 --- a/subiquity/controllers/installprogress.py +++ b/subiquity/controllers/installprogress.py @@ -44,7 +44,7 @@ from subiquitycore.utils import ( ) from subiquity.common.errorreport import ErrorReportKind -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.journald import journald_listener from subiquity.ui.views.installprogress import ProgressView @@ -78,7 +78,7 @@ class TracebackExtractor: self.traceback.append(line) -class InstallProgressController(SubiquityController): +class InstallProgressController(SubiquityTuiController): def __init__(self, app): super().__init__(app) diff --git a/subiquity/controllers/keyboard.py b/subiquity/controllers/keyboard.py index 1a9310e5..d88e8ee7 100644 --- a/subiquity/controllers/keyboard.py +++ b/subiquity/controllers/keyboard.py @@ -20,14 +20,14 @@ import attr from subiquitycore.async_helpers import schedule_task from subiquitycore.context import with_context -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.models.keyboard import KeyboardSetting from subiquity.ui.views import KeyboardView log = logging.getLogger('subiquity.controllers.keyboard') -class KeyboardController(SubiquityController): +class KeyboardController(SubiquityTuiController): autoinstall_key = model_name = "keyboard" autoinstall_schema = { diff --git a/subiquity/controllers/mirror.py b/subiquity/controllers/mirror.py index a26c069e..04110138 100644 --- a/subiquity/controllers/mirror.py +++ b/subiquity/controllers/mirror.py @@ -27,7 +27,7 @@ from subiquitycore.async_helpers import ( ) from subiquitycore.context import with_context -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views.mirror import MirrorView log = logging.getLogger('subiquity.controllers.mirror') @@ -40,7 +40,7 @@ class CheckState(enum.IntEnum): DONE = enum.auto() -class MirrorController(SubiquityController): +class MirrorController(SubiquityTuiController): autoinstall_key = "apt" autoinstall_schema = { # This is obviously incomplete. diff --git a/subiquity/controllers/network.py b/subiquity/controllers/network.py index 9d8cc266..cdc17eab 100644 --- a/subiquity/controllers/network.py +++ b/subiquity/controllers/network.py @@ -21,7 +21,7 @@ from subiquitycore.context import with_context from subiquitycore.controllers.network import NetworkController from subiquity.common.errorreport import ErrorReportKind -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController log = logging.getLogger("subiquity.controllers.network") @@ -65,7 +65,7 @@ NETPLAN_SCHEMA = { } -class NetworkController(NetworkController, SubiquityController): +class NetworkController(NetworkController, SubiquityTuiController): ai_data = None autoinstall_key = "network" diff --git a/subiquity/controllers/package.py b/subiquity/controllers/package.py index 077cd770..56340bca 100644 --- a/subiquity/controllers/package.py +++ b/subiquity/controllers/package.py @@ -13,10 +13,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from subiquity.controller import NoUIController +from subiquity.controller import SubiquityController -class PackageController(NoUIController): +class PackageController(SubiquityController): model_name = autoinstall_key = "packages" autoinstall_default = [] diff --git a/subiquity/controllers/proxy.py b/subiquity/controllers/proxy.py index f4620fdf..366f1291 100644 --- a/subiquity/controllers/proxy.py +++ b/subiquity/controllers/proxy.py @@ -18,13 +18,13 @@ import os from subiquitycore.context import with_context -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views.proxy import ProxyView log = logging.getLogger('subiquity.controllers.proxy') -class ProxyController(SubiquityController): +class ProxyController(SubiquityTuiController): autoinstall_key = model_name = "proxy" autoinstall_schema = { diff --git a/subiquity/controllers/reboot.py b/subiquity/controllers/reboot.py index c7f5bc4e..04ea89d5 100644 --- a/subiquity/controllers/reboot.py +++ b/subiquity/controllers/reboot.py @@ -22,12 +22,12 @@ from subiquitycore.async_helpers import schedule_task from subiquitycore.context import with_context from subiquitycore.utils import arun_command, run_command -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController log = logging.getLogger("subiquity.controllers.restart") -class RebootController(SubiquityController): +class RebootController(SubiquityTuiController): def __init__(self, app): super().__init__(app) diff --git a/subiquity/controllers/refresh.py b/subiquity/controllers/refresh.py index a73be4ab..360f4d89 100644 --- a/subiquity/controllers/refresh.py +++ b/subiquity/controllers/refresh.py @@ -25,12 +25,12 @@ from subiquitycore.async_helpers import ( SingleInstanceTask, ) from subiquitycore.context import with_context -from subiquitycore.controller import ( +from subiquitycore.tuicontroller import ( Skip, ) from subiquity.controller import ( - SubiquityController, + SubiquityTuiController, ) @@ -43,7 +43,7 @@ class CheckState(enum.IntEnum): UNAVAILABLE = enum.auto() -class RefreshController(SubiquityController): +class RefreshController(SubiquityTuiController): autoinstall_key = "refresh-installer" autoinstall_schema = { diff --git a/subiquity/controllers/reporting.py b/subiquity/controllers/reporting.py index 67c5f349..df23d34f 100644 --- a/subiquity/controllers/reporting.py +++ b/subiquity/controllers/reporting.py @@ -29,7 +29,7 @@ from curtin.reporter.handlers import ( LogHandler, ) -from subiquity.controller import NoUIController +from subiquity.controller import SubiquityController class LogHandler(LogHandler): @@ -46,7 +46,7 @@ INITIAL_CONFIG = {'logging': {'type': 'log'}} NON_INTERACTIVE_CONFIG = {'builtin': {'type': 'print'}} -class ReportingController(NoUIController): +class ReportingController(SubiquityController): autoinstall_key = "reporting" autoinstall_schema = { diff --git a/subiquity/controllers/snaplist.py b/subiquity/controllers/snaplist.py index ae873b9c..14a2d5dd 100644 --- a/subiquity/controllers/snaplist.py +++ b/subiquity/controllers/snaplist.py @@ -21,12 +21,12 @@ from subiquitycore.async_helpers import ( schedule_task, ) from subiquitycore.context import with_context -from subiquitycore.controller import ( +from subiquitycore.tuicontroller import ( Skip, ) from subiquity.controller import ( - SubiquityController, + SubiquityTuiController, ) from subiquity.models.snaplist import SnapSelection @@ -104,7 +104,7 @@ class SnapdSnapInfoLoader: return self.tasks[snap] -class SnapListController(SubiquityController): +class SnapListController(SubiquityTuiController): autoinstall_key = "snaps" autoinstall_default = [] diff --git a/subiquity/controllers/ssh.py b/subiquity/controllers/ssh.py index 470d332d..7e4d3462 100644 --- a/subiquity/controllers/ssh.py +++ b/subiquity/controllers/ssh.py @@ -20,7 +20,7 @@ from subiquitycore.async_helpers import schedule_task from subiquitycore.context import with_context from subiquitycore import utils -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views.ssh import SSHView log = logging.getLogger('subiquity.controllers.ssh') @@ -32,7 +32,7 @@ class FetchSSHKeysFailure(Exception): self.output = output -class SSHController(SubiquityController): +class SSHController(SubiquityTuiController): autoinstall_key = model_name = "ssh" autoinstall_schema = { diff --git a/subiquity/controllers/userdata.py b/subiquity/controllers/userdata.py index b781508a..db18b2e1 100644 --- a/subiquity/controllers/userdata.py +++ b/subiquity/controllers/userdata.py @@ -13,10 +13,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from subiquity.controller import NoUIController +from subiquity.controller import SubiquityController -class UserdataController(NoUIController): +class UserdataController(SubiquityController): model_name = 'userdata' autoinstall_key = "user-data" diff --git a/subiquity/controllers/welcome.py b/subiquity/controllers/welcome.py index 54fcd627..acab981f 100644 --- a/subiquity/controllers/welcome.py +++ b/subiquity/controllers/welcome.py @@ -18,14 +18,14 @@ import os from subiquitycore.screen import is_linux_tty -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views import WelcomeView log = logging.getLogger('subiquity.controllers.welcome') -class WelcomeController(SubiquityController): +class WelcomeController(SubiquityTuiController): autoinstall_key = model_name = "locale" autoinstall_schema = {'type': 'string'} diff --git a/subiquity/controllers/zdev.py b/subiquity/controllers/zdev.py index f7e56071..83fdbae5 100644 --- a/subiquity/controllers/zdev.py +++ b/subiquity/controllers/zdev.py @@ -24,7 +24,7 @@ from urwid import Text from subiquitycore.ui.utils import Color from subiquitycore.utils import run_command -from subiquity.controller import SubiquityController +from subiquity.controller import SubiquityTuiController from subiquity.ui.views import ZdevView @@ -631,7 +631,7 @@ class ZdevInfo: return self.type -class ZdevController(SubiquityController): +class ZdevController(SubiquityTuiController): def __init__(self, app): super().__init__(app) diff --git a/subiquity/core.py b/subiquity/core.py index 6116a173..36d7eb47 100644 --- a/subiquity/core.py +++ b/subiquity/core.py @@ -31,7 +31,7 @@ from subiquitycore.async_helpers import ( run_in_thread, schedule_task, ) -from subiquitycore.controller import Skip +from subiquitycore.tuicontroller import Skip from subiquitycore.tui import TuiApplication from subiquitycore.snapd import ( AsyncSnapd, diff --git a/subiquitycore/controller.py b/subiquitycore/controller.py index 16d8e5fe..968dcf64 100644 --- a/subiquitycore/controller.py +++ b/subiquitycore/controller.py @@ -13,16 +13,12 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from abc import ABC, abstractmethod +from abc import ABC import logging log = logging.getLogger("subiquitycore.controller") -class Skip(Exception): - """Raise this from a controller's start_ui method to skip a screen.""" - - class BaseController(ABC): """Base class for controllers.""" @@ -31,12 +27,10 @@ class BaseController(ABC): def __init__(self, app): self.name = type(self).__name__[:-len("Controller")] - self.ui = app.ui self.signal = app.signal self.opts = app.opts self.app = app self.context = self.app.context.child(self.name, childlevel="DEBUG") - self.answers = app.answers.get(self.name, {}) if self.model_name is not None: self.model = getattr(self.app.base_model, self.model_name) @@ -57,92 +51,9 @@ class BaseController(ABC): """ pass - @abstractmethod - def cancel(self): - pass - - @property - def showing(self): - inst = self.app.controllers.cur - while isinstance(inst, RepeatedController): - inst = inst.orig - return inst is self - - @abstractmethod - def start_ui(self): - """Start running this controller's UI. - - This method should call self.ui.set_body. - """ - - def end_ui(self): - """Stop running this controller's UI. - - This method doesn't actually need to remove this controller's UI - as the next one is about to replace it, it's more of a hook to - stop any background tasks that can be stopped when the UI is not - running. - """ - def serialize(self): return None def deserialize(self, data): if data is not None: raise Exception("missing deserialize method on {}".format(self)) - - # Stuff for fine grained actions, used by filesystem and network - # controller at time of writing this comment. - - def _enter_form_data(self, form, data, submit, clean_suffix=''): - for k, v in data.items(): - c = getattr( - self, '_action_clean_{}_{}'.format(k, clean_suffix), None) - if c is None: - c = getattr(self, '_action_clean_{}'.format(k), lambda x: x) - field = getattr(form, k) - from subiquitycore.ui.selector import Selector - v = c(v) - if isinstance(field.widget, Selector): - field.widget._emit('select', v) - field.value = v - yield - yield - for bf in form._fields: - bf.validate() - form.validated() - if submit: - if not form.done_btn.enabled: - raise Exception("answers left form invalid!") - form._click_done(None) - - def _run_actions(self, actions): - for action in actions: - yield from self._answers_action(action) - - def _run_iterator(self, it, delay=None): - if delay is None: - delay = 0.2/self.app.scale_factor - try: - next(it) - except StopIteration: - return - self.app.aio_loop.call_later(delay, self._run_iterator, it, delay/1.1) - - -class RepeatedController(BaseController): - - def __init__(self, orig, index): - self.name = "{}-{}".format(orig.name, index) - self.orig = orig - self.index = index - self.context = orig.context - - def register_signals(self): - pass - - def start_ui(self): - self.orig.start_ui(self.index) - - def cancel(self): - self.orig.cancel() diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py index 793f7da6..56d22ad0 100644 --- a/subiquitycore/controllers/network.py +++ b/subiquitycore/controllers/network.py @@ -24,13 +24,13 @@ from probert.network import IFF_UP, NetworkEventReceiver from subiquitycore.async_helpers import SingleInstanceTask from subiquitycore.context import with_context -from subiquitycore.controller import BaseController from subiquitycore.file_util import write_file from subiquitycore.models.network import ( BondParameters, NetDevAction, ) from subiquitycore import netplan +from subiquitycore.tuicontroller import TuiController from subiquitycore.ui.stretchy import StretchyOverlay from subiquitycore.ui.views.network import ( NetworkView, @@ -134,7 +134,7 @@ network: ''' -class NetworkController(BaseController): +class NetworkController(TuiController): model_name = "network" root = "/" diff --git a/subiquitycore/tui.py b/subiquitycore/tui.py index 7b452a3f..900778b4 100644 --- a/subiquitycore/tui.py +++ b/subiquitycore/tui.py @@ -20,13 +20,13 @@ import yaml import urwid from subiquitycore.async_helpers import schedule_task -from subiquitycore.controller import Skip from subiquitycore.core import Application from subiquitycore.palette import ( PALETTE_COLOR, PALETTE_MONO, ) from subiquitycore.screen import make_screen +from subiquitycore.tuicontroller import Skip from subiquitycore.ui.frame import SubiquityCoreUI from subiquitycore.utils import arun_command @@ -66,8 +66,8 @@ class TuiApplication(Application): # Set rich_mode to the opposite of what we want, so we can # call toggle_rich to get the right things set up. self.rich_mode = opts.run_on_serial - self.urwid_loop = None + self.cur_screen = None def _remove_last_screen(self): last_screen = self.state_path('last-screen') @@ -102,6 +102,7 @@ class TuiApplication(Application): raise Skip try: new.start_ui() + self.cur_screen = new except Skip: new.context.exit("(skipped)") raise @@ -110,7 +111,7 @@ class TuiApplication(Application): def _move_screen(self, increment): self.save_state() - old = self.controllers.cur + old, self.cur_screen = self.cur_screen, None if old is not None: old.context.exit("completed") old.end_ui() @@ -142,6 +143,8 @@ class TuiApplication(Application): for controller in self.controllers.instances[:controller_index]: controller.configured() self.controllers.index = controller_index - 1 + for controller in self.controllers.instances[:controller_index]: + controller.configured() self.next_screen() def run_scripts(self, scripts): diff --git a/subiquitycore/tuicontroller.py b/subiquitycore/tuicontroller.py new file mode 100644 index 00000000..345f623a --- /dev/null +++ b/subiquitycore/tuicontroller.py @@ -0,0 +1,120 @@ +# Copyright 2020 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from abc import abstractmethod +import logging + +from subiquitycore.controller import BaseController + +log = logging.getLogger("subiquitycore.tuicontroller") + + +class Skip(Exception): + """Raise this from a controller's start_ui method to skip a screen.""" + + +class TuiController(BaseController): + """Base class for controllers.""" + + def __init__(self, app): + super().__init__(app) + self.ui = app.ui + self.answers = app.answers.get(self.name, {}) + + @abstractmethod + def cancel(self): + pass + + @property + def showing(self): + inst = self.app.controllers.cur + while isinstance(inst, RepeatedController): + inst = inst.orig + return inst is self + + @abstractmethod + def start_ui(self): + """Start running this controller's UI. + + This method should call self.ui.set_body. + """ + + def end_ui(self): + """Stop running this controller's UI. + + This method doesn't actually need to remove this controller's UI + as the next one is about to replace it, it's more of a hook to + stop any background tasks that can be stopped when the UI is not + running. + """ + + # Stuff for fine grained actions, used by filesystem and network + # controller at time of writing this comment. + + def _enter_form_data(self, form, data, submit, clean_suffix=''): + for k, v in data.items(): + c = getattr( + self, '_action_clean_{}_{}'.format(k, clean_suffix), None) + if c is None: + c = getattr(self, '_action_clean_{}'.format(k), lambda x: x) + field = getattr(form, k) + from subiquitycore.ui.selector import Selector + v = c(v) + if isinstance(field.widget, Selector): + field.widget._emit('select', v) + field.value = v + yield + yield + for bf in form._fields: + bf.validate() + form.validated() + if submit: + if not form.done_btn.enabled: + raise Exception("answers left form invalid!") + form._click_done(None) + + def _run_actions(self, actions): + for action in actions: + yield from self._answers_action(action) + + def _run_iterator(self, it, delay=None): + if delay is None: + delay = 0.2/self.app.scale_factor + try: + next(it) + except StopIteration: + return + self.app.aio_loop.call_later(delay, self._run_iterator, it, delay/1.1) + + +class RepeatedController(BaseController): + + def __init__(self, orig, index): + self.name = "{}-{}".format(orig.name, index) + self.orig = orig + self.index = index + self.context = orig.context + + def register_signals(self): + pass + + def start_ui(self): + self.orig.start_ui(self.index) + + def end_ui(self): + self.orig.end_ui() + + def cancel(self): + self.orig.cancel()