diff --git a/CHANGELOG.md b/CHANGELOG.md index 6047c153..bac55c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ 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.5.1] - 2019-08-09 +### Improvements: +- suggestions are now retrieved asynchronously taking 45% less time. +- search response takes an average of 20% less time ( reaching 35% for several results ) +- app boot takes 98% less time when snapd is installed, but disabled +- FPAKMAN_TRAY (--tray) is not enabled by default (0). +### Fixes +- not showing correctly the latest flatpak app versions when bringing the search results +- [flatpak dependency](https://github.com/vinifmor/fpakman/issues/36) + ## [0.5.0] - 2019-08-06 ### Improvements - search results sorting takes an average of 35% less time, reaching 60% in some scenarios diff --git a/README.md b/README.md index e5edc104..a3b8aa64 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ You can change some application settings via environment variables or arguments - **FPAKMAN_FLATPAK**: enables / disables flatpak usage. Use **0** (disable) or **1** (enabled, default) - **FPAKMAN_SNAP**: enables / disables snap usage. Use **0** (disable) or **1** (enabled, default) - **FPAKMAN_CHECK_PACKAGING_ONCE**: If the available supported packaging types should be checked ONLY once. It improves the application speed if enabled, but can generate errors if you uninstall any packaging technology while using it, and every time a supported packaging type is installed it will only be available after a restart. Use **0** (disable, default) or **1** (enable). -- **FPAKMAN_TRAY**: If the tray icon and update-check daemon should be created. Use **0** (disable) or **1** (enable, default). +- **FPAKMAN_TRAY**: If the tray icon and update-check daemon should be created. Use **0** (disable, default) or **1** (enable). - **FPAKMAN_SUGGESTIONS**: If application suggestions should be displayed if no app is installed (runtimes do not count as apps). Use **0** (disable) or **1** (enable, default). ### How to improve the application performance diff --git a/aur/panel_entry.py b/aur/panel_entry.py index a2e2887e..20576729 100644 --- a/aur/panel_entry.py +++ b/aur/panel_entry.py @@ -1,4 +1,5 @@ # Generates a .desktop file based on the current python version. Used for AUR installation +import os import sys desktop_file = """ @@ -7,11 +8,15 @@ Type = Application Name = fpakman Categories = System; Comment = Manage your Flatpak / Snap applications -Exec = /usr/bin/fpakman --tray=0 -Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/logo.svg +Exec = {path} +Icon = {lib_path}/python{version}/site-packages/fpakman/resources/img/logo.svg """ py_version = "{}.{}".format(sys.version_info.major, sys.version_info.minor) +fpakman_cmd = os.getenv('FPAKMAN_PATH', '/usr/bin/fpakman') + with open('fpakman.desktop', 'w+') as f: - f.write(desktop_file.format(version=py_version)) + f.write(desktop_file.format(lib_path=os.getenv('FPAKMAN_LIB_PATH', '/usr/lib'), + version=py_version, + path=fpakman_cmd)) diff --git a/aur/tray_entry.py b/aur/tray_entry.py index 881a95e6..9b1b3d77 100644 --- a/aur/tray_entry.py +++ b/aur/tray_entry.py @@ -1,17 +1,26 @@ # Generates a .desktop file based on the current python version. Used for AUR installation +import os import sys desktop_file = """ [Desktop Entry] Type = Application -Name = fpakman ( tray ) +Name = fpakman (tray) Categories = System; Comment = Manage your Flatpak / Snap applications -Exec = /usr/bin/fpakman -Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/logo.svg +Exec = {path} +Icon = {lib_path}/python{version}/site-packages/fpakman/resources/img/logo.svg """ py_version = "{}.{}".format(sys.version_info.major, sys.version_info.minor) +fpakman_cmd = os.getenv('FPAKMAN_PATH', '/usr/bin/fpakman') + ' --tray=1' + with open('fpakman_tray.desktop', 'w+') as f: - f.write(desktop_file.format(version=py_version)) + f.write(desktop_file.format(lib_path=os.getenv('FPAKMAN_LIB_PATH', '/usr/lib'), + version=py_version, + path=fpakman_cmd)) + + +with open('fpakman-tray', 'w') as f: + f.write(fpakman_cmd) diff --git a/fpakman/__init__.py b/fpakman/__init__.py index 37b3cd4f..522235a1 100644 --- a/fpakman/__init__.py +++ b/fpakman/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.5.0' +__version__ = '0.5.1' __app_name__ = 'fpakman' import os diff --git a/fpakman/app.py b/fpakman/app.py index 89d6e8da..1eb7b586 100755 --- a/fpakman/app.py +++ b/fpakman/app.py @@ -21,7 +21,6 @@ from fpakman.core.snap.model import SnapApplication from fpakman.util import util from fpakman.util.cache import Cache from fpakman.util.memory import CacheCleaner -from fpakman.view.qt import dialog from fpakman.view.qt.systray import TrayIcon from fpakman.view.qt.window import ManageWindow @@ -46,7 +45,7 @@ parser.add_argument('-di', '--download-icons', action="store", choices=[0, 1], d parser.add_argument('--flatpak', action="store", default=os.getenv('FPAKMAN_FLATPAK', 1), choices=[0, 1], type=int, help='Enables / disables flatpak usage. Default: %(default)s') parser.add_argument('--snap', action="store", default=os.getenv('FPAKMAN_SNAP', 1), choices=[0, 1], type=int, help='Enables / disables snap usage. Default: %(default)s') parser.add_argument('-co', '--check-packaging-once', action="store", default=os.getenv('FPAKMAN_CHECK_PACKAGING_ONCE', 0), choices=[0, 1], type=int, help='If the available supported packaging types should be checked ONLY once. It improves the application speed if enabled, but can generate errors if you uninstall any packaging technology while using it, and every time a supported packaging type is installed it will only be available after a restart. Default: %(default)s') -parser.add_argument('--tray', action="store", default=os.getenv('FPAKMAN_TRAY', 1), choices=[0, 1], type=int, help='If the tray icon and update-check daemon should be created. Default: %(default)s') +parser.add_argument('--tray', action="store", default=os.getenv('FPAKMAN_TRAY', 0), choices=[0, 1], type=int, help='If the tray icon and update-check daemon should be created. Default: %(default)s') parser.add_argument('--sugs', action="store", default=os.getenv('FPAKMAN_SUGGESTIONS', 1), choices=[0, 1], type=int, help='If app suggestions should be displayed if no app is installed (runtimes do not count as apps). Default: %(default)s') args = parser.parse_args() @@ -135,18 +134,11 @@ if args.tray: manage_window=manage_window, check_interval=args.check_interval, update_notification=bool(args.update_notification)) - manage_window.tray_icon = trayIcon trayIcon.show() else: manage_window.refresh_apps() manage_window.show() -warnings = manager.list_warnings() - -if warnings: - for warning in warnings: - dialog.show_warning(title=locale_keys['warning'].capitalize(), body=warning) - CacheCleaner(caches).start() sys.exit(app.exec_()) diff --git a/fpakman/core/controller.py b/fpakman/core/controller.py index 065dba1b..22ce4b66 100755 --- a/fpakman/core/controller.py +++ b/fpakman/core/controller.py @@ -1,6 +1,6 @@ -import time from abc import ABC, abstractmethod from argparse import Namespace +from threading import Thread from typing import List, Dict from fpakman.core.disk import DiskCacheLoader, DiskCacheLoaderFactory @@ -103,6 +103,12 @@ class GenericApplicationManager(ApplicationManager): self.map = {m.get_app_type(): m for m in self.managers} self.disk_loader_factory = disk_loader_factory self._enabled_map = {} if app_args.check_packaging_once else None + self.thread_prepare = None + + def _wait_to_be_ready(self): + if self.thread_prepare: + self.thread_prepare.join() + self.thread_prepare = None def _sort(self, apps: List[Application], word: str) -> List[Application]: exact_name_matches, contains_name_matches, others = [], [], [] @@ -125,7 +131,6 @@ class GenericApplicationManager(ApplicationManager): return res def _is_enabled(self, man: ApplicationManager): - if self._enabled_map is not None: enabled = self._enabled_map.get(man.get_app_type()) @@ -137,21 +142,30 @@ class GenericApplicationManager(ApplicationManager): else: return man.is_enabled() + def _search(self, word: str, man: ApplicationManager, disk_loader, res: dict): + if self._is_enabled(man): + apps_found = man.search(word=word, disk_loader=disk_loader) + res['installed'].extend(apps_found['installed']) + res['new'].extend(apps_found['new']) + def search(self, word: str, disk_loader: DiskCacheLoader = None) -> Dict[str, List[Application]]: + self._wait_to_be_ready() + res = {'installed': [], 'new': []} norm_word = word.strip().lower() - disk_loader = None + disk_loader = self.disk_loader_factory.new() + disk_loader.start() + + threads = [] for man in self.managers: - if self._is_enabled(man): - if not disk_loader: - disk_loader = self.disk_loader_factory.new() - disk_loader.start() + t = Thread(target=self._search, args=(norm_word, man, disk_loader, res)) + t.start() + threads.append(t) - apps_found = man.search(word=norm_word, disk_loader=disk_loader) - res['installed'].extend(apps_found['installed']) - res['new'].extend(apps_found['new']) + for t in threads: + t.join() if disk_loader: disk_loader.stop = True @@ -163,6 +177,8 @@ class GenericApplicationManager(ApplicationManager): return res def read_installed(self, disk_loader: DiskCacheLoader = None) -> List[Application]: + self._wait_to_be_ready() + installed = [] disk_loader = None @@ -254,18 +270,26 @@ class GenericApplicationManager(ApplicationManager): return man.requires_root(action, app) def refresh(self, app: Application, root_password: str) -> FpakmanProcess: + self._wait_to_be_ready() + man = self._get_manager_for(app) if man: return man.refresh(app, root_password) - def prepare(self): + def _prepare(self): if self.managers: for man in self.managers: if self._is_enabled(man): man.prepare() + def prepare(self): + self.thread_prepare = Thread(target=self._prepare) + self.thread_prepare.start() + def list_updates(self) -> List[ApplicationUpdate]: + self._wait_to_be_ready() + updates = [] if self.managers: @@ -290,10 +314,22 @@ class GenericApplicationManager(ApplicationManager): return warnings + def _fill_suggestions(self, suggestions: list, man: ApplicationManager, limit: int): + if self._is_enabled(man): + man_sugs = man.list_suggestions(limit) + + if man_sugs: + suggestions.extend(man_sugs) + def list_suggestions(self, limit: int) -> List[Application]: if self.managers: - suggestions = [] + suggestions, threads = [], [] for man in self.managers: - if self._is_enabled(man): - suggestions.extend(man.list_suggestions(6)) + t = Thread(target=self._fill_suggestions, args=(suggestions, man, 6)) + t.start() + threads.append(t) + + for t in threads: + t.join() + return suggestions diff --git a/fpakman/core/flatpak/controller.py b/fpakman/core/flatpak/controller.py index 710d70f8..f1d64b55 100644 --- a/fpakman/core/flatpak/controller.py +++ b/fpakman/core/flatpak/controller.py @@ -181,8 +181,9 @@ class FlatpakManager(ApplicationManager): return updates def list_warnings(self) -> List[str]: - if not flatpak.has_remotes_set(): - return [self.locale_keys['flatpak.notification.no_remotes']] + if flatpak.is_installed(): + if not flatpak.has_remotes_set(): + return [self.locale_keys['flatpak.notification.no_remotes']] def list_suggestions(self, limit: int) -> List[FlatpakApplication]: diff --git a/fpakman/core/flatpak/worker.py b/fpakman/core/flatpak/worker.py index 44bff278..c6d5e422 100644 --- a/fpakman/core/flatpak/worker.py +++ b/fpakman/core/flatpak/worker.py @@ -48,6 +48,9 @@ class FlatpakAsyncDataLoader(AsyncDataLoader): if not self.app.base_data.version and self.app.base_data.latest_version: self.app.base_data.version = self.app.base_data.latest_version + if not self.app.installed and self.app.base_data.latest_version: + self.app.base_data.version = self.app.base_data.latest_version + if self.app.base_data.icon_url and self.app.base_data.icon_url.startswith('/'): self.app.base_data.icon_url = FLATHUB_URL + self.app.base_data.icon_url diff --git a/fpakman/util/memory.py b/fpakman/util/memory.py index 35ec2659..12f65911 100644 --- a/fpakman/util/memory.py +++ b/fpakman/util/memory.py @@ -3,7 +3,6 @@ from threading import Thread from typing import List from fpakman.util.cache import Cache -import gc class CacheCleaner(Thread): @@ -20,6 +19,5 @@ class CacheCleaner(Thread): for cache in self.caches: cache.clean_expired() - gc.collect() time.sleep(self.check_interval) diff --git a/fpakman/view/qt/apps_table.py b/fpakman/view/qt/apps_table.py index ca06ad80..b953626e 100644 --- a/fpakman/view/qt/apps_table.py +++ b/fpakman/view/qt/apps_table.py @@ -315,7 +315,7 @@ class AppsTable(QTableWidget): label_version.setStyleSheet("color: #4EC306; font-weight: bold") tooltip = self.window.locale_keys['version.installed_outdated'] - if app_v.model.base_data.version and app_v.model.base_data.latest_version and app_v.model.base_data.version < app_v.model.base_data.latest_version: + if app_v.model.installed and app_v.model.base_data.version and app_v.model.base_data.latest_version and app_v.model.base_data.version < app_v.model.base_data.latest_version: tooltip = '{}. {}: {}'.format(tooltip, self.window.locale_keys['version.latest'], app_v.model.base_data.latest_version) label_version.setText(label_version.text() + ' > {}'.format(app_v.model.base_data.latest_version)) diff --git a/fpakman/view/qt/thread.py b/fpakman/view/qt/thread.py index 3ad1fde1..401c005e 100644 --- a/fpakman/view/qt/thread.py +++ b/fpakman/view/qt/thread.py @@ -347,4 +347,18 @@ class FindSuggestions(AsyncAction): def run(self): self.signal_finished.emit(self.man.list_suggestions(limit=-1)) - \ No newline at end of file + + +class ListWarnings(QThread): + + signal_warnings = pyqtSignal(list) + + def __init__(self, man: ApplicationManager, locale_keys: dict): + super(QThread, self).__init__() + self.locale_keys = locale_keys + self.man = man + + def run(self): + warnings = self.man.list_warnings() + if warnings: + self.signal_warnings.emit(warnings) diff --git a/fpakman/view/qt/window.py b/fpakman/view/qt/window.py index a3a4a89a..946b9cb8 100755 --- a/fpakman/view/qt/window.py +++ b/fpakman/view/qt/window.py @@ -18,7 +18,7 @@ from fpakman.view.qt.history import HistoryDialog from fpakman.view.qt.info import InfoDialog from fpakman.view.qt.root import is_root, ask_root_password from fpakman.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \ - GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp, FindSuggestions + GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp, FindSuggestions, ListWarnings from fpakman.view.qt.view_model import ApplicationView DARK_ORANGE = '#FF4500' @@ -213,6 +213,19 @@ class ManageWindow(QWidget): self.dialog_about = None self.first_refresh = suggestions + self.thread_warnings = ListWarnings(man=manager, locale_keys=locale_keys) + self.thread_warnings.signal_warnings.connect(self._show_warnings) + + def _show_warnings(self, warnings: List[str]): + if warnings: + for warning in warnings: + dialog.show_warning(title=self.locale_keys['warning'].capitalize(), body=warning) + + def show(self): + super(ManageWindow, self).show() + if not self.thread_warnings.isFinished(): + self.thread_warnings.start() + def _show_about(self): if self.dialog_about is None: self.dialog_about = AboutDialog(self.locale_keys)