#!/usr/bin/python import sys from PyQt4 import QtGui, uic import sys sys.path.insert(0,'/usr/share/yum-cli') import yum import yum.comps from utils import YumUtilBase import cPickle as pickle class GenericItem(object): def __init__(self, ui_name): self.ui_name = ui_name self.children = [] def append(self, child): self.children.append(child) def isEmpty(self): return False class GenericSearchItem(GenericItem): def __init__(self, pattern, result): GenericItem.__init__(self, pattern) self.pattern = pattern self.result = result def __str__(self): return "<%s %s>(%s)" % (self.__class__.__name__, self.type, self.pattern) def isEmpty(self): return not bool(self.result) class SearchItem(GenericSearchItem): pass class RepositoryItem(GenericSearchItem): pass class PackageTagItem(GenericSearchItem): pass class MenuEntryItem(GenericItem): pass class MenuTagItem(PackageTagItem): pass class CompsCategoryItem(GenericItem): pass class CompsGroupItem(GenericSearchItem): pass class PackagesUI: def __init__(self): self.yum = YumUtilBase("", "", "") self.yum.setCacheDir() #self.yum.conf.cache = 1 self.searchItems = GenericItem("Invisible Toplevel") self.searchItem = GenericItem("Searches") self.searchItems.append(self.searchItem) self.activeSearchItems = [] self.mainwindow = m = uic.loadUi("packages.ui") # searchBox self.searchBox = m.findChild(QtGui.QLineEdit, "searchBox") self.searchBox.returnPressed.connect(self.search) # tagList self.tagList = m.findChild(QtGui.QTreeWidget, "tagList") self.tagList.setColumnCount(3) self.tagList.itemActivated.connect(self.searchActivated) self.tagList.currentItemChanged.connect(self.currentSearchItemChanged) # searchItems self.searchItemLayout = m.findChild(QtGui.QHBoxLayout, "searchItemLayout") m.findChild(QtGui.QPushButton, "pushButton").hide() m.findChild(QtGui.QPushButton, "pushButton_2").hide() self.searchItemLayout.addStretch(1) # resultList self.resultList = m.findChild(QtGui.QListWidget, "resultList") self.resultList.currentItemChanged.connect(self.packageSelected) self.packageDescription = m.findChild(QtGui.QTextBrowser, "packageDescription") self.packageProperties = m.findChild(QtGui.QTextBrowser, "packageProperties") pkglists = self.yum.doPackageLists("available", showdups=0) self.allPkgs = set(pkglists.available) #self.allPkgs.update(pkglists.installed) self.result = self.allPkgs.copy() self.allPackagesByName = dict(((p.name, p) for p in self.allPkgs)) self._getRepositories() self._getPackageTags() self._getMenuTags() self._getMenuEntries() self._getCompsGroups() self.fillTags() self.mainwindow.show() def _getPackageTags(self): item = GenericItem("PackageDB Tags") self.searchItems.append(item) self.tag2pkgs = {} for name, tags in self.yum.pkgtags.search_tags('').iteritems(): if name not in self.allPackagesByName: continue for tag in tags: self.tag2pkgs.setdefault(tag, set()).add(self.allPackagesByName[name]) for tag, pkgs in self.tag2pkgs.iteritems(): if len(pkgs) <= 1: # or tag.startswith("X-"): continue item.append(PackageTagItem(tag, pkgs)) def _getMenuTags(self): item = GenericItem("Menu Tags") self.searchItems.append(item) self.menutag2packages = pickle.load(open("categories.pickle")) for name, pkgnames in self.menutag2packages.iteritems(): pkgs = self._names2pkgs(pkgnames) if len(pkgs) <= 1: continue item.append(MenuTagItem(name, pkgs)) def _buildMenuEntries(self, menu, parent): for name, submenus in menu.iteritems(): if name in self.menu2packages: pkgs = self._names2pkgs(self.menu2packages[name]) item = MenuTagItem(name, pkgs) else: item = GenericItem(name) parent.append(item) self._buildMenuEntries(submenus, item) def _getMenuEntries(self): self.menu2packages = pickle.load(open("menu2packages.pickle")) menutree = pickle.load(open("menustructure.pickle")) self._buildMenuEntries(menutree, self.searchItems) def _getCompsGroups(self): item = GenericItem("Comps Groups") self.searchItems.append(item) for cat in self.yum.comps.get_categories(): categoryItem = GenericItem(cat.ui_name) item.append(categoryItem) for g in cat.groups: if self.yum.comps.has_group(g): group = self.yum.comps.return_group(g) if not group.user_visible: continue pkgs = self._names2pkgs(group.packages) if pkgs: categoryItem.append(SearchItem(group.ui_name, pkgs)) def _getRepositories(self): item = GenericItem("Repositories") self.searchItems.append(item) repos = {} for pkg in self.allPkgs: repos.setdefault(pkg.repo,set()).add(pkg) for repo, pkgs in repos.iteritems(): item.append(RepositoryItem(repo.name, pkgs)) ######################################################### def _names2pkgs(self, pkgnames): pkgs = set((self.allPackagesByName.get(name) for name in pkgnames)) pkgs.discard(None) return pkgs def fillResults(self, pkgs): self.resultList.clear() # XXX cheat for now # use pages instead if len(pkgs) > 1000: l = [(p.name, p) for p in pkgs] l.sort() l = l[:1000] pkgs = set((p for n, p in l)) for pkg in pkgs: item = QtGui.QListWidgetItem("%s\n - %s" % (pkg.ui_nevra, pkg.summary), self.resultList, 0) item.setData(1000, pkg) self.resultList.sortItems(0) self.resultList.setCurrentRow(0) def _addActiveSearchItem(self, searchItem): if searchItem in self.activeSearchItems: return b = QtGui.QPushButton(searchItem.pattern) b.clicked.connect(self.deleteSearchItem) self.searchItemLayout.insertWidget(0, b) self.activeSearchItems.append(searchItem) self.refreshSearchResults() def search(self): pattern = str(self.searchBox.text()) for item in self.searchItem.children: if item.pattern == pattern: self._addActiveSearchItem(item) return # XXX allow different searches via combobox searchlist = ['name', 'summary', 'description', 'url'] result = set(self.yum.searchPackages(searchlist, [pattern], False)) l = len(result) item = SearchItem(pattern, result) self.searchItem.append(item) treeitem = QtGui.QTreeWidgetItem( self.searchItem.treeItem, [pattern, "%i/%i" % (l, l), "%8i" % l], 0) item.treeItem = treeitem treeitem.setData(3, 0, item) self._addActiveSearchItem(item) def searchActivated(self, treeitem, column): searchItem = treeitem.data(3, 0).toPyObject() if searchItem and isinstance(searchItem, GenericSearchItem): self._addActiveSearchItem(searchItem) def deleteSearchItem(self, w): b = self.mainwindow.sender() b.hide() pattern = b.text() self.searchItemLayout.removeWidget(b) b.destroy(True, True) # XXX enough? for nr, s in enumerate(self.activeSearchItems): if s.pattern == pattern: del self.activeSearchItems[nr] self.refreshSearchResults() def refreshSearchResults(self): result = self.allPkgs.copy() for s in self.activeSearchItems: result &= s.result self.result = result self.fillResults(result) self.updateSearchItems() def currentSearchItemChanged(self, current, previous): searchItem = current.data(3, 0).toPyObject() if searchItem and isinstance(searchItem, GenericSearchItem): self.fillResults(searchItem.result & self.result) else: self.fillResults(self.result) def fillTags(self): for child in self.searchItems.children: self._addSearchItem(child, self.tagList) self.tagList.sortItems(2, 1) def _addSearchItem(self, item, parent): if item.isEmpty(): return if isinstance(item, GenericSearchItem): hits = len(item.result) treeitem = QtGui.QTreeWidgetItem( parent, [item.ui_name, "%i/%i" % (hits, hits), "%8i" % hits], 0) else: treeitem = QtGui.QTreeWidgetItem(parent, [item.ui_name], 0) treeitem.setExpanded(True) item.treeItem = treeitem treeitem.setData(3, 0, item) for child in item.children: self._addSearchItem(child, treeitem) def updateSearchItems(self): self._updateSearchItem(self.searchItems) self.tagList.sortItems(2, 1) def _updateSearchItem(self, item): if isinstance(item, GenericSearchItem) and hasattr(item, "treeItem"): if self.result: r = len(item.result & self.result) else: r = len(item.result) item.treeItem.setData(1, 0, "%i/%i" % (r, len(item.result))) item.treeItem.setData(2, 0, "%8i" % r) item.treeItem.setHidden(r == 0) for child in item.children: self._updateSearchItem(child) def packageSelected(self, current, previous): if not current: return pkg = current.data(1000).toPyObject() if pkg: self.packageDescription.setText(pkg.description) app = QtGui.QApplication(sys.argv) main = PackagesUI() sys.exit(app.exec_())