--- pyanaconda/ui/__init__.py | 30 +++++++++++++++++----- pyanaconda/ui/common.py | 14 ++++++++++ pyanaconda/ui/gui/__init__.py | 44 +++++++++++++++++++++++--------- pyanaconda/ui/gui/categories/__init__.py | 2 +- pyanaconda/ui/gui/hubs/__init__.py | 14 ++++------ pyanaconda/ui/tui/__init__.py | 24 ++++++++++++++--- pyanaconda/ui/tui/hubs/__init__.py | 2 +- 7 files changed, 96 insertions(+), 34 deletions(-)
diff --git a/pyanaconda/ui/__init__.py b/pyanaconda/ui/__init__.py index aabc719..26e540a 100644 --- a/pyanaconda/ui/__init__.py +++ b/pyanaconda/ui/__init__.py @@ -22,6 +22,7 @@ __all__ = ["UserInterface"]
import os +import inspect from common import collect
class UserInterface(object): @@ -59,6 +60,27 @@ class UserInterface(object): from pyanaconda.errors import errorHandler errorHandler.ui = self
+ @property + def basepath(self): + """returns the directory name for UI subtree""" + return os.path.dirname(inspect.getfile(self.__class__)) + + @property + def basemask(self): + """returns the python module name for basepath directory""" + return "pyanaconda.ui" + + @property + def paths(self): + """return dictionary mapping plugin elements (spokes, hubs, categories) + to a list of tuples (module mask, search path)""" + return { + "spokes": [(self.basemask + ".spokes.%s", + os.path.join(self.basepath, "spokes"))], + "hubs": [(self.basemask + ".hubs.%s", + os.path.join(self.basepath, "hubs"))] + } + def setup(self, data): """Construct all the objects required to implement this interface. This method must be provided by all subclasses. @@ -121,7 +143,7 @@ class UserInterface(object):
return standalones
- def _orderActionClasses(self, spokes, hubs, standalone_class): + def _orderActionClasses(self, spokes, hubs): """Order all the Hub and Spoke classes which should be enqueued for processing according to their pre/post dependencies.
@@ -129,15 +151,9 @@ class UserInterface(object): to the hub dependencies :type spokes: list of Spoke instances
- :param path: the directory we are picking up modules from - :type path: string - :param hubs: the list of Hub classes we check to be in pre/postForHub attribute of Spokes to pick up :type hubs: common.Hub based types - - :param standalone_class: the parent type of Spokes we want to pick up - :type standalone_class: common.StandaloneSpoke based types """
actionClasses = [] diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py index d5ff8af..11f5e0b 100644 --- a/pyanaconda/ui/common.py +++ b/pyanaconda/ui/common.py @@ -23,6 +23,14 @@ import os import imp import inspect +import copy + +class PathDict(dict): + """Dictionary class supporting + operator""" + def __add__(self, ext): + new_dict = copy.copy(self) + new_dict.update(ext) + return new_dict
class UIObject(object): """This is the base class from which all other UI classes are derived. It @@ -377,7 +385,13 @@ class Hub(UIObject): self.storage = storage self.payload = payload self.instclass = instclass + self.paths = {}
+ def set_path(self, path_id, paths): + """Update the paths attribute with list of tuples in the form (module + name format string, directory name)""" + self.paths[path_id] = paths + def collect(module_pattern, path, pred): """Traverse the directory (given by path), import all files as a module module_pattern % filename and find all classes within that match diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index f9b51e2..a09ce20 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -28,6 +28,7 @@ from pyanaconda.product import distributionText, isFinal from pyanaconda.ui import UserInterface, common from pyanaconda.ui.gui.utils import enlightbox, gtk_thread_wait from pyanaconda.product import isFinal, productName, productVersion +import os.path
import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -62,17 +63,19 @@ class GraphicalUserInterface(UserInterface): import ctypes ctypes.CDLL("libAnacondaWidgets.so.0", ctypes.RTLD_GLOBAL)
+ @property + def basemask(self): + return "pyanaconda.ui.gui" + def _list_hubs(self): - from hubs.summary import SummaryHub - from hubs.progress import ProgressHub + """Return a list of Hub classes to be imported to this interface""" + from .hubs.summary import SummaryHub + from .hubs.progress import ProgressHub return [SummaryHub, ProgressHub]
- def _list_standalone_paths(self): - path = os.path.join(os.path.dirname(__file__), "spokes") - return [("pyanaconda.ui.gui.spokes.%s", path)] - def _is_standalone(self, obj): - from spokes import StandaloneSpoke + """Is the spoke passes as obj standalone?""" + from .spokes import StandaloneSpoke return isinstance(obj, StandaloneSpoke)
def setup(self, data): @@ -82,13 +85,25 @@ class GraphicalUserInterface(UserInterface): self.data = data
def getActionClasses(self, hubs): - from spokes import StandaloneSpoke + """Grab all relevant standalone spokes, add them to the passed + list of hubs and order the list according to the + relationships between hubs and standalones.""" + from .spokes import StandaloneSpoke
# First, grab a list of all the standalone spokes. - standalones = self._collectActionClasses(self._list_standalone_paths(), StandaloneSpoke) + standalones = self._collectActionClasses(self.paths["spokes"], StandaloneSpoke)
# Second, order them according to their relationship - return self._orderActionClasses(standalones, hubs, StandaloneSpoke) + return self._orderActionClasses(standalones, hubs) + + @property + def paths(self): + _paths = UserInterface.paths.fget(self) + _paths.update({"categories": [(self.basemask + ".categories.%s", + os.path.join(self.basepath, "categories"))] + }) + return _paths +
def _instantiateAction(self, actionClass): from spokes import StandaloneSpoke @@ -97,10 +112,15 @@ class GraphicalUserInterface(UserInterface): # spoke API and setting up continue/quit signal handlers. obj = actionClass(self.data, self.storage, self.payload, self.instclass)
+ # set spoke search paths in Hubs + if hasattr(obj, "set_path"): + obj.set_path("spokes", self.paths["spokes"]) + obj.set_path("categories", self.paths["categories"]) + # If we are doing a kickstart install, some standalone spokes - # could already be filled out. In that case, we do not want + # could already be filled out. In taht case, we do not want # to display them. - if isinstance(obj, StandaloneSpoke) and obj.completed: + if self._is_standalone(obj) and obj.completed: del(obj) return None
diff --git a/pyanaconda/ui/gui/categories/__init__.py b/pyanaconda/ui/gui/categories/__init__.py index c318901..14dc2a0 100644 --- a/pyanaconda/ui/gui/categories/__init__.py +++ b/pyanaconda/ui/gui/categories/__init__.py @@ -77,7 +77,7 @@ def collect_categories(mask_paths): """ categories = [] for mask, path in mask_paths: - categories.extend(collect(mask, path, lambda obj: getattr(obj, "displayOnHub", None) != None) + categories.extend(collect(mask, path, lambda obj: getattr(obj, "displayOnHub", None) != None))
return categories
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index 4447367..f9cc933 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -22,7 +22,9 @@ import gettext _ = lambda x: gettext.ldgettext("anaconda", x)
+ # pylint: disable-msg=E0611 +import os from gi.repository import GLib
from pyanaconda.flags import flags @@ -111,12 +113,6 @@ class Hub(GUIObject, common.Hub): action.apply() action.execute()
- def _list_spokes_mask_paths(self): - return [("pyanaconda.ui.gui.spokes.%s", os.path.join(os.path.dirname(__file__), "spokes"))] - - def _list_categories_mask_paths(self): - return [("pyanaconda.ui.gui.categories.%s", os.path.join(os.path.dirname(__file__), "categories"))] - def _collectCategoriesAndSpokes(self): """collects categories and spokes to be displayed on this Hub
@@ -128,10 +124,10 @@ class Hub(GUIObject, common.Hub):
# Collect all the categories this hub displays, then collect all the # spokes belonging to all those categories. - categories = sorted(filter(lambda c: c.displayOnHub == self.__class__, collect_categories(self._list_categories_mask_paths())), + categories = sorted(filter(lambda c: c.displayOnHub == self.__class__, collect_categories(self.paths["categories"])), key=lambda c: c.title) for c in categories: - ret[c] = collect_spokes(self._list_spokes_mask_paths(), c.__name__) + ret[c] = collect_spokes(self.paths["spokes"], c.__name__)
return ret
@@ -250,7 +246,7 @@ class Hub(GUIObject, common.Hub):
@property def continuePossible(self): - return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0 + return len(self._incompleteSpokes)==0 and len(self._notReadySpokes) == 0
def _updateContinueButton(self): self.continueButton.set_sensitive(self.continuePossible) diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py index c4a15a0..cccbf70 100644 --- a/pyanaconda/ui/tui/__init__.py +++ b/pyanaconda/ui/tui/__init__.py @@ -129,27 +129,43 @@ class TextUserInterface(ui.UserInterface): ui.UserInterface.__init__(self, storage, payload, instclass) self._app = None
+ @property + def basemask(self): + return "pyanaconda.ui.tui" + + def _list_hubs(self): + """returns the list of hubs to use""" + return [SummaryHub, ProgressHub] + + def _is_standalone(self, spoke): + """checks if the passed spoke is standalone""" + return isinstance(spoke, StandaloneSpoke) + def setup(self, data): """Construct all the objects required to implement this interface. This method must be provided by all subclasses. """ self._app = tui.App(u"Anaconda", yes_or_no_question = YesNoDialog) - self._hubs = [SummaryHub, ProgressHub] + _hubs = self._list_hubs()
# First, grab a list of all the standalone spokes. path = os.path.join(os.path.dirname(__file__), "spokes") - actionClasses = self.getActionClasses("pyanaconda.ui.tui.spokes.%s", path, self._hubs, StandaloneSpoke) - + spokes = self._collectActionClasses(self.paths["spokes"], StandaloneSpoke) + actionClasses = self._orderActionClasses(spokes, _hubs) + for klass in actionClasses: obj = klass(self._app, data, self.storage, self.payload, self.instclass)
# If we are doing a kickstart install, some standalone spokes # could already be filled out. In taht case, we do not want # to display them. - if isinstance(obj, StandaloneSpoke) and obj.completed: + if self._is_standalone(obj) and obj.completed: del(obj) continue
+ if hasattr(obj, "set_path"): + obj.set_path("spokes", self.paths["spokes"]) + self._app.schedule_screen(obj)
def run(self): diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py index 0f505f7..d669bc7 100644 --- a/pyanaconda/ui/tui/hubs/__init__.py +++ b/pyanaconda/ui/tui/hubs/__init__.py @@ -52,7 +52,7 @@ class TUIHub(TUIObject, common.Hub):
# look for spokes having category present in self.categories for c in self.categories: - spokes = collect_spokes([("pyanaconda.ui.tui.spokes.%s", os.path.join(os.path.dirname(__file__), "spokes"))], c) + spokes = collect_spokes(self.paths["spokes"], c)
# sort them according to their priority for s in sorted(spokes, key = lambda s: s.priority):