diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c3e9378..514294f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.6.0] +### Improvements: + +### Fixes: + ## [0.5.1] - 2019-08-12 ### Improvements: - suggestions are now retrieved asynchronously taking 45% less time. diff --git a/bauh/core/controller.py b/bauh/core/controller.py index c6829977..a44e1ee8 100755 --- a/bauh/core/controller.py +++ b/bauh/core/controller.py @@ -1,6 +1,6 @@ from argparse import Namespace from threading import Thread -from typing import List, Set +from typing import List, Set, Type from bauh_api.abstract.controller import SoftwareManager, SearchResult from bauh_api.abstract.handler import ProcessWatcher @@ -96,20 +96,34 @@ class GenericSoftwareManager(SoftwareManager): self.thread_prepare.join() self.thread_prepare = None - def read_installed(self, disk_loader: DiskCacheLoader = None) -> List[SoftwarePackage]: + def read_installed(self, disk_loader: DiskCacheLoader = None, pkg_types: Set[Type[SoftwarePackage]] = None) -> List[SoftwarePackage]: self._wait_to_be_ready() installed = [] disk_loader = None - for man in self.managers: - if self._is_enabled(man): - if not disk_loader: - disk_loader = self.disk_loader_factory.new() - disk_loader.start() + if not pkg_types: # any type + for man in self.managers: + if self._is_enabled(man): + if not disk_loader: + disk_loader = self.disk_loader_factory.new() + disk_loader.start() - installed.extend(man.read_installed(disk_loader=disk_loader)) + installed.extend(man.read_installed(disk_loader=disk_loader, pkg_types=None)) + + else: + man_already_used = [] + + for t in pkg_types: + man = self.map.get(t) + if man and (man not in man_already_used) and self._is_enabled(man): + + if not disk_loader: + disk_loader = self.disk_loader_factory.new() + disk_loader.start() + + installed.extend(man.read_installed(disk_loader=disk_loader, pkg_types=None)) if disk_loader: disk_loader.stop = True diff --git a/bauh/resources/img/disc.svg b/bauh/resources/img/disc.svg new file mode 100755 index 00000000..f3ca7d18 --- /dev/null +++ b/bauh/resources/img/disc.svg @@ -0,0 +1,128 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bauh/view/qt/apps_table.py b/bauh/view/qt/apps_table.py index 569b72e6..90830eb1 100644 --- a/bauh/view/qt/apps_table.py +++ b/bauh/view/qt/apps_table.py @@ -239,19 +239,19 @@ class AppsTable(QTableWidget): self.setEnabled(True) if pkg_views: - for idx, app_v in enumerate(pkg_views): - self._set_col_name(idx, app_v) - self._set_col_version(idx, app_v) - self._set_col_description(idx, app_v) - self._set_col_type(idx, app_v) - self._set_col_installed(idx, app_v) + for idx, pkgv in enumerate(pkg_views): + self._set_col_name(idx, pkgv) + self._set_col_version(idx, pkgv) + self._set_col_description(idx, pkgv) + self._set_col_type(idx, pkgv) + self._set_col_installed(idx, pkgv) - self._set_col_settings(idx, app_v) + self._set_col_settings(idx, pkgv) col_update = None - if update_check_enabled and app_v.model.update: - col_update = UpdateToggleButton(app_v, self.window, self.window.locale_keys, app_v.model.update) + if update_check_enabled and pkgv.model.update: + col_update = UpdateToggleButton(pkgv, self.window, self.window.locale_keys, pkgv.model.update) self.setCellWidget(idx, 6, col_update) diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index 493263a2..6334e691 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -1,13 +1,13 @@ import subprocess import time from datetime import datetime, timedelta -from typing import List +from typing import List, Type, Set import requests from PyQt5.QtCore import QThread, pyqtSignal from bauh_api.abstract.controller import SoftwareManager from bauh_api.abstract.handler import ProcessWatcher -from bauh_api.abstract.model import PackageStatus +from bauh_api.abstract.model import PackageStatus, SoftwarePackage from bauh_api.abstract.view import InputViewComponent, MessageType from bauh_api.exception import NoInternetException from bauh_api.util.cache import Cache @@ -74,7 +74,7 @@ class UpdateSelectedApps(AsyncAction): success = False if self.apps_to_update: - updated = 0 + updated, updated_types = 0, set() for app in self.apps_to_update: self.change_status('{} {}...'.format(self.locale_keys['manage_window.status.upgrading'], app.model.base_data.name)) success = bool(self.manager.update(app.model, self.root_password, self)) @@ -83,37 +83,44 @@ class UpdateSelectedApps(AsyncAction): break else: updated += 1 + updated_types.add(app.model.__class__) self.signal_output.emit('\n') - self.notify_finished({'success': success, 'updated': updated}) + self.notify_finished({'success': success, 'updated': updated, 'types': updated_types}) self.apps_to_update = None class RefreshApps(AsyncAction): - def __init__(self, manager: SoftwareManager, app: PackageView = None): + def __init__(self, manager: SoftwareManager, app: PackageView = None, pkg_types: Set[Type[SoftwarePackage]] = None): super(RefreshApps, self).__init__() self.manager = manager self.app = app # app that should be on list top + self.pkg_types = pkg_types def run(self): - installed = self.manager.read_installed() + installed = self.manager.read_installed(pkg_types=self.pkg_types) + refreshed_types = set() - if installed and self.app: + if installed: idx_found, app_found = None, None for idx, ins in enumerate(installed): - if ins.get_type() == self.app.model.get_type() and ins.base_data.id == self.app.model.base_data.id: + if self.app and ins.get_type() == self.app.model.get_type() and ins.base_data.id == self.app.model.base_data.id: idx_found = idx app_found = ins break + if self.pkg_types: + refreshed_types.add(ins.__class__) + if app_found: del installed[idx_found] installed.insert(0, app_found) - self.notify_finished(installed) + self.notify_finished({'installed': installed, 'types': refreshed_types}) self.app = None + self.pkg_types = None class UninstallApp(AsyncAction): diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 73ab5d21..f493e507 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -1,6 +1,6 @@ import operator from functools import reduce -from typing import List +from typing import List, Type, Set from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap @@ -273,6 +273,7 @@ class ManageWindow(QWidget): self.ref_checkbox_only_apps.setVisible(True) self.ref_bt_upgrade.setVisible(True) self.update_apps(apps=None, as_installed=True) + self.input_search.setText('') def _show_about(self): if self.dialog_about is None: @@ -324,7 +325,7 @@ class ManageWindow(QWidget): self.checkbox_console.setChecked(False) self.textarea_output.hide() - def refresh_apps(self, keep_console: bool = True, top_app: PackageView = None): + def refresh_apps(self, keep_console: bool = True, top_app: PackageView = None, pkg_types: Set[Type[SoftwarePackage]] = None): self.type_filter = None self.input_search.clear() @@ -335,16 +336,15 @@ class ManageWindow(QWidget): self.ref_checkbox_only_apps.setVisible(False) self._begin_action(self.locale_keys['manage_window.status.refreshing'], clear_filters=True) - if top_app: - self.thread_refresh.app = top_app # the app will be on top when refresh happens - + self.thread_refresh.app = top_app # the app will be on top when refresh happens + self.thread_refresh.pkg_types = pkg_types self.thread_refresh.start() - def _finish_refresh_apps(self, apps: List[SoftwarePackage]): + def _finish_refresh_apps(self, res: dict): self.finish_action() self.ref_checkbox_only_apps.setVisible(True) self.ref_bt_upgrade.setVisible(True) - self.update_apps(apps, as_installed=True) + self.update_apps(res['installed'], as_installed=True, types=res['types']) self.first_refresh = False def uninstall_app(self, app: PackageView): @@ -393,7 +393,7 @@ class ManageWindow(QWidget): if self._can_notify_user(): util.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['uninstalled'])) - self.refresh_apps() + self.refresh_apps(pkg_types={app.model.__class__}) else: if self._can_notify_user(): util.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.uninstall.failed'])) @@ -410,7 +410,7 @@ class ManageWindow(QWidget): if self._can_notify_user(): util.notify_user('{} {}'.format(res['app'], self.locale_keys['downgraded'])) - self.refresh_apps() + self.refresh_apps(pkg_types={res['app'].model.__class__}) if self.tray_icon: self.tray_icon.verify_updates(notify_user=False) @@ -514,13 +514,19 @@ class ManageWindow(QWidget): geo.moveCenter(center_point) self.move(geo.topLeft()) - def update_apps(self, apps: List[SoftwarePackage], as_installed: bool, update_check_enabled: bool = True): + def update_apps(self, apps: List[SoftwarePackage], as_installed: bool, update_check_enabled: bool = True, types: Set[type] = None): napps = 0 # number of apps (not libraries, runtimes or something else) available_types = {} if apps is not None: self.pkgs = [] + old_installed = None + + if as_installed: + old_installed = self.pkgs_installed + self.pkgs_installed = [] + for app in apps: app_model = PackageView(model=app, visible=app.is_application() or not self.checkbox_only_apps.isChecked()) available_types[app.get_type()] = app.get_type_icon_path() @@ -529,6 +535,17 @@ class ManageWindow(QWidget): if as_installed: self.pkgs_installed.append(app_model) + + if old_installed and types: + for pkgv in old_installed: + if not pkgv.model.__class__ in types: + available_types[pkgv.model.get_type()] = pkgv.model.get_type_icon_path() + napps += 1 if pkgv.model.is_application() else 0 + self.pkgs.append(pkgv) + + if as_installed: + self.pkgs_installed.append(pkgv) + else: # use installed self.pkgs = self.pkgs_installed for app in self.pkgs: @@ -607,11 +624,10 @@ class ManageWindow(QWidget): if self.manager.requires_root('update', app_v.model): requires_root = True - bt_ex = UpdateToggleButton(None, self, self.locale_keys, clickable=False) if to_update and dialog.ask_confirmation(title=self.locale_keys['manage_window.upgrade_all.popup.title'], body=self.locale_keys['manage_window.upgrade_all.popup.body'], locale_keys=self.locale_keys, - widgets=[bt_ex]): + widgets=[UpdateToggleButton(None, self, self.locale_keys, clickable=False)]): pwd = None if not is_root() and requires_root: @@ -633,7 +649,7 @@ class ManageWindow(QWidget): if self._can_notify_user(): util.notify_user('{} {}'.format(res['updated'], self.locale_keys['notification.update_selected.success'])) - self.refresh_apps() + self.refresh_apps(pkg_types=res['types']) if self.tray_icon: self.tray_icon.verify_updates() @@ -691,9 +707,9 @@ class ManageWindow(QWidget): self.combo_filter_type.setEnabled(True) self.checkbox_updates.setEnabled(True) - def downgrade_app(self, app: PackageView): + def downgrade_app(self, pkgv: PackageView): pwd = None - requires_root = self.manager.requires_root('downgrade', app.model) + requires_root = self.manager.requires_root('downgrade', pkgv.model) if not is_root() and requires_root: pwd, ok = ask_root_password(self.locale_keys) @@ -702,17 +718,17 @@ class ManageWindow(QWidget): return self._handle_console_option(True) - self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.downgrading'], app.model.base_data.name)) + self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.downgrading'], pkgv.model.base_data.name)) - self.thread_downgrade.app = app + self.thread_downgrade.app = pkgv self.thread_downgrade.root_password = pwd self.thread_downgrade.start() - def get_app_info(self, app: dict): + def get_app_info(self, pkg: dict): self._handle_console_option(False) self._begin_action(self.locale_keys['manage_window.status.info']) - self.thread_get_info.app = app + self.thread_get_info.app = pkg self.thread_get_info.start() def get_app_history(self, app: dict): @@ -786,7 +802,7 @@ class ManageWindow(QWidget): if self._can_notify_user(): util.notify_user(msg='{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['installed'])) - self.refresh_apps(top_app=app) + self.refresh_apps(top_app=app, pkg_types={app.model.__class__}) else: if self._can_notify_user(): util.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.install.failed']))