diff --git a/CHANGELOG.md b/CHANGELOG.md index 349b0123..a2d8259b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - "Application settings" button located in right lower corner - Package "Name" filter field (above the packages table) - Showing the number of packages being shown by the total found +- "Show" button for large fields of the "Info" window ### Improvements: - Reading installed Snaps now takes around 95% less time - Reading installed Flatpaks now takes around 45% less time - Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...) +- When a package is installed, it will be the first element of the table after refreshing - Progress bar status can now be controlled by the software manager while an operation is being executed - Flatpak: showing runtime branches as versions when they are not available @@ -32,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixes: - cached Flatpak app current version +- disk loader not filling all requested cached data from the disk ### Code - Code was internally modularized as: "api" (conceptual classes used to create custom software managers), "gems" (software managers), "commons" (common classes shared between the UI and "gems") diff --git a/bauh/app.py b/bauh/app.py index 71c10475..36c7ca9f 100755 --- a/bauh/app.py +++ b/bauh/app.py @@ -30,7 +30,7 @@ context = ApplicationContext(i18n=i18n, download_icons=args.download_icons, app_root_dir=ROOT_DIR, cache_factory=cache_factory, - disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache), + disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache, logger=logger), logger=logger) user_config = config.read() diff --git a/bauh/core/controller.py b/bauh/core/controller.py index fb9bc656..862c12a5 100755 --- a/bauh/core/controller.py +++ b/bauh/core/controller.py @@ -1,3 +1,4 @@ +import time from argparse import Namespace from threading import Thread from typing import List, Set, Type @@ -20,6 +21,8 @@ class GenericSoftwareManager(SoftwareManager): self.thread_prepare = None self.i18n = context.i18n self.disk_loader_factory = context.disk_loader_factory + self.logger = context.logger + self._already_prepared = [] def reset_cache(self): if self._available_map is not None: @@ -66,6 +69,7 @@ class GenericSoftwareManager(SoftwareManager): res.new.extend(apps_found.new) def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult: + ti = time.time() self._wait_to_be_ready() res = SearchResult([], [], 0) @@ -85,13 +89,15 @@ class GenericSoftwareManager(SoftwareManager): t.join() if disk_loader: - disk_loader.stop = True + disk_loader.stop_working() disk_loader.join() res.installed = self._sort(res.installed, norm_word) res.new = self._sort(res.new, norm_word) res.total = len(res.installed) + len(res.new) + tf = time.time() + self.logger.info('Took {0:.2f} seconds'.format(tf - ti)) return res def _wait_to_be_ready(self): @@ -106,6 +112,7 @@ class GenericSoftwareManager(SoftwareManager): return True def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult: + ti = time.time() self._wait_to_be_ready() res = SearchResult([], None, 0) @@ -138,9 +145,11 @@ class GenericSoftwareManager(SoftwareManager): res.total += man_res.total if disk_loader: - disk_loader.stop = True + disk_loader.stop_working() disk_loader.join() + tf = time.time() + self.logger.info('Took {0:.2f} seconds'.format(tf - ti)) return res def downgrade(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool: @@ -221,8 +230,9 @@ class GenericSoftwareManager(SoftwareManager): def _prepare(self): if self.managers: for man in self.managers: - if self._can_work(man): + if man not in self._already_prepared and self._can_work(man): man.prepare() + self._already_prepared.append(man) def prepare(self): self.thread_prepare = Thread(target=self._prepare) diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 02c2007d..d2ed351e 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -100,13 +100,13 @@ class ArchManager(SoftwareManager): if pkgsinfo: for pkgdata in pkgsinfo: - app = self.mapper.map_api_data(pkgdata, not_signed) - app.downgrade_enabled = downgrade_enabled + pkg = self.mapper.map_api_data(pkgdata, not_signed) + pkg.downgrade_enabled = downgrade_enabled if disk_loader: - disk_loader.fill(app) - app.status = PackageStatus.READY + disk_loader.fill(pkg) + pkg.status = PackageStatus.READY - apps.append(app) + apps.append(pkg) def _fill_mirror_pkgs(self, mirrors: dict, apps: list): # TODO @@ -203,8 +203,8 @@ class ArchManager(SoftwareManager): watcher.change_progress(50) if info.get('required by'): - pkname = '"{}"'.format(pkg.name) - msg = '{}:\n\n{}\n\n{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), info['required by'], self.i18n['arch.uninstall.required_by.advice'].format(pkname)) + pkname = bold(pkg.name) + msg = '{}:

{}

{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), bold(info['required by']), self.i18n['arch.uninstall.required_by.advice'].format(pkname)) watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING) return False @@ -537,7 +537,6 @@ class ArchManager(SoftwareManager): def prepare(self): self.dcache_updater.start() - self.aur_index_updater.start() def list_updates(self) -> List[PackageUpdate]: diff --git a/bauh/gems/arch/worker.py b/bauh/gems/arch/worker.py index 5afa4327..bebdc376 100644 --- a/bauh/gems/arch/worker.py +++ b/bauh/gems/arch/worker.py @@ -22,7 +22,6 @@ class AURIndexUpdater(Thread): self.man = man def run(self): - while True: self.logger.info('Pre-indexing AUR packages in memory') res = self.http_client.get(URL_INDEX) diff --git a/bauh/util/disk.py b/bauh/util/disk.py index accdd5d2..a6246970 100644 --- a/bauh/util/disk.py +++ b/bauh/util/disk.py @@ -1,5 +1,7 @@ import json +import logging import os +import time from threading import Thread, Lock from typing import Type, Dict @@ -10,13 +12,15 @@ from bauh.api.abstract.model import SoftwarePackage class AsyncDiskCacheLoader(Thread, DiskCacheLoader): - def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache]): + def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache], logger: logging.Logger): super(AsyncDiskCacheLoader, self).__init__(daemon=True) - self.apps = [] - self.stop = False + self.pkgs = [] + self._work = True self.lock = Lock() self.cache_map = cache_map self.enabled = enabled + self.logger = logger + self.processed = 0 def fill(self, pkg: SoftwarePackage): """ @@ -24,25 +28,28 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader): :param pkg: :return: """ - if self.enabled: - if pkg and pkg.supports_disk_cache(): - self.lock.acquire() - self.apps.append(pkg) - self.lock.release() + if self.enabled and pkg and pkg.supports_disk_cache(): + self.pkgs.append(pkg) + + def stop_working(self): + self._work = False def run(self): if self.enabled: + last = 0 + while True: - if self.apps: - self.lock.acquire() - app = self.apps[0] - del self.apps[0] - self.lock.release() - self._fill_cached_data(app) - elif self.stop: + time.sleep(0.00001) + if len(self.pkgs) > self.processed: + pkg = self.pkgs[last] + + self._fill_cached_data(pkg) + self.processed += 1 + last += 1 + elif not self._work: break - def _fill_cached_data(self, pkg: SoftwarePackage): + def _fill_cached_data(self, pkg: SoftwarePackage) -> bool: if self.enabled: if os.path.exists(pkg.get_disk_data_path()): with open(pkg.get_disk_data_path()) as f: @@ -54,12 +61,16 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader): if cache: cache.add_non_existing(pkg.id, cached_data) + return True + return False + class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory): - def __init__(self, disk_cache_enabled: bool): + def __init__(self, disk_cache_enabled: bool, logger: logging.Logger): super(DefaultDiskCacheLoaderFactory, self).__init__() self.disk_cache_enabled = disk_cache_enabled + self.logger = logger self.cache_map = {} def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache): @@ -68,4 +79,4 @@ class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory): self.cache_map[pkg_type] = cache def new(self) -> AsyncDiskCacheLoader: - return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map) + return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map, logger=self.logger) diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index b17e9c92..cdb8a28f 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -5,7 +5,7 @@ from typing import List, Type, Set from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \ - QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction + QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction, QApplication from bauh.api.abstract.cache import MemoryCache from bauh.api.abstract.context import ApplicationContext @@ -231,7 +231,8 @@ class ManageWindow(QWidget): self.toolbar_bottom.addWidget(new_spacer()) self.progress_bar = QProgressBar() - self.progress_bar.setMaximumHeight(7) + self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4) + self.progress_bar.setTextVisible(False) self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)