[master 4/6] Make the dispatcher call the shots.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Changes the way the dispatcher and anaconda.intf cooperate in an attempt
to take control away from the interface and give it to the dispatcher
where it should be.

The dispatcher.moveStep() method is rewritten into a form that does not
include the complex condition in the while cycle. Also the method was
renamed to dispatch() to reflect that it (exclusively) containts the
step-shifting logic and that upon a single call to it the installation can
progress more than one step (or all of them).

For GUI interface implementation, this patch removes icw.setScreen and
isolates the code that creates next GUI screen into the display_step()
method. Similar changes happened for the text and command line interfaces.
---
 anaconda                |    2 +-
 pyanaconda/cmdline.py   |   25 +++++-----
 pyanaconda/constants.py |    3 +-
 pyanaconda/dispatch.py  |  120 ++++++++++++++++++++++++----------------------
 pyanaconda/gui.py       |   69 ++++++++++++++-------------
 pyanaconda/packages.py  |    8 ++--
 pyanaconda/text.py      |   69 ++++++++++++++-------------
 7 files changed, 154 insertions(+), 142 deletions(-)

diff --git a/anaconda b/anaconda
index 3f74832..c8d2a6d 100755
--- a/anaconda
+++ b/anaconda
@@ -924,7 +924,7 @@ if __name__ == "__main__":
         kickstart.setSteps(anaconda)
 
     try:
-        anaconda.intf.run(anaconda)
+        anaconda.dispatch.run()
     except SystemExit, code:
         anaconda.intf.shutdown()
 
diff --git a/pyanaconda/cmdline.py b/pyanaconda/cmdline.py
index 4559513..2ca34f9 100644
--- a/pyanaconda/cmdline.py
+++ b/pyanaconda/cmdline.py
@@ -163,19 +163,18 @@ class InstallInterface(InstallInterfaceBase):
         pass
 
     def run(self, anaconda):
-        (step, instance) = anaconda.dispatch.currentStep()
-        while step:
-            if stepToClasses.has_key(step):
-                s = "nextWin = %s" %(stepToClasses[step],)
-                exec s
-                nextWin(instance)
-            else:
-                print("In interactive step %s, can't continue" %(step,))
-                while 1:
-                    time.sleep(1)
-
-            anaconda.dispatch.gotoNext()
-	    (step, instance) = anaconda.dispatch.currentStep()
+        self.anaconda = anaconda
+        self.anaconda.dispatch.dispatch()
+
+    def display_step(self, step):
+        if stepToClasses.has_key(step):
+            s = "nextWin = %s" %(stepToClasses[step],)
+            exec s
+            nextWin(self.anaconda)
+        else:
+            print("In interactive step %s, can't continue" %(step,))
+            while 1:
+                time.sleep(1)
 
     def setInstallProgressClass(self, c):
         self.instProgress = c
diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 498d41f..994bdda 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -28,7 +28,8 @@ SELINUX_DEFAULT = 1
 
 DISPATCH_BACK = -1
 DISPATCH_FORWARD = 1
-DISPATCH_NOOP = None
+DISPATCH_DEFAULT = None
+DISPATCH_WAITING = 2
 
 # different types of partition requests
 # REQUEST_PREEXIST is a placeholder for a pre-existing partition on the system
diff --git a/pyanaconda/dispatch.py b/pyanaconda/dispatch.py
index 70c508b..247413b 100644
--- a/pyanaconda/dispatch.py
+++ b/pyanaconda/dispatch.py
@@ -118,16 +118,17 @@ installSteps = [
 
 class Dispatcher(object):
 
-    def gotoPrev(self):
+    def go_back(self):
         """
-        You should make sure canGoBack() returns True before attempting gotoPrev().
+        The caller should make sure canGoBack() is True before calling this
+        method.
         """
         self._setDir(DISPATCH_BACK)
-        self.moveStep()
+        self.dispatch()
 
-    def gotoNext(self):
+    def go_forward(self):
         self._setDir(DISPATCH_FORWARD)
-        self.moveStep()
+        self.dispatch()
 
     def canGoBack(self):
         # begin with the step before this one.  If all steps are skipped,
@@ -139,6 +140,10 @@ class Dispatcher(object):
             i = i - 1
         return False
 
+    def run(self):
+        self.anaconda.intf.run(self.anaconda)
+        log.info("dispatch: finished.")
+
     def setStepList(self, *steps):
         # only remove non-permanently skipped steps from our skip list
         for step, state in self.skipSteps.items():
@@ -157,7 +162,7 @@ class Dispatcher(object):
             if not stepExists.has_key(name):
                 #XXX: hack for yum support
                 #raise KeyError, ("step %s does not exist" % name)
-                log.warning("step %s does not exist", name)
+                log.warning("dispatch: step %s does not exist", name)
 
     def stepInSkipList(self, step):
         if type(step) == type(1):
@@ -179,7 +184,7 @@ class Dispatcher(object):
                         del self.skipSteps[name]
                 return
 
-        log.warning("step %s does not exist", stepToSkip)
+        log.warning("dispatch: step %s does not exist", stepToSkip)
 
     def stepIsDirect(self, step):
         """Takes a step number"""
@@ -188,57 +193,56 @@ class Dispatcher(object):
         else:
             return False
 
-    def moveStep(self):
+    def dispatch(self):
+        total_steps = len(installSteps)
         if self.step == None:
+            log.info("dispatch: resetting to the first step.")
             self.step = self.firstStep
         else:
-            if self.step >= len(installSteps):
-                return None
-
-            log.info("leaving (%d) step %s" %(self._getDir(), installSteps[self.step][0]))
-            self.step = self.step + self._getDir()
-
-            if self.step >= len(installSteps):
-                return None
-
-        while self.step >= self.firstStep and self.step < len(installSteps) \
-            and (self.stepInSkipList(self.step) or self.stepIsDirect(self.step)):
-
-            if self.stepIsDirect(self.step) and not self.stepInSkipList(self.step):
-                (stepName, stepFunc) = installSteps[self.step]
-                log.info("moving (%d) to step %s" %(self._getDir(), stepName))
-                log.debug("%s is a direct step" %(stepName,))
-                rc = stepFunc(self.anaconda)
-                if rc in [DISPATCH_BACK, DISPATCH_FORWARD]:
-                    self._setDir(rc)
-                log.info("leaving (%d) step %s" %(self._getDir(), stepName))
-                # if anything else, leave self.dir alone
-
-            self.step = self.step + self._getDir()
-            if self.step == len(installSteps):
-                return None
-
-        if (self.step < 0):
-            # pick the first step not in the skip list
-            self.step = 0
-            while self.skipSteps.has_key(installSteps[self.step][0]):
-                self.step = self.step + 1
-        elif self.step >= len(installSteps):
-            self.step = len(installSteps) - 1
-            while self.skipSteps.has_key(installSteps[self.step][0]):
-                self.step = self.step - 1
-        log.info("moving (%d) to step %s" %(self._getDir(), installSteps[self.step][0]))
-
-    def currentStep(self):
-        if self.step == None:
-            self.gotoNext()
-        elif self.step >= len(installSteps):
-            return (None, None)
-
-        stepInfo = installSteps[self.step]
-        step = stepInfo[0]
-
-        return (step, self.anaconda)
+            log.info("dispatch: leaving (%d) step %s" %
+                     (self.dir, installSteps[self.step][0]))
+            self.step += self.dir
+
+        while True:
+            if self.step >= total_steps:
+                # installation has proceeded beyond the last step: finished
+                self.anaconda.intf.shutdown()
+                return
+            if self.step < 0:
+                raise RuntimeError("dispatch: out  of bounds "
+                                   "(dir: %d, step: %d)" % (self.dir, self.step))
+
+            if self.stepInSkipList(self.step):
+                self.step += self.dir
+                continue
+
+            if self.stepIsDirect(self.step):
+                # handle a direct step by just calling the function
+                (step_name, step_func) = installSteps[self.step]
+                log.info("dispatch: moving (%d) to step %s" %
+                         (self.dir, step_name))
+                log.debug("dispatch: %s is a direct step" % step_name)
+                self.dir = step_func(self.anaconda)
+            else:
+                # handle an indirect step (IOW the user interface has a screen
+                # to display to the user):
+                step_name = installSteps[self.step][0]
+                log.info("dispatch: moving (%d) to step %s" %
+                         (self.dir, step_name))
+                rc = self.anaconda.intf.display_step(step_name)
+                if rc == DISPATCH_WAITING:
+                    # a new screen has been set up and we are waiting for the
+                    # user input now (this only ever happens with the GTK UI and
+                    # is because we need to get back to gtk.main())
+                    return
+                elif rc == DISPATCH_DEFAULT:
+                    log.debug("dispatch: the interface chose "
+                                "not to display step %s." % step_name)
+                else:
+                    self.dir = rc
+            log.info("dispatch: leaving (%d) step %s" %
+                     (self.dir, step_name))
+            self.step += self.dir
 
     def __init__(self, anaconda):
         self.anaconda = anaconda
@@ -252,7 +256,9 @@ class Dispatcher(object):
         return self.anaconda.dir
 
     def _setDir(self, dir):
-        self.anaconda.dir = dir
+        if dir not in [DISPATCH_BACK, DISPATCH_FORWARD, DISPATCH_DEFAULT]:
+            raise RuntimeError("dispatch: wrong direction code")
+        if dir in [DISPATCH_BACK, DISPATCH_FORWARD]:
+            self.anaconda.dir = dir
 
     dir = property(_getDir,_setDir)
-
diff --git a/pyanaconda/gui.py b/pyanaconda/gui.py
index 61f2d69..4d91aa7 100755
--- a/pyanaconda/gui.py
+++ b/pyanaconda/gui.py
@@ -89,6 +89,14 @@ stepToClass = {
 if iutil.isS390():
     stepToClass["bootloader"] = ("zipl_gui", "ZiplWindow")
 
+def idle_gtk(func, *args, **kwargs):
+    def return_false(func, *args, **kwargs):
+        gtk.gdk.threads_enter()
+        func(*args, **kwargs)
+        gtk.gdk.threads_leave()
+        return False
+    gobject.idle_add(return_false, func, *args, **kwargs)
+
 #
 # Stuff for screenshots
 #
@@ -930,8 +938,9 @@ class InstallInterface(InstallInterfaceBase):
     def __del__ (self):
         pass
 
-    def shutdown (self):
-        pass
+    def shutdown(self):
+        if self.icw:
+            self.icw.close()
 
     def suspend(self):
         pass
@@ -1208,6 +1217,9 @@ class InstallInterface(InstallInterfaceBase):
                                   custom_buttons = [_("_Exit installer")],
                                   custom_icon = "error")
 
+    def display_step(self, step):
+        return self.icw.display_step(step)
+
     def getBootdisk (self):
         return None
 
@@ -1217,7 +1229,7 @@ class InstallInterface(InstallInterfaceBase):
         if anaconda.keyboard and not flags.livecdInstall:
             anaconda.keyboard.activate()
 
-        self.icw = InstallControlWindow (self.anaconda)
+        self.icw = InstallControlWindow(self.anaconda)
         self.icw.run()
 
     def setSteps(self, anaconda):
@@ -1245,8 +1257,7 @@ class InstallControlWindow:
         except StayOnScreen:
             return
 
-        self.anaconda.dispatch.gotoPrev()
-        self.setScreen ()
+        self.anaconda.dispatch.go_back()
 
     def nextClicked (self, *args):
         try:
@@ -1254,8 +1265,7 @@ class InstallControlWindow:
         except StayOnScreen:
             return
 
-        self.anaconda.dispatch.gotoNext()
-        self.setScreen ()
+        self.anaconda.dispatch.go_forward()
 
     def debugClicked (self, *args):
         try:
@@ -1286,18 +1296,7 @@ class InstallControlWindow:
         else:
             gobject.source_remove(self.handle)
 
-    def setScreen (self):
-        (step, anaconda) = self.anaconda.dispatch.currentStep()
-        if step is None:
-            gtk.main_quit()
-            return
-
-        if not stepToClass[step]:
-            if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
-                return self.nextClicked()
-            else:
-                return self.prevClicked()
-
+    def display_step(self, step):
         (file, className) = stepToClass[step]
         newScreenClass = None
 
@@ -1335,33 +1334,27 @@ class InstallControlWindow:
         self.destroyCurrentWindow()
         self.currentWindow = newScreenClass(ics)
 
-        new_screen = self.currentWindow.getScreen(anaconda)
+        new_screen = self.currentWindow.getScreen(self.anaconda)
 
         # If the getScreen method returned None, that means the screen did not
         # want to be displayed for some reason and we should skip to the next
         # step.  However, we do not want to remove the current step from the
         # list as later events may cause the screen to be displayed.
         if not new_screen:
-            if self.anaconda.dispatch.dir == DISPATCH_FORWARD:
-                self.anaconda.dispatch.gotoNext()
-            else:
-                self.anaconda.dispatch.gotoPrev()
-
-            return self.setScreen()
+            return DISPATCH_DEFAULT
 
         self.update (ics)
-
         self.installFrame.add(new_screen)
         self.installFrame.show_all()
-
         self.currentWindow.focus()
-
         self.handle = gobject.idle_add(self.handleRenderCallback)
-
         if self.reloadRcQueued:
             self.window.reset_rc_styles()
             self.reloadRcQueued = 0
 
+        # the screen is displayed, we wait for the user now
+        return DISPATCH_WAITING
+
     def destroyCurrentWindow(self):
         children = self.installFrame.get_children ()
         if children:
@@ -1380,6 +1373,7 @@ class InstallControlWindow:
         self.mainxml.get_widget("nextButton").set_flags(gtk.HAS_DEFAULT)
 
     def __init__ (self, anaconda):
+        self._main_loop_running = False
         self.reloadRcQueued = 0
         self.currentWindow = None
         self.anaconda = anaconda
@@ -1399,7 +1393,9 @@ class InstallControlWindow:
             takeScreenShot()
 
     def close(self, *args):
-        gtk.main_quit()
+        if self._main_loop_running:
+            gtk.main_quit()
+            self._main_loop_running = False
 
     def _doExitConfirm (self, win = None, *args):
         # FIXME: translate the string
@@ -1413,6 +1409,7 @@ class InstallControlWindow:
         self.close()
 
     def createWidgets (self):
+        """ Sets up the widgets in the main installler window. """
         self.window.set_title(_("%s Installer") %(productName,))
 
         i = self.mainxml.get_widget("headerImage")
@@ -1442,7 +1439,7 @@ class InstallControlWindow:
 
     def connectSignals(self):
         sigs = { "on_nextButton_clicked": self.nextClicked,
-            "on_rebootButton_clicked": self.nextClicked,
+            "on_rebootButton_clicked": self.close,
             "on_closeButton_clicked": self.close,
             "on_backButton_clicked": self.prevClicked,
             "on_debugButton_clicked": self.debugClicked,
@@ -1472,7 +1469,10 @@ class InstallControlWindow:
 
         self.createWidgets()
         self.connectSignals()
-        self.setScreen()
+        # 'Back and 'Next' is disabled by default
+        icw = InstallControlState(self)
+        icw.setPrevEnabled(False)
+        icw.setNextEnabled(False)
         self.window.show()
         # calling present() will focus the window in the winodw manager so
         # the mnemonics work without additional clicking
@@ -1487,6 +1487,9 @@ class InstallControlWindow:
     def run (self):
         self.setup_theme()
         self.setup_window(False)
+        # start the dispatcher right after the main loop is started:
+        idle_gtk(self.anaconda.dispatch.dispatch)
+        self._main_loop_running = True
         gtk.main()
 
 class InstallControlState:
diff --git a/pyanaconda/packages.py b/pyanaconda/packages.py
index aa8dfc7..a3e561a 100644
--- a/pyanaconda/packages.py
+++ b/pyanaconda/packages.py
@@ -90,7 +90,7 @@ def turnOnFilesystems(anaconda):
         if not anaconda.upgrade:
             log.info("unmounting filesystems")
             anaconda.storage.umountFilesystems()
-        return DISPATCH_NOOP
+        return DISPATCH_DEFAULT
 
     if not anaconda.upgrade:
         if (flags.livecdInstall and
@@ -305,9 +305,9 @@ def betaNagScreen(anaconda):
                     "Fedora Core": "Fedora Core",
                     "Fedora": "Fedora" }
 
-    
+
     if anaconda.dir == DISPATCH_BACK:
-	return DISPATCH_NOOP
+	return DISPATCH_DEFAULT
 
     fileagainst = None
     for (key, val) in publicBetas.items():
@@ -350,7 +350,7 @@ def betaNagScreen(anaconda):
 
 def doReIPL(anaconda):
     if not iutil.isS390() or anaconda.dir == DISPATCH_BACK:
-        return DISPATCH_NOOP
+        return DISPATCH_DEFAULT
 
     anaconda.reIPLMessage = iutil.reIPL(anaconda, os.getppid())
 
diff --git a/pyanaconda/text.py b/pyanaconda/text.py
index 86ae5e4..3905266 100644
--- a/pyanaconda/text.py
+++ b/pyanaconda/text.py
@@ -518,47 +518,50 @@ class InstallInterface(InstallInterfaceBase):
 
         # draw the frame after setting up the fallback
         self.drawFrame()
+        # and now descend into the dispatcher
+        self.anaconda.dispatch.dispatch()
 
-        (step, instance) = anaconda.dispatch.currentStep()
-	while step:
-            (file, className) = stepToClasses[step]
-            while 1:
-                try:
-                    found = imp.find_module(file, textw.__path__)
-                    moduleName = 'pyanaconda.textw.%s' % file
-                    loaded = imp.load_module(moduleName, *found)
-                    nextWindow = loaded.__dict__[className]
-                    break
-                except ImportError as e:
-                    log.error("loading interface component %s" % className)
-                    log.error(traceback.format_exc())
-                    rc = ButtonChoiceWindow(self.screen, _("Error!"),
-                                      _("An error occurred when attempting "
-                                        "to load an installer interface "
-                                        "component.\n\nclassName = %s")
-                                      % className,
-                                      buttons=[_("Exit"), _("Retry")])
-
-                    if rc == string.lower(_("Exit")):
-                        sys.exit(0)
-
-            win = nextWindow()
-            rc = win(self.screen, instance)
-
-            if rc in [INSTALL_OK, INSTALL_NOOP]:
-                anaconda.dispatch.gotoNext()
+    def display_step(self, step):
+        (file, className) = stepToClasses[step]
+        while True:
+            try:
+                found = imp.find_module(file, textw.__path__)
+                moduleName = 'pyanaconda.textw.%s' % file
+                loaded = imp.load_module(moduleName, *found)
+                nextWindow = loaded.__dict__[className]
+                break
+            except ImportError as e:
+                log.error("loading interface component %s" % className)
+                log.error(traceback.format_exc())
+                rc = ButtonChoiceWindow(self.screen, _("Error!"),
+                                  _("An error occurred when attempting "
+                                    "to load an installer interface "
+                                    "component.\n\nclassName = %s")
+                                  % className,
+                                  buttons=[_("Exit"), _("Retry")])
+
+                if rc == string.lower(_("Exit")):
+                    sys.exit(0)
+        win = nextWindow()
+
+        while True:
+            rc = win(self.screen, self.anaconda)
+            if rc == INSTALL_OK:
+                return DISPATCH_FORWARD
+            elif rc == INSTALL_NOOP:
+                return DISPATCH_DEFAULT
             elif rc == INSTALL_BACK:
-                if anaconda.dispatch.canGoBack():
-                    anaconda.dispatch.gotoPrev()
+                if self.anaconda.dispatch.canGoBack():
+                    return DISPATCH_BACK
                 else:
                     ButtonChoiceWindow(self.screen, _("Cancelled"),
                                        _("I can't go to the previous step "
                                          "from here. You will have to try "
                                          "again."),
                                        buttons=[_("OK")])
-            (step, instance) = anaconda.dispatch.currentStep()
-
-        self.screen.finish()
+                    # keep displaying the same dialog until the user gives us
+                    # a better answer
+                    continue
 
     def setSteps(self, anaconda):
         anaconda.dispatch.skipStep("filtertype", permanent=1)
-- 
1.7.3.3

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list


[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux