def __init__(self, db, cache, icons, icon_size=48, global_icon_cache=False): GObject.GObject.__init__(self) self.db = db self.cache = cache # get all categories cat_parser = CategoriesParser(db) self.all_categories = cat_parser.parse_applications_menu( softwarecenter.paths.APP_INSTALL_PATH) # reviews stats loader self.review_loader = get_review_loader(cache, db) # icon jazz self.icons = icons self.icon_size = icon_size self._missing_icon = None # delay this until actually needed if global_icon_cache: self.icon_cache = _app_icon_cache else: self.icon_cache = {}
def __init__(self, datadir, desktopdir, cache, db, icons, apps_filter=None, # FIXME: kill this, its not needed anymore? apps_limit=0): """ init the widget, takes datadir - the base directory of the app-store data desktopdir - the dir where the applications.menu file can be found db - a Database object icons - a Gtk.IconTheme apps_filter - ? apps_limit - the maximum amount of items to display to query for """ self.cache = cache self.db = db self.icons = icons self.properties_helper = AppPropertiesHelper( self.db, self.cache, self.icons) self.section = None Viewport.__init__(self) CategoriesParser.__init__(self, db) self.set_name("category-view") # setup base widgets # we have our own viewport so we know when the viewport grows/shrinks # setup widgets self.vbox = Gtk.VBox() self.add(self.vbox) # atk stuff atk_desc = self.get_accessible() atk_desc.set_name(_("Departments")) # appstore stuff self.categories = [] self.header = "" #~ self.apps_filter = apps_filter self.apps_limit = apps_limit # for comparing on refreshes self._supported_only = False # more stuff self._poster_sigs = [] self._allocation = None self._cache_art_assets() #~ assets = self._cache_art_assets() #~ self.vbox.connect("draw", self.on_draw, assets) self._prev_alloc = None self.connect("size-allocate", self.on_size_allocate) return
def __init__(self, db, cache, icons, icon_size=48, global_icon_cache=False): GObject.GObject.__init__(self) self.db = db self.cache = cache # get all categories cat_parser = CategoriesParser(db) self.all_categories = cat_parser.parse_applications_menu( softwarecenter.paths.APP_INSTALL_PATH) # reviews stats loader self.review_loader = get_review_loader(cache, db) # icon jazz self.icons = icons self.icon_size = icon_size # cache the 'missing icon' used in the treeview for apps without an # icon self._missing_icon = icons.load_icon(Icons.MISSING_APP, icon_size, 0) if global_icon_cache: self.icon_cache = _app_icon_cache else: self.icon_cache = {}
def get_test_categories(db): import softwarecenter.paths from softwarecenter.db.categories import CategoriesParser parser = CategoriesParser(db) cats = parser.parse_applications_menu( softwarecenter.paths.APP_INSTALL_PATH) return cats
def __init__(self, cache, db, icons, apps_filter, apps_limit=0): CategoriesView.__init__(self, cache, db, icons, apps_filter, apps_limit=0) self.top_rated = None self.exhibit_banner = None # sections self.departments = None self.appcount = None # get categories self.categories_parser = CategoriesParser(db) self.categories = self.categories_parser.parse_applications_menu() # build before connecting the signals to avoid race self.build() # ensure that on db-reopen we refresh the whats-new titles self.db.connect("reopen", self._on_db_reopen) # ensure that updates to the stats are reflected in the UI self.reviews_loader = get_review_loader(self.cache) self.reviews_loader.connect( "refresh-review-stats-finished", self._on_refresh_review_stats)
def __init__(self, cache, db, distro, icons, datadir): # parent SoftwarePane.__init__(self, cache, db, distro, icons, datadir, show_ratings=False) CategoriesParser.__init__(self, db) self.current_appview_selection = None self.icons = icons self.loaded = False self.pane_name = _("Installed Software") self.installed_apps = 0 # None is local self.current_hostid = None self.current_hostname = None self.oneconf_additional_pkg = set() self.oneconf_missing_pkg = set() # switches to terminate build in progress self._build_in_progress = False self._halt_build = False self.nonapps_visible = NonAppVisibility.NEVER_VISIBLE self.visible_docids = None self.visible_cats = {} self.installed_spinner_notebook = None
def __init__( self, datadir, desktopdir, cache, db, icons, apps_filter=None, # FIXME: kill this, its not needed anymore? apps_limit=0): """ init the widget, takes datadir - the base directory of the app-store data desktopdir - the dir where the applications.menu file can be found db - a Database object icons - a Gtk.IconTheme apps_filter - ? apps_limit - the maximum amount of items to display to query for """ self.cache = cache self.db = db self.icons = icons self.properties_helper = AppPropertiesHelper(self.db, self.cache, self.icons) self.section = None Viewport.__init__(self) CategoriesParser.__init__(self, db) self.set_name("category-view") # setup base widgets # we have our own viewport so we know when the viewport grows/shrinks # setup widgets self.vbox = Gtk.VBox() self.add(self.vbox) # atk stuff atk_desc = self.get_accessible() atk_desc.set_name(_("Departments")) # appstore stuff self.categories = [] self.header = "" #~ self.apps_filter = apps_filter self.apps_limit = apps_limit # for comparing on refreshes self._supported_only = False # more stuff self._poster_sigs = [] self._allocation = None self._cache_art_assets() #~ assets = self._cache_art_assets() #~ self.vbox.connect("draw", self.on_draw, assets) self._prev_alloc = None self.connect("size-allocate", self.on_size_allocate) return
def _get_query_for_category(self, category): cat_parser = CategoriesParser(self.db) categories = cat_parser.parse_applications_menu(APP_INSTALL_PATH) for c in categories: if category == c.untranslated_name: query = c.query return query return False
def test_category_dynamic_categories(self, mock_get_region_cached): mock_get_region_cached.return_value = { "countrycode": "us", } parser = CategoriesParser(self.db) cats = parser.parse_applications_menu("./data/") cat = get_category_by_name(cats, 'Dynamic') self.assertEqual("%s" % cat.query, "Xapian::Query((<alldocuments> AND XTregion::us))")
def test_category_dynamic_categories(self, mock_get_region_cached): mock_get_region_cached.return_value = { "countrycode" : "us", } parser = CategoriesParser(self.db) cats = parser.parse_applications_menu(DATA_DIR, use_cache=False) cat = get_category_by_name(cats, 'Dynamic') self.assertEqual( "%s" % cat.query, "Xapian::Query((<alldocuments> AND XTregion::us))")
def setUp(self): cache = get_pkg_info() cache.open() xapian_base_path = XAPIAN_BASE_PATH pathname = os.path.join(xapian_base_path, "xapian") self.db = StoreDatabase(pathname, cache) self.db.open() self.catview = CategoriesParser(self.db) self.catview.db = self.db self.cats = self.catview.parse_applications_menu('/usr/share/app-install')
def test_recommends_in_category_category(self, AgentMockCls): # ensure we use the same instance in test and code parser = CategoriesParser(self.db) cats = parser.parse_applications_menu("./data") # "2" is a multimedia query # see ./test/data/desktop/software-center.menu recommends_cat = RecommendedForYouCategory(cats[2]) # ensure we get a query when the callback is called recommends_cat._recommend_me_result( None, make_recommender_agent_recommend_me_dict()) recommendations_in_cat = recommends_cat.get_documents(self.db) print recommendations_in_cat self.assertNotEqual(recommendations_in_cat, [])
def __init__(self, parent=None): super(CategoriesModel, self).__init__() self._categories = [] roles = dict(enumerate(CategoriesModel.COLUMNS)) self.setRoleNames(roles) pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") # FIXME: move this into app cache = get_pkg_info() db = StoreDatabase(pathname, cache) db.open() # /FIXME self.catparser = CategoriesParser(db) self._categories = self.catparser.parse_applications_menu()
def test_recommends_in_category_category(self, AgentMockCls): # ensure we use the same instance in test and code parser = CategoriesParser(self.db) cats = parser.parse_applications_menu(DATA_DIR) # "2" is a multimedia query # see ./test/data/desktop/software-center.menu recommends_cat = RecommendedForYouCategory(self.db, subcategory=cats[2]) # ensure we get a query when the callback is called recommends_cat._recommend_me_result( None, make_recommender_agent_recommend_me_dict()) recommendations_in_cat = recommends_cat.get_documents(self.db) self.assertNotEqual(recommendations_in_cat, [])
def __init__(self, parent=None): super(PkgListModel, self).__init__() self._docs = [] roles = dict(enumerate(PkgListModel.COLUMNS)) self.setRoleNames(roles) self._query = "" self._category = "" pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") self.cache = get_pkg_info() self.db = StoreDatabase(pathname, self.cache) self.db.open(use_axi=False) self.backend = get_install_backend() self.backend.connect("transaction-progress-changed", self._on_backend_transaction_progress_changed) self.reviews = get_review_loader(self.cache) # FIXME: get this from a parent self._catparser = CategoriesParser(self.db) self._categories = self._catparser.parse_applications_menu( '/usr/share/app-install')
def __init__(self, parent=None): super(PkgListModel, self).__init__() self._docs = [] roles = dict(enumerate(PkgListModel.COLUMNS)) self.setRoleNames(roles) self._query = "" self._category = "" pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") self.cache = get_pkg_info() self.db = StoreDatabase(pathname, self.cache) self.db.open(use_axi=False) self.backend = get_install_backend() self.backend.connect("transaction-progress-changed", self._on_backend_transaction_progress_changed) self.reviews = get_review_loader(self.cache) # FIXME: get this from a parent self._catparser = CategoriesParser(self.db) self._categories = self._catparser.parse_applications_menu("/usr/share/app-install")
class CategoriesModel(QAbstractListModel): # should match the softwarecenter.backend.reviews.Review attributes COLUMNS = ( '_name', '_iconname', ) def __init__(self, parent=None): super(CategoriesModel, self).__init__() self._categories = [] roles = dict(enumerate(CategoriesModel.COLUMNS)) self.setRoleNames(roles) pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") # FIXME: move this into app cache = get_pkg_info() db = StoreDatabase(pathname, cache) db.open() # /FIXME self.catparser = CategoriesParser(db) self._categories = self.catparser.parse_applications_menu( softwarecenter.paths.APP_INSTALL_PATH) # QAbstractListModel code def rowCount(self, parent=QModelIndex()): return len(self._categories) def data(self, index, role): if not index.isValid(): return None cat = self._categories[index.row()] role = self.COLUMNS[role] if role == "_name": return unicode(cat.name, "utf8", "ignore") elif role == "_iconname": # funny, but it appears like Qt does not have something # to lookup the icon path in QIcon icons = Gtk.IconTheme.get_default() info = icons.lookup_icon(cat.iconname, 48, 0) if info: return info.get_filename() return ""
class CategoriesModel(QAbstractListModel): # should match the softwarecenter.backend.reviews.Review attributes COLUMNS = ('_name', '_iconname', ) def __init__(self, parent=None): super(CategoriesModel, self).__init__() self._categories = [] roles = dict(enumerate(CategoriesModel.COLUMNS)) self.setRoleNames(roles) pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") # FIXME: move this into app cache = get_pkg_info() db = StoreDatabase(pathname, cache) db.open() # /FIXME self.catparser = CategoriesParser(db) self._categories = self.catparser.parse_applications_menu( softwarecenter.paths.APP_INSTALL_PATH) # QAbstractListModel code def rowCount(self, parent=QModelIndex()): return len(self._categories) def data(self, index, role): if not index.isValid(): return None cat = self._categories[index.row()] role = self.COLUMNS[role] if role == "_name": return unicode(cat.name, "utf8", "ignore") elif role == "_iconname": # funny, but it appears like Qt does not have something # to lookup the icon path in QIcon icons = Gtk.IconTheme.get_default() info = icons.lookup_icon(cat.iconname, 48, 0) if info: return info.get_filename() return ""
class TestCatParsing(unittest.TestCase): """ tests the "where is it in the menu" code """ def setUp(self): cache = get_pkg_info() cache.open() xapian_base_path = XAPIAN_BASE_PATH pathname = os.path.join(xapian_base_path, "xapian") self.db = StoreDatabase(pathname, cache) self.db.open() self.catview = CategoriesParser(self.db) self.catview.db = self.db self.cats = self.catview.parse_applications_menu('/usr/share/app-install') def test_get_cat_by_name(self): cat = get_category_by_name(self.cats, 'Games') self.assertEqual(cat.untranslated_name, 'Games') cat = get_category_by_name(self.cats, 'Featured') self.assertEqual(cat.untranslated_name, 'Featured') def test_cat_has_flags(self): cat = get_category_by_name(self.cats, 'Featured') self.assertEqual(cat.flags[0], 'carousel-only')
def setUpClass(cls): cls.db = get_test_db() cls.parser = CategoriesParser(cls.db) cls.cats = cls.parser.parse_applications_menu("./data/")
class LobbyView(CategoriesView): def __init__(self, cache, db, icons, apps_filter, apps_limit=0): CategoriesView.__init__(self, cache, db, icons, apps_filter, apps_limit=0) self.top_rated = None self.exhibit_banner = None # sections self.departments = None self.appcount = None # get categories self.categories_parser = CategoriesParser(db) self.categories = self.categories_parser.parse_applications_menu() # build before connecting the signals to avoid race self.build() # ensure that on db-reopen we refresh the whats-new titles self.db.connect("reopen", self._on_db_reopen) # ensure that updates to the stats are reflected in the UI self.reviews_loader = get_review_loader(self.cache) self.reviews_loader.connect( "refresh-review-stats-finished", self._on_refresh_review_stats) def _on_db_reopen(self, db): self._update_whats_new_content() def _on_refresh_review_stats(self, reviews_loader, review_stats): self._update_top_rated_content() def _build_homepage_view(self): # these methods add sections to the page # changing order of methods changes order that they appear in the page self._append_banner_ads() self.top_hbox = Gtk.HBox(spacing=StockEms.SMALL) top_hbox_alignment = Gtk.Alignment() top_hbox_alignment.set_padding(0, 0, StockEms.MEDIUM - 2, StockEms.MEDIUM - 2) top_hbox_alignment.add(self.top_hbox) self.vbox.pack_start(top_hbox_alignment, False, False, 0) self._append_departments() self.right_column = Gtk.Box.new(Gtk.Orientation.VERTICAL, self.SPACING) self.top_hbox.pack_start(self.right_column, True, True, 0) self.bottom_hbox = Gtk.HBox(spacing=StockEms.SMALL) bottom_hbox_alignment = Gtk.Alignment() bottom_hbox_alignment.set_padding( StockEms.SMALL, 0, StockEms.MEDIUM - 2, StockEms.MEDIUM - 2) bottom_hbox_alignment.add(self.bottom_hbox) self.vbox.pack_start(bottom_hbox_alignment, False, False, 0) self._append_whats_new() self._append_top_rated() self._append_recommended_for_you() self._append_appcount() def _on_show_exhibits(self, exhibit_banner, exhibit): pkgs = exhibit.package_names.split(",") url = exhibit.click_url if url: webbrowser.open_new_tab(url) elif len(pkgs) == 1: app = Application("", pkgs[0]) self.emit("application-activated", app) else: query = self.db.get_query_for_pkgnames(pkgs) title = exhibit.title_translated untranslated_name = exhibit.package_names # create a temp query cat = Category(untranslated_name, title, None, query, flags=['nonapps-visible']) self.emit("category-selected", cat) def _filter_and_set_exhibits(self, sca_client, exhibit_list): result = [] # filter out those exhibits that are not available in this run for exhibit in exhibit_list: if not exhibit.package_names: result.append(exhibit) else: available = all(self.db.is_pkgname_known(p) for p in exhibit.package_names.split(',')) if available: result.append(exhibit) else: LOG.warn("skipping exhibit for: '%r' not available" % ( exhibit.package_names)) # its ok if result is empty, since set_exhibits() will ignore # empty lists self.exhibit_banner.set_exhibits(result) def _append_banner_ads(self): self.exhibit_banner = ExhibitBanner() self.exhibit_banner.set_exhibits([FeaturedExhibit()]) self.exhibit_banner.connect( "show-exhibits-clicked", self._on_show_exhibits) # query using the agent scagent = SoftwareCenterAgent() scagent.connect("exhibits", self._filter_and_set_exhibits) scagent.query_exhibits() a = Gtk.Alignment() a.set_padding(0, StockEms.SMALL, 0, 0) a.add(self.exhibit_banner) self.vbox.pack_start(a, False, False, 0) def _append_departments(self): # set the departments section to use the label markup we have just # defined cat_vbox = FramedBox(Gtk.Orientation.VERTICAL) self.top_hbox.pack_start(cat_vbox, False, False, 0) # sort Category.name's alphabetically sorted_cats = categories_sorted_by_name(self.categories) mrkup = "<small>%s</small>" for cat in sorted_cats: if 'carousel-only' in cat.flags: continue category_name = mrkup % GLib.markup_escape_text(cat.name) label = LabelTile(category_name, None) label.label.set_margin_left(StockEms.SMALL) label.label.set_margin_right(StockEms.SMALL) label.label.set_alignment(0.0, 0.5) label.label.set_use_markup(True) label.connect('clicked', self.on_category_clicked, cat) cat_vbox.pack_start(label, False, False, 0) return # FIXME: _update_{top_rated,whats_new,recommended_for_you}_content() # duplicates a lot of code def _update_top_rated_content(self): # remove any existing children from the grid widget self.top_rated.remove_all() # get top_rated category and docs top_rated_cat = get_category_by_name( self.categories, u"Top Rated") # untranslated name if top_rated_cat: docs = top_rated_cat.get_documents(self.db) self.top_rated.add_tiles(self.properties_helper, docs, TOP_RATED_CAROUSEL_LIMIT) self.top_rated.show_all() return top_rated_cat def _append_top_rated(self): self.top_rated = TileGrid() self.top_rated.connect("application-activated", self.on_application_activated) #~ self.top_rated.row_spacing = StockEms.SMALL self.top_rated_frame = FramedHeaderBox() self.top_rated_frame.set_header_label(_("Top Rated")) self.top_rated_frame.add(self.top_rated) self.bottom_hbox.pack_start(self.top_rated_frame, True, True, 0) top_rated_cat = self._update_top_rated_content() # only display the 'More' LinkButton if we have top_rated content if top_rated_cat is not None: self.top_rated_frame.header_implements_more_button() self.top_rated_frame.more.connect('clicked', self.on_category_clicked, top_rated_cat) def _update_whats_new_content(self): # remove any existing children from the grid widget self.whats_new.remove_all() # get top_rated category and docs whats_new_cat = get_category_by_name( self.categories, u"What\u2019s New") # untranslated name if whats_new_cat: docs = whats_new_cat.get_documents(self.db) self.whats_new.add_tiles(self.properties_helper, docs, WHATS_NEW_CAROUSEL_LIMIT) self.whats_new.show_all() return whats_new_cat def _append_whats_new(self): self.whats_new = TileGrid() self.whats_new.connect("application-activated", self.on_application_activated) self.whats_new_frame = FramedHeaderBox() self.whats_new_frame.set_header_label(_(u"What\u2019s New")) self.whats_new_frame.add(self.whats_new) whats_new_cat = self._update_whats_new_content() if whats_new_cat is not None: # only add to the visible right_frame if we actually have it self.right_column.pack_start(self.whats_new_frame, True, True, 0) self.whats_new_frame.header_implements_more_button() self.whats_new_frame.more.connect( 'clicked', self.on_category_clicked, whats_new_cat) def _update_recommended_for_you_content(self): if (self.recommended_for_you_panel and self.recommended_for_you_panel.get_parent()): # disconnect listeners self.recommended_for_you_panel.disconnect_by_func( self.on_application_activated) self.recommended_for_you_panel.disconnect_by_func( self.on_category_clicked) # and remove the panel self.right_column.remove(self.recommended_for_you_panel) self.recommended_for_you_panel = RecommendationsPanelLobby( self.db, self.properties_helper) self.recommended_for_you_panel.connect("application-activated", self.on_application_activated) self.recommended_for_you_panel.connect( 'more-button-clicked', self.on_category_clicked) # until bug #1048912 with the testcase in # tests/gtk3/test_lp1048912.py # is fixed this workaround for the drawing code in FramedHeaderBox # is needed self.recommended_for_you_panel.connect( "size-allocate", self._on_recommended_for_you_panel_size_allocate) self.right_column.pack_start(self.recommended_for_you_panel, True, True, 0) def _on_recommended_for_you_panel_size_allocate(self, rec_panel, stuff): """This workaround can go once the root cause for bug #1048912 is found, see also tests/gtk3/test_lp1048912.py """ self.queue_draw() def _append_recommended_for_you(self): # update will (re)create the widget from scratch self.recommended_for_you_panel = None self._update_recommended_for_you_content() def _update_appcount(self): enq = AppEnquire(self.cache, self.db) distro = get_distro() if get_global_filter().supported_only: query = distro.get_supported_query() else: query = xapian.Query('') length = enq.get_estimated_matches_count(query) text = gettext.ngettext("%(amount)s item", "%(amount)s items", length ) % {'amount': length} self.appcount.set_text(text) def _append_appcount(self): self.appcount = Gtk.Label() self.appcount.set_alignment(0.5, 0.5) self.appcount.set_margin_top(1) self.appcount.set_margin_bottom(4) self.vbox.pack_start(self.appcount, False, True, 0) self._update_appcount() return def build(self): self.header = _('Departments') self._build_homepage_view() self.show_all() return def refresh_apps(self): supported_only = get_global_filter().supported_only if (self._supported_only == supported_only): return self._supported_only = supported_only self._update_top_rated_content() self._update_whats_new_content() self._update_recommended_for_you_content() self._update_appcount() return
def setUp(self): self.db = get_test_db() parser = CategoriesParser(self.db) self.cats = parser.parse_applications_menu( '/usr/share/app-install')
def get_test_categories(db): parser = CategoriesParser(db) cats = parser.parse_applications_menu() return cats
class PkgListModel(QAbstractListModel): COLUMNS = ('_appname', '_pkgname', '_icon', '_summary', '_installed', '_description', '_ratings_total', '_ratings_average', '_installremoveprogress') def __init__(self, parent=None): super(PkgListModel, self).__init__() self._docs = [] roles = dict(enumerate(PkgListModel.COLUMNS)) self.setRoleNames(roles) self._query = "" self._category = "" pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") self.cache = get_pkg_info() self.db = StoreDatabase(pathname, self.cache) self.db.open(use_axi=False) self.backend = get_install_backend() self.backend.connect("transaction-progress-changed", self._on_backend_transaction_progress_changed) self.reviews = get_review_loader(self.cache) # FIXME: get this from a parent self._catparser = CategoriesParser(self.db) self._categories = self._catparser.parse_applications_menu( '/usr/share/app-install') # QAbstractListModel code def rowCount(self, parent=QModelIndex()): return len(self._docs) def data(self, index, role): if not index.isValid(): return None doc = self._docs[index.row()] role = self.COLUMNS[role] pkgname = unicode(self.db.get_pkgname(doc), "utf8", "ignore") appname = unicode(self.db.get_appname(doc), "utf8", "ignore") if role == "_pkgname": return pkgname elif role == "_appname": return appname elif role == "_summary": return unicode(self.db.get_summary(doc)) elif role == "_installed": if not pkgname in self.cache: return False return self.cache[pkgname].is_installed elif role == "_description": if not pkgname in self.cache: return "" return self.cache[pkgname].description elif role == "_icon": iconname = self.db.get_iconname(doc) return self._findIcon(iconname) elif role == "_ratings_average": stats = self.reviews.get_review_stats(Application( appname, pkgname)) if stats: return stats.ratings_average return 0 elif role == "_ratings_total": stats = self.reviews.get_review_stats(Application( appname, pkgname)) if stats: return stats.ratings_total return 0 elif role == "_installremoveprogress": if pkgname in self.backend.pending_transactions: return self.backend.pending_transactions[pkgname].progress return -1 return None # helper def _on_backend_transaction_progress_changed(self, backend, pkgname, progress): column = self.COLUMNS.index("_installremoveprogress") # FIXME: instead of the entire model, just find the row that changed top = self.createIndex(0, column) bottom = self.createIndex(self.rowCount() - 1, column) self.dataChanged.emit(top, bottom) def _findIcon(self, iconname): path = "/usr/share/icons/Humanity/categories/32/applications-other.svg" for ext in ["svg", "png", ".xpm"]: p = "/usr/share/app-install/icons/%s" % iconname if os.path.exists(p + ext): path = "file://%s" % p + ext break return path def clear(self): if self._docs == []: return self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1) self._docs = [] self.endRemoveRows() def _runQuery(self, querystr): self.clear() docs = self.db.get_docs_from_query(str(querystr), start=0, end=500, category=self._category) self.beginInsertRows(QModelIndex(), 0, len(docs) - 1) self._docs = docs self.endInsertRows() # install/remove interface (for qml) @pyqtSlot(str) def installPackage(self, pkgname): appname = "" iconname = "" app = Application(appname, pkgname) self.backend.install(app, iconname) @pyqtSlot(str) def removePackage(self, pkgname): appname = "" iconname = "" app = Application(appname, pkgname) self.backend.remove(app, iconname) # searchQuery property (for qml ) def getSearchQuery(self): return self._query def setSearchQuery(self, query): self._query = query self._runQuery(query) searchQueryChanged = QtCore.pyqtSignal() searchQuery = QtCore.pyqtProperty(unicode, getSearchQuery, setSearchQuery, notify=searchQueryChanged) # allow to refine searches for specific categories @pyqtSlot(str) def setCategory(self, catname): # empty category resets it if not catname: self._category = None else: # search for the category for cat in self._categories: if cat.name == catname: self._category = cat break else: raise Exception("Can not find category '%s'" % catname) # and trigger a query self._runQuery(self._query)
class PkgListModel(QAbstractListModel): COLUMNS = ('_appname', '_pkgname', '_icon', '_summary', '_installed', '_description', '_ratings_total', '_ratings_average', '_installremoveprogress') def __init__(self, parent=None): super(PkgListModel, self).__init__() self._docs = [] roles = dict(enumerate(PkgListModel.COLUMNS)) self.setRoleNames(roles) self._query = "" self._category = "" pathname = os.path.join(XAPIAN_BASE_PATH, "xapian") self.cache = get_pkg_info() self.db = StoreDatabase(pathname, self.cache) self.db.open(use_axi=False) self.backend = get_install_backend() self.backend.connect("transaction-progress-changed", self._on_backend_transaction_progress_changed) self.reviews = get_review_loader(self.cache) # FIXME: get this from a parent self._catparser = CategoriesParser(self.db) self._categories = self._catparser.parse_applications_menu( '/usr/share/app-install') # QAbstractListModel code def rowCount(self, parent=QModelIndex()): return len(self._docs) def data(self, index, role): if not index.isValid(): return None doc = self._docs[index.row()] role = self.COLUMNS[role] pkgname = unicode(self.db.get_pkgname(doc)) appname = unicode(self.db.get_appname(doc)) if role == "_pkgname": return pkgname elif role == "_appname": return appname elif role == "_summary": return unicode(self.db.get_summary(doc)) elif role == "_installed": if not pkgname in self.cache: return False return self.cache[pkgname].is_installed elif role == "_description": if not pkgname in self.cache: return "" return self.cache[pkgname].description elif role == "_icon": iconname = self.db.get_iconname(doc) return self._findIcon(iconname) elif role == "_ratings_average": stats = self.reviews.get_review_stats(Application(appname, pkgname)) if stats: return stats.ratings_average return 0 elif role == "_ratings_total": stats = self.reviews.get_review_stats(Application(appname, pkgname)) if stats: return stats.ratings_total return 0 elif role == "_installremoveprogress": if pkgname in self.backend.pending_transactions: return self.backend.pending_transactions[pkgname].progress return -1 return None # helper def _on_backend_transaction_progress_changed(self, backend, pkgname, progress): column = self.COLUMNS.index("_installremoveprogress") # FIXME: instead of the entire model, just find the row that changed top = self.createIndex(0, column) bottom = self.createIndex(self.rowCount()-1, column) self.dataChanged.emit(top, bottom) def _findIcon(self, iconname): path = "/usr/share/icons/Humanity/categories/32/applications-other.svg" for ext in ["svg", "png", ".xpm"]: p = "/usr/share/app-install/icons/%s" % iconname if os.path.exists(p+ext): path = "file://%s" % p+ext break return path def clear(self): self.beginRemoveRows(QModelIndex(), 0, self.rowCount()-1) self._docs = [] self.endRemoveRows() def _runQuery(self, querystr): self.clear() docs = self.db.get_docs_from_query( str(querystr), start=0, end=500, category=self._category) self.beginInsertRows(QModelIndex(), len(docs), len(docs)) self._docs = docs self.endInsertRows() # install/remove interface (for qml) @Slot(str) def installPackage(self, pkgname): appname = "" iconname = "" self.backend.install(pkgname, appname, iconname) @Slot(str) def removePackage(self, pkgname): appname = "" iconname = "" self.backend.remove(pkgname, appname, iconname) # searchQuery property (for qml ) def getSearchQuery(self): return self._query def setSearchQuery(self, query): self._query = query self._runQuery(query) searchQueryChanged = QtCore.Signal() searchQuery = Property(unicode, getSearchQuery, setSearchQuery, notify=searchQueryChanged) # allow to refine searches for specific categories @Slot(str) def setCategory(self, catname): # empty category resets it if not catname: self._category = None else: # search for the category for cat in self._categories: if cat.name == catname: self._category = cat break else: raise Exception("Can not find category '%s'" % catname) # and trigger a query self._runQuery(self._query)
def setUp(self): self.db = get_test_db() parser = CategoriesParser(self.db) self.cats = parser.parse_applications_menu('/usr/share/app-install')