Commits

Rodrigo Bistolfi  committed 316a61c

Split the controller logic from Application object

  • Participants
  • Parent commits eeedf95

Comments (0)

Files changed (1)

File vinstall/core/wizard.py

+# coding: utf8
+
+"""VASM application toolkit prototype"""
+
+
+import Queue
+import sys
+from inspect import isgeneratorfunction
+from .command import ProcessingFacade
+
+
+class WizardApplication(object):
+    "Wizard App"
+
+
+    def __init__(self, first_controller, config=None):
+        self.config = config or {}
+        self.controller = first_controller
+        self.protocol = WizardProtocol()
+        self.protocol.factory = self
+
+    def set_view(self, view):
+        "Set the view for this app"
+        mod_name = "vinstall.ui"
+
+        try:
+            module = __import__(mod_name, globals(), locals(), [view],
+                    level=1)
+        except ImportError:
+            #2.7 behaves differently
+            module = __import__(mod_name, globals(), locals(), [view],
+                    level=0)
+        sys.modules[mod_name] = module
+
+    def make_controller_instance(self, klass):
+        """Create an instance of the controller class
+
+        """
+        obj = klass()
+        obj.config = self.config
+        init_method = getattr(obj, "init", None)
+        if init_method:
+            init_method()
+        return obj
+
+    def run(self):
+        controller = self.make_controller_instance(self.controller)
+        self.protocol.start(controller)
+
+    def stop(self):
+        self.protocol.stop()
+        sys.exit(0)
+
+
+class WizardProtocol(object):
+    """Controller protocol for wizard-like applications
+    This object is responsible for processing each controller object thats part
+    of the wizard.
+
+    """
+    def __init__(self):
+        self.factory = None
+        self.current_controller = None
+        self.current_render = None
+        self.commands = []
+
+    def process_state(self, controller):
+        "Process the current state of the wizard"
+        self.current_controller = controller
+        self.show()
+
+    def show(self):
+        "Present the current controller to the user"
+        self.current_render = render = self.current_controller.render()
+        render.main_window.clear_callbacks()
+        render.update_main_window()
+        render.main_window.add_next_callback(self.next)
+        render.main_window.add_previous_callback(self.previous)
+
+    def run_controller(self):
+        "Execute tasks specified by the controller"
+        process_method = getattr(self.current_controller, "process", None)
+        args = self.current_render.get_user_input()
+        if process_method:
+            self.execute(process_method, args, {})
+        command_method = getattr(self.current_controller, "command", None)
+        if command_method:
+            self.commands.append((command_method, args))
+
+    def next(self, *_):
+        "Jump to the next state of the application"
+        self.run_controller()
+        next_controller = self.current_controller.next()
+        if next_controller:
+            instance = self.factory.make_controller_instance(next_controller)
+            self.process_state(instance)
+        else:
+            self.process_task_queue()
+
+    def previous(self, *_):
+        "Jump back to previous controller"
+        controller = self.current_controller.previous()
+        if controller:
+            command = getattr(controller, "command", None)
+            if command:
+                self.commands.pop()
+            instance = self.factory.make_controller_instance(controller)
+            self.process_state(instance)
+        else:
+            self.stop()
+
+    def execute(self, a_callable, args, kwargs):
+        "Execute callable"
+        a_callable(*args, **kwargs)
+
+    def process_task_queue(self):
+        """Execute tasks scheduled in the command list.
+        We will inject a last controller for showing progress and reporting
+        task completion, and set callbacks for quiting.
+
+        """
+        self.current_render.main_window.disable_buttons()
+        queue = Queue.Queue()
+        processing = ProcessingFacade(queue)
+        for func, args in self.commands:
+            if isgeneratorfunction(func):
+                for f, a, desc in func(*args):
+                    processing.add_command(f, args=a, description=desc)
+            else:
+                processing.add_command(func, args=args,
+                        description=func.__doc__)
+
+        def tasks_finished_callback():
+            self.current_render.main_window.set_next_button_label("Reboot")
+            self.current_render.main_window.set_previous_button_label("Quit")
+            self.current_render.main_window.enable_buttons()
+            #self.current_render.main_window.alert("Installation finished")
+
+        processing.add_command(tasks_finished_callback, args=tuple(),
+                description="All tasks done")
+
+        Render = type(self.current_render)
+
+        class ProcessingController(object):
+
+            def render(self):
+                return Render("Processing tasks", ("Please wait until we "
+                        "process the requested operations"), processing)
+
+            def next(self):
+                return None
+
+            def previous(self):
+                return None
+
+	controller = self.factory.make_controller_instance(ProcessingController)
+        self.process_state(controller)
+        self.current_render.main_window.clear_callbacks()
+        self.current_render.main_window.add_next_callback(self.stop)
+        self.current_render.main_window.add_previous_callback(self.stop)
+        processing.execute_all()
+
+    def start(self, first_controller):
+        "Start the main loop"
+        self.process_state(first_controller)
+        self.current_render.main_window.run()
+
+    def stop(self, *_):
+        "Exit the wizard"
+        self.current_render.main_window.stop()