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