[master] Fix the way categories are handled in text mode.

Samantha N. Bueno sbueno+anaconda at redhat.com
Thu Mar 27 21:17:37 UTC 2014


This statically maintained list of spoke categories for text mode has
been bothering me for almost a year, so it's time to get rid of it.

This now more or less mirrors the GUI logic we have for handling spoke
categories. I wanted to originally make the category classes something
that could be used by both GUI and TUI to reduce duplication, but it is
not meant to be.

This also moves the collectCategoriesAndSpokes, collect_categories, and
collect_spokes functions into pyanaconda.ui.common since I could at
least consolidate that.
---
 po/POTFILES.in                                |  6 ++++
 pyanaconda/ui/common.py                       | 46 +++++++++++++++++++++++++++
 pyanaconda/ui/gui/categories/__init__.py      | 13 +-------
 pyanaconda/ui/gui/hubs/__init__.py            | 23 ++------------
 pyanaconda/ui/gui/spokes/__init__.py          | 20 +-----------
 pyanaconda/ui/tui/__init__.py                 |  4 +++
 pyanaconda/ui/tui/categories/Makefile.am      | 22 +++++++++++++
 pyanaconda/ui/tui/categories/__init__.py      | 46 +++++++++++++++++++++++++++
 pyanaconda/ui/tui/categories/customization.py | 32 +++++++++++++++++++
 pyanaconda/ui/tui/categories/localization.py  | 31 ++++++++++++++++++
 pyanaconda/ui/tui/categories/software.py      | 31 ++++++++++++++++++
 pyanaconda/ui/tui/categories/system.py        | 32 +++++++++++++++++++
 pyanaconda/ui/tui/categories/user_settings.py | 32 +++++++++++++++++++
 pyanaconda/ui/tui/hubs/__init__.py            | 18 +++++------
 pyanaconda/ui/tui/hubs/summary.py             |  2 --
 pyanaconda/ui/tui/spokes/__init__.py          | 24 ++------------
 pyanaconda/ui/tui/spokes/askvnc.py            |  2 --
 pyanaconda/ui/tui/spokes/network.py           |  3 +-
 pyanaconda/ui/tui/spokes/password.py          |  3 +-
 pyanaconda/ui/tui/spokes/shell_spoke.py       |  3 +-
 pyanaconda/ui/tui/spokes/software.py          |  3 +-
 pyanaconda/ui/tui/spokes/source.py            | 11 ++++---
 pyanaconda/ui/tui/spokes/storage.py           |  7 ++--
 pyanaconda/ui/tui/spokes/time_spoke.py        |  3 +-
 pyanaconda/ui/tui/spokes/user.py              |  3 +-
 25 files changed, 319 insertions(+), 101 deletions(-)
 create mode 100644 pyanaconda/ui/tui/categories/Makefile.am
 create mode 100644 pyanaconda/ui/tui/categories/__init__.py
 create mode 100644 pyanaconda/ui/tui/categories/customization.py
 create mode 100644 pyanaconda/ui/tui/categories/localization.py
 create mode 100644 pyanaconda/ui/tui/categories/software.py
 create mode 100644 pyanaconda/ui/tui/categories/system.py
 create mode 100644 pyanaconda/ui/tui/categories/user_settings.py

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 358534c..e48469e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -62,6 +62,12 @@ pyanaconda/ui/tui/spokes/warnings.py
 pyanaconda/ui/tui/spokes/progress.py
 pyanaconda/ui/tui/spokes/__init__.py
 pyanaconda/ui/tui/__init__.py
+pyanaconda/ui/tui/categories/__init__.py
+pyanaconda/ui/tui/categories/customization.py
+pyanaconda/ui/tui/categories/localization.py
+pyanaconda/ui/tui/categories/software.py
+pyanaconda/ui/tui/categories/system.py
+pyanaconda/ui/tui/categories/user_settings.py
 
 # Graphical interface
 pyanaconda/ui/gui/__init__.py
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py
index ddd5016..afca06a 100644
--- a/pyanaconda/ui/common.py
+++ b/pyanaconda/ui/common.py
@@ -673,3 +673,49 @@ def collect(module_pattern, path, pred):
 
     return retval
 
+def collect_spokes(mask_paths, category):
+    """Return a list of all spoke subclasses that should appear for a given
+       category. Look for them in files imported as module_path % basename(f)
+
+       :param mask_paths: list of mask, path tuples to search for classes
+       :type mask_paths: list of (mask, path)
+
+       :return: list of Spoke classes belonging to category
+       :rtype: list of Spoke classes
+
+    """
+    spokes = []
+    for mask, path in mask_paths:
+        spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category.__name__ == category))
+    
+    return spokes
+
+def collect_categories(mask_paths):
+    """Return a list of all category subclasses. Look for them in modules
+       imported as module_mask % basename(f) where f is name of all files in path.
+    """
+    categories = []
+    for mask, path in mask_paths:
+        categories.extend(collect(mask, path, lambda obj: getattr(obj, "displayOnHub", None) != None))
+
+    return categories
+
+def collectCategoriesAndSpokes(paths, klass):
+    """collects categories and spokes to be displayed on this Hub
+
+       :param paths: dictionary mapping categories, spokes, and hubs to their
+       their respective search path(s)
+       :return: dictionary mapping category class to list of spoke classes
+       :rtype: dictionary[category class] -> [ list of spoke classes ]
+    """
+    
+    ret = {}
+    
+    # Collect all the categories this hub displays, then collect all the
+    # spokes belonging to all those categories.
+    categories = sorted(filter(lambda c: c.displayOnHub == klass, collect_categories(paths["categories"])),
+                        key=lambda c: c.sortOrder)
+    for c in categories:
+        ret[c] = collect_spokes(paths["spokes"], c.__name__)
+
+    return ret
diff --git a/pyanaconda/ui/gui/categories/__init__.py b/pyanaconda/ui/gui/categories/__init__.py
index 66bd5d4..06e431a 100644
--- a/pyanaconda/ui/gui/categories/__init__.py
+++ b/pyanaconda/ui/gui/categories/__init__.py
@@ -21,9 +21,8 @@
 
 import os.path
 from pyanaconda.i18n import N_
-from pyanaconda.ui.common import collect
 
-__all__ = ["SpokeCategory", "collect_categories"]
+__all__ = ["SpokeCategory"]
 
 class SpokeCategory(object):
     """A SpokeCategory is an object used to group multiple related Spokes
@@ -45,13 +44,3 @@ class SpokeCategory(object):
     displayOnHub = None
     sortOrder = 1000
     title = N_("DEFAULT TITLE")
-
-def collect_categories(mask_paths):
-    """Return a list of all category subclasses. Look for them in modules
-       imported as module_mask % basename(f) where f is name of all files in path.
-    """
-    categories = []
-    for mask, path in mask_paths:
-        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 7dcdcdb..f7fc172 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -29,8 +29,7 @@ from pyanaconda.product import distributionText
 
 from pyanaconda.ui import common
 from pyanaconda.ui.gui import GUIObject
-from pyanaconda.ui.gui.categories import collect_categories
-from pyanaconda.ui.gui.spokes import StandaloneSpoke, collect_spokes
+from pyanaconda.ui.gui.spokes import StandaloneSpoke
 from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup
 from pyanaconda.constants import ANACONDA_ENVIRON
 
@@ -130,29 +129,11 @@ class Hub(GUIObject, common.Hub):
 
         action.exit_logger()
 
-    def _collectCategoriesAndSpokes(self):
-        """collects categories and spokes to be displayed on this Hub
-
-           :return: dictionary mapping category class to list of spoke classes
-           :rtype: dictionary[category class] -> [ list of spoke classes ]
-        """
-
-        ret = {}
-
-        # 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.paths["categories"])),
-                            key=lambda c: c.sortOrder)
-        for c in categories:
-            ret[c] = collect_spokes(self.paths["spokes"], c.__name__)
-
-        return ret
-
     def _createBox(self):
         from gi.repository import Gtk, AnacondaWidgets
         from pyanaconda.ui.gui.utils import setViewportBackground
 
-        cats_and_spokes = self._collectCategoriesAndSpokes()
+        cats_and_spokes = common.collectCategoriesAndSpokes(self.paths, self.__class__)
         categories = cats_and_spokes.keys()
 
         grid = Gtk.Grid(row_spacing=6, column_spacing=6, column_homogeneous=True,
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index f3fc74f..f9d11e8 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -20,11 +20,10 @@
 #                    Martin Sivak <msivak at redhat.com>
 
 from pyanaconda.ui import common
-from pyanaconda.ui.common import collect
 from pyanaconda.ui.gui import GUIObject
 import os.path
 
-__all__ = ["StandaloneSpoke", "NormalSpoke", "collect_spokes"]
+__all__ = ["StandaloneSpoke", "NormalSpoke"]
 
 class Spoke(GUIObject):
     def __init__(self, data):
@@ -93,20 +92,3 @@ class NormalSpoke(Spoke, common.NormalSpoke):
 
         self.window.hide()
         Gtk.main_quit()
-
-def collect_spokes(mask_paths, category):
-    """Return a list of all spoke subclasses that should appear for a given
-       category. Look for them in files imported as module_path % basename(f)
-
-       :param mask_paths: list of mask, path tuples to search for classes
-       :type mask_paths: list of (mask, path)
-
-       :return: list of Spoke classes belonging to category
-       :rtype: list of Spoke classes
-
-    """
-    spokes = []
-    for mask, path in mask_paths:
-        spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category.__name__ == category))
-
-    return spokes
diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py
index 402d625..4cf3690 100644
--- a/pyanaconda/ui/tui/__init__.py
+++ b/pyanaconda/ui/tui/__init__.py
@@ -105,6 +105,9 @@ class TextUserInterface(ui.UserInterface):
     pathlist = set([updatepath, basepath] + sitepackages)
 
     paths = ui.UserInterface.paths + {
+            "categories": [(basemask + ".categories.%s",
+                        os.path.join(path, "categories"))
+                        for path in pathlist],
             "spokes": [(basemask + ".spokes.%s",
                         os.path.join(path, "spokes"))
                         for path in pathlist],
@@ -162,6 +165,7 @@ class TextUserInterface(ui.UserInterface):
 
             if hasattr(obj, "set_path"):
                 obj.set_path("spokes", self.paths["spokes"])
+                obj.set_path("categories", self.paths["categories"])
 
             should_schedule = obj.setup(self.ENVIRONMENT)
 
diff --git a/pyanaconda/ui/tui/categories/Makefile.am b/pyanaconda/ui/tui/categories/Makefile.am
new file mode 100644
index 0000000..0ad7913
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/Makefile.am
@@ -0,0 +1,22 @@
+# Copyright (C) 2011  Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Chris Lumens <clumens at redhat.com>
+
+MAINTAINERCLEANFILES = Makefile.in
+
+pkgpyexecdir        = $(pyexecdir)/py$(PACKAGE_NAME)
+categoriesdir       = $(pkgpyexecdir)/ui/tui/categories
+categories_PYTHON   = $(srcdir)/*.py
diff --git a/pyanaconda/ui/tui/categories/__init__.py b/pyanaconda/ui/tui/categories/__init__.py
new file mode 100644
index 0000000..06e431a
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/__init__.py
@@ -0,0 +1,46 @@
+# Base classes for spoke categories.
+#
+# Copyright (C) 2011, 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#
+
+import os.path
+from pyanaconda.i18n import N_
+
+__all__ = ["SpokeCategory"]
+
+class SpokeCategory(object):
+    """A SpokeCategory is an object used to group multiple related Spokes
+       together on a hub.  It consists of a title displayed above, and then
+       a two-column grid of SpokeSelectors.  Each SpokeSelector is associated
+       with a Spoke subclass.  A SpokeCategory will only display those Spokes
+       with a matching category attribute.
+
+       Class attributes:
+
+       displayOnHub  -- The Hub subclass to display this Category on.  If
+                        None, this Category will be skipped.
+       sortOrder     -- A number indicating the order in which this Category
+                        will be displayed.  A lower number indicates display
+                        higher up in the Hub.
+       title         -- The title of this SpokeCategory, to be displayed above
+                        the grid.
+    """
+    displayOnHub = None
+    sortOrder = 1000
+    title = N_("DEFAULT TITLE")
diff --git a/pyanaconda/ui/tui/categories/customization.py b/pyanaconda/ui/tui/categories/customization.py
new file mode 100644
index 0000000..b845e39
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/customization.py
@@ -0,0 +1,32 @@
+# Localization category classes
+#
+# Copyright (C) 2011, 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    Martin Sivak <msivak at redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["CustomizationCategory"]
+
+class CustomizationCategory(SpokeCategory):
+    displayOnHub = SummaryHub
+    sortOrder = 100
+    title = N_("CUSTOMIZATION")
diff --git a/pyanaconda/ui/tui/categories/localization.py b/pyanaconda/ui/tui/categories/localization.py
new file mode 100644
index 0000000..b87ce40
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/localization.py
@@ -0,0 +1,31 @@
+# Localization category classes
+#
+# Copyright (C) 2011, 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["LocalizationCategory"]
+
+class LocalizationCategory(SpokeCategory):
+    displayOnHub = SummaryHub
+    sortOrder = 100
+    title = N_("LOCALIZATION")
diff --git a/pyanaconda/ui/tui/categories/software.py b/pyanaconda/ui/tui/categories/software.py
new file mode 100644
index 0000000..e15b7a9
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/software.py
@@ -0,0 +1,31 @@
+# Software category classes
+#
+# Copyright (C) 2011, 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["SoftwareCategory"]
+
+class SoftwareCategory(SpokeCategory):
+    displayOnHub = SummaryHub
+    sortOrder = 200
+    title = N_("SOFTWARE")
diff --git a/pyanaconda/ui/tui/categories/system.py b/pyanaconda/ui/tui/categories/system.py
new file mode 100644
index 0000000..4613c9b
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/system.py
@@ -0,0 +1,32 @@
+# Storage category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    David Lehman <dlehman at redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["SystemCategory"]
+
+class SystemCategory(SpokeCategory):
+    displayOnHub = SummaryHub
+    sortOrder = 300
+    title = N_("SYSTEM")
diff --git a/pyanaconda/ui/tui/categories/user_settings.py b/pyanaconda/ui/tui/categories/user_settings.py
new file mode 100644
index 0000000..0ef3612
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/user_settings.py
@@ -0,0 +1,32 @@
+# User settings category classes
+#
+# Copyright (C) 2011, 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    Martin Sivak <msivak at redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["UserSettingsCategory"]
+
+class UserSettingsCategory(SpokeCategory):
+    displayOnHub = SummaryHub
+    sortOrder = 200
+    title = N_("USER SETTINGS")
diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py
index e886b63..a1ee93e 100644
--- a/pyanaconda/ui/tui/hubs/__init__.py
+++ b/pyanaconda/ui/tui/hubs/__init__.py
@@ -20,7 +20,6 @@
 #
 from pyanaconda.ui.tui import simpleline as tui
 from pyanaconda.ui.tui.tuiobject import TUIObject
-from pyanaconda.ui.tui.spokes import collect_spokes
 from pyanaconda.ui import common
 
 from pyanaconda.i18n import _, C_, N_
@@ -50,17 +49,18 @@ class TUIHub(TUIObject, common.Hub):
         self._spoke_count = 0
 
     def setup(self, environment="anaconda"):
-        # look for spokes having category present in self.categories
-        for c in self.categories:
-            spokes = collect_spokes(self.paths["spokes"], c)
+        cats_and_spokes = common.collectCategoriesAndSpokes(self.paths, self.__class__)
+        categories = cats_and_spokes.keys()
 
-            # sort them according to their priority
-            for s in sorted(spokes, key = lambda s: s.title):
+        for c in sorted(categories, key=lambda c: c.title):
+            obj = c()
+
+            for spokeClass in sorted(cats_and_spokes[c], key=lambda s: s.title):
                 # Check if this spoke is to be shown in anaconda
-                if not s.should_run(environment, self.data):
+                if not spokeClass.should_run(environment, self.data):
                     continue
 
-                spoke = s(self.app, self.data, self.storage, self.payload, self.instclass)
+                spoke = spokeClass(self.app, self.data, self.storage, self.payload, self.instclass)
                 spoke.initialize()
 
                 if not spoke.showable:
@@ -73,7 +73,7 @@ class TUIHub(TUIObject, common.Hub):
 
                 self._spoke_count += 1
                 self._keys[self._spoke_count] = spoke
-                self._spokes[spoke.__class__.__name__] = spoke
+                self._spokes[spokeClass.__name__] = spoke
 
         # only schedule the hub if it has some spokes
         return self._spoke_count != 0
diff --git a/pyanaconda/ui/tui/hubs/summary.py b/pyanaconda/ui/tui/hubs/summary.py
index 936812c..3b04c90 100644
--- a/pyanaconda/ui/tui/hubs/summary.py
+++ b/pyanaconda/ui/tui/hubs/summary.py
@@ -32,8 +32,6 @@ log = logging.getLogger("anaconda")
 
 class SummaryHub(TUIHub):
     title = N_("Installation")
-    ## FIXME: this should be pulling data from somewhere, not just a static list
-    categories = ["localization", "software", "system", "user"]
 
     def setup(self, environment="anaconda"):
         should_schedule = TUIHub.setup(self, environment=environment)
diff --git a/pyanaconda/ui/tui/spokes/__init__.py b/pyanaconda/ui/tui/spokes/__init__.py
index 57984ba..51eb6d3 100644
--- a/pyanaconda/ui/tui/spokes/__init__.py
+++ b/pyanaconda/ui/tui/spokes/__init__.py
@@ -20,7 +20,7 @@
 #
 from pyanaconda.ui.tui import simpleline as tui
 from pyanaconda.ui.tui.tuiobject import TUIObject, YesNoDialog
-from pyanaconda.ui.common import Spoke, StandaloneSpoke, NormalSpoke, collect
+from pyanaconda.ui.common import Spoke, StandaloneSpoke, NormalSpoke
 from pyanaconda.users import validatePassword, cryptPassword
 import re
 from collections import namedtuple
@@ -29,8 +29,7 @@ from pyanaconda.i18n import N_, _
 from pyanaconda.constants import PASSWORD_CONFIRM_ERROR_TUI
 
 __all__ = ["TUISpoke", "EditTUISpoke", "EditTUIDialog", "EditTUISpokeEntry",
-           "StandaloneSpoke", "NormalTUISpoke", "collect_spokes",
-           "collect_categories"]
+           "StandaloneSpoke", "NormalTUISpoke"]
 
 # Inherit abstract methods from Spoke
 # pylint: disable=W0223
@@ -47,7 +46,6 @@ class TUISpoke(TUIObject, tui.Widget, Spoke):
     """
 
     title = N_("Default spoke title")
-    category = u""
 
     def __init__(self, app, data, storage, payload, instclass):
         if self.__class__ is TUISpoke:
@@ -311,21 +309,3 @@ class EditTUISpoke(NormalTUISpoke):
 
 class StandaloneTUISpoke(TUISpoke, StandaloneSpoke):
     pass
-
-def collect_spokes(mask_paths, category):
-    """Return a list of all spoke subclasses that should appear for a given
-       category.
-    """
-    spokes = []
-    for mask, path in mask_paths:
-        spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category == category))
-
-    return spokes
-
-def collect_categories(mask_paths):
-    classes = []
-    for mask, path in mask_paths:
-        classes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category != ""))
-
-    categories = set(c.category for c in classes)
-    return categories
diff --git a/pyanaconda/ui/tui/spokes/askvnc.py b/pyanaconda/ui/tui/spokes/askvnc.py
index 19c0d50..dc9273e 100644
--- a/pyanaconda/ui/tui/spokes/askvnc.py
+++ b/pyanaconda/ui/tui/spokes/askvnc.py
@@ -36,7 +36,6 @@ def exception_msg_handler_and_exit(event, data):
 
 class AskVNCSpoke(NormalTUISpoke):
     title = N_("VNC")
-    category = "vnc"
 
     # This spoke is kinda standalone, not meant to be used with a hub
     # We pass in some fake data just to make our parents happy
@@ -109,7 +108,6 @@ class AskVNCSpoke(NormalTUISpoke):
 
 class VNCPassSpoke(NormalTUISpoke):
     title = N_("VNC Password")
-    category = "vnc"
 
     def __init__(self, app, data, storage, payload, instclass, message=None):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py
index 8ede756..fe74446 100644
--- a/pyanaconda/ui/tui/spokes/network.py
+++ b/pyanaconda/ui/tui/spokes/network.py
@@ -22,6 +22,7 @@
 
 
 from pyanaconda.flags import can_touch_runtime_system
+from pyanaconda.ui.tui.categories.system import SystemCategory
 from pyanaconda.ui.tui.spokes import EditTUISpoke, OneShotEditTUIDialog
 from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
@@ -40,7 +41,7 @@ __all__ = ["NetworkSpoke"]
 class NetworkSpoke(EditTUISpoke):
     """ Spoke used to configure network settings. """
     title = N_("Network configuration")
-    category = "system"
+    category = SystemCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         EditTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/password.py b/pyanaconda/ui/tui/spokes/password.py
index 870fff4..bb98418 100644
--- a/pyanaconda/ui/tui/spokes/password.py
+++ b/pyanaconda/ui/tui/spokes/password.py
@@ -21,6 +21,7 @@
 #                    Chris Lumens <clumens at redhat.com>
 #
 
+from pyanaconda.ui.tui.categories.user_settings import UserSettingsCategory
 from pyanaconda.ui.tui.spokes import EditTUIDialog, EditTUISpokeEntry
 from pyanaconda.ui.common import FirstbootSpokeMixIn
 from pyanaconda.ui.tui.simpleline import TextWidget
@@ -29,7 +30,7 @@ from pyanaconda.i18n import N_, _
 
 class PasswordSpoke(FirstbootSpokeMixIn, EditTUIDialog):
     title = N_("Root password")
-    category = "user"
+    category = UserSettingsCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         EditTUIDialog.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/shell_spoke.py b/pyanaconda/ui/tui/spokes/shell_spoke.py
index 4915f98..b355387 100644
--- a/pyanaconda/ui/tui/spokes/shell_spoke.py
+++ b/pyanaconda/ui/tui/spokes/shell_spoke.py
@@ -20,6 +20,7 @@
 
 """Text mode shell spoke"""
 
+from pyanaconda.ui.tui.categories.system import SystemCategory
 from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline.widgets import TextWidget
 from pyanaconda.i18n import N_, _
@@ -31,7 +32,7 @@ import subprocess
 
 class ShellSpoke(NormalTUISpoke):
     title = N_("Shell")
-    category = "system"
+    category = SystemCategory
 
     @classmethod
     def should_run(cls, environment, data):
diff --git a/pyanaconda/ui/tui/spokes/software.py b/pyanaconda/ui/tui/spokes/software.py
index 2b414d6..e63242f 100644
--- a/pyanaconda/ui/tui/spokes/software.py
+++ b/pyanaconda/ui/tui/spokes/software.py
@@ -20,6 +20,7 @@
 #
 
 from pyanaconda.flags import flags
+from pyanaconda.ui.tui.categories.software import SoftwareCategory
 from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget, CheckboxWidget
 from pyanaconda.threads import threadMgr, AnacondaThread
@@ -36,7 +37,7 @@ __all__ = ["SoftwareSpoke"]
 class SoftwareSpoke(NormalTUISpoke):
     """ Spoke used to read new value of text to represent source repo. """
     title = N_("Software selection")
-    category = "software"
+    category = SoftwareCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/source.py b/pyanaconda/ui/tui/spokes/source.py
index 45381df..0aaf4c2 100644
--- a/pyanaconda/ui/tui/spokes/source.py
+++ b/pyanaconda/ui/tui/spokes/source.py
@@ -20,6 +20,7 @@
 #
 
 from pyanaconda.flags import flags
+from pyanaconda.ui.tui.categories.software import SoftwareCategory
 from pyanaconda.ui.tui.spokes import EditTUISpoke, NormalTUISpoke
 from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
@@ -51,7 +52,7 @@ __all__ = ["SourceSpoke"]
 class SourceSpoke(EditTUISpoke, SourceSwitchHandler):
     """ Spoke used to customize the install source repo. """
     title = N_("Installation source")
-    category = "software"
+    category = SoftwareCategory
 
     _protocols = (N_("Closest mirror"), "http://", "https://", "ftp://", "nfs")
 
@@ -254,7 +255,7 @@ class SourceSpoke(EditTUISpoke, SourceSwitchHandler):
 class SpecifyRepoSpoke(EditTUISpoke, SourceSwitchHandler):
     """ Specify the repo URL here if closest mirror not selected. """
     title = N_("Specify Repo Options")
-    category = "software"
+    category = SoftwareCategory
 
     edit_fields = [
         Entry(N_("Repo URL"), "url", re.compile(".*$"), True)
@@ -292,7 +293,7 @@ class SpecifyRepoSpoke(EditTUISpoke, SourceSwitchHandler):
 class SpecifyNFSRepoSpoke(EditTUISpoke, SourceSwitchHandler):
     """ Specify server and mount opts here if NFS selected. """
     title = N_("Specify Repo Options")
-    category = "software"
+    category = SoftwareCategory
 
     edit_fields = [
         Entry(N_("NFS <server>:/<path>"), "server", re.compile(".*$"), True),
@@ -339,7 +340,7 @@ class SpecifyNFSRepoSpoke(EditTUISpoke, SourceSwitchHandler):
 class SelectDeviceSpoke(NormalTUISpoke):
     """ Select device containing the install source ISO file. """
     title = N_("Select device containing the ISO file")
-    category = "source"
+    category = SoftwareCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -426,7 +427,7 @@ class SelectDeviceSpoke(NormalTUISpoke):
 class SelectISOSpoke(NormalTUISpoke, SourceSwitchHandler):
     """ Select an ISO to use as install source. """
     title = N_("Select an ISO to use as install source")
-    category = "source"
+    category = SoftwareCategory
 
     def __init__(self, app, data, storage, payload, instclass, device):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index cb43d98..9e47f81 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -23,6 +23,7 @@
 #
 
 from pyanaconda.ui.lib.disks import getDisks, applyDiskSelection
+from pyanaconda.ui.tui.categories.system import SystemCategory
 from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget
 from pyanaconda.ui.tui.tuiobject import YesNoDialog
@@ -66,7 +67,7 @@ class StorageSpoke(NormalTUISpoke):
     as disk selection, partitioning, and fs type.
     """
     title = N_("Installation Destination")
-    category = "system"
+    category = SystemCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -433,7 +434,7 @@ class StorageSpoke(NormalTUISpoke):
 class AutoPartSpoke(NormalTUISpoke):
     """ Autopartitioning options are presented here. """
     title = N_("Autopartitioning Options")
-    category = "system"
+    category = SystemCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -499,7 +500,7 @@ class AutoPartSpoke(NormalTUISpoke):
 class PartitionSchemeSpoke(NormalTUISpoke):
     """ Spoke to select what partitioning scheme to use on disk(s). """
     title = N_("Partition Scheme Options")
-    category = "system"
+    category = SystemCategory
 
     # set default FS to LVM, for consistency with graphical behavior
     _selection = 1
diff --git a/pyanaconda/ui/tui/spokes/time_spoke.py b/pyanaconda/ui/tui/spokes/time_spoke.py
index 75c1244..ec0f8f4 100644
--- a/pyanaconda/ui/tui/spokes/time_spoke.py
+++ b/pyanaconda/ui/tui/spokes/time_spoke.py
@@ -19,6 +19,7 @@
 # Red Hat Author(s): Martin Sivak <msivak at redhat.com>
 #
 
+from pyanaconda.ui.tui.categories.localization import LocalizationCategory
 from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
 from pyanaconda.ui.common import FirstbootSpokeMixIn
@@ -28,7 +29,7 @@ from pyanaconda.constants_text import INPUT_PROCESSED
 
 class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
     title = N_("Timezone settings")
-    category = "localization"
+    category = LocalizationCategory
 
     def __init__(self, app, data, storage, payload, instclass):
         NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/user.py b/pyanaconda/ui/tui/spokes/user.py
index f069eec..4871ab7 100644
--- a/pyanaconda/ui/tui/spokes/user.py
+++ b/pyanaconda/ui/tui/spokes/user.py
@@ -20,6 +20,7 @@
 #                    Chris Lumens <clumens at redhat.com>
 #
 
+from pyanaconda.ui.tui.categories.user_settings import UserSettingsCategory
 from pyanaconda.ui.tui.spokes import EditTUISpoke
 from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
 from pyanaconda.ui.common import FirstbootSpokeMixIn
@@ -34,7 +35,7 @@ __all__ = ["UserSpoke"]
 
 class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
     title = N_("User creation")
-    category = "user"
+    category = UserSettingsCategory
 
     edit_fields = [
         Entry("Create user", "_create", EditTUISpoke.CHECK, True),
-- 
1.8.3.1



More information about the anaconda-patches mailing list