From f36991ea1fbc09c0d973d20f8dde1ab777f880f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Moreira?= Date: Tue, 18 Jun 2019 19:02:16 -0300 Subject: [PATCH] 0.2.0 **Features** - Management panel shows update commands streams - Management panel status label is "orange" now **Fixes** - Application name is not properly showing for Flatpak 1.2.X --- CHANGELOG.md | 18 ++- README.md | 9 +- {arch => aur}/register_app.py | 12 +- fpakman/__init__.py | 1 + fpakman/app.py | 11 +- fpakman/core/__init__.py | 2 +- fpakman/core/controller.py | 4 +- fpakman/core/flatpak.py | 15 ++- fpakman/core/model.py | 16 +++ fpakman/core/resource.py | 7 +- fpakman/core/system.py | 5 + fpakman/core/util.py | 5 +- fpakman/resources/img/flathub_10.svg | 179 --------------------------- fpakman/resources/locale/pt | 2 +- fpakman/view/qt/systray.py | 9 +- fpakman/view/qt/window.py | 54 +++++--- setup.py | 2 +- 17 files changed, 122 insertions(+), 229 deletions(-) rename {arch => aur}/register_app.py (67%) delete mode 100644 fpakman/resources/img/flathub_10.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index d0046e94..7b9681db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,18 @@ 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.2.0] - 2019-06-18 +### Features +- Management panel shows update commands streams +- Management panel status label is "orange" now + +### Fixes +- Application name is not properly showing for Flatpak 1.2.X + ## [0.1.0] - 2019-06-14 ### Features - - System tray icon. - - Applications management window. - - Support for the following locales: PT, EN, ES. - - System notification for new updates. - - Update applications. +- System tray icon. +- Applications management window. +- Support for the following locales: PT, EN, ES. +- System notification for new updates. +- Update applications. diff --git a/README.md b/README.md index 817b6a35..f9895877 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,13 @@ It has also a management window allowing the user to see all installed applicati - python-pyqt5 ### Distribution -Currently available via **PyPi** (sudo pip3 install fpakman) or **AUR** (for Arch Linux users. **fpakman-staging** is just for testing, do not install it.) +**PyPi** +``` +sudo pip3 install fpakman +``` + +**AUR** +As **fpakman** package. There is also a staging version (**fpakman-staging**) but is intended for testing and it may not work properly. ### Manual installation: @@ -38,6 +44,5 @@ You can change some application settings via environment variables: - **FPAKMAN_CHECK_INTERVAL**: define the updates check interval in seconds. Default: 60. ### Roadmap -- Show updates being applied - Search and install applications - Uninstall applications diff --git a/arch/register_app.py b/aur/register_app.py similarity index 67% rename from arch/register_app.py rename to aur/register_app.py index d057c6ed..98516e09 100644 --- a/arch/register_app.py +++ b/aur/register_app.py @@ -1,5 +1,5 @@ # used for AUR installation -import subprocess +import os import sys from pathlib import Path @@ -7,14 +7,18 @@ desktop_file = """ [Desktop Entry] Type = Application Name = fpakman +Categories = System; Comment = Manage your Flatpak applications Exec = /usr/bin/fpakman Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/flathub_45.svg """.format(version="{}.{}".format(sys.version_info.major, sys.version_info.minor)) -file_path = '{}/.local/share/applications/fpakman.desktop'.format(str(Path.home())) +apps_path = '{}/.local/share/applications'.format(str(Path.home())) + +if not os.path.exists(apps_path): + os.mkdir(apps_path) + +file_path = '{}/fpakman.desktop'.format(apps_path) with open(file_path, 'w+') as f: f.write(desktop_file) - -subprocess.run('desktop-file-install {}'.format(file_path), shell=True) diff --git a/fpakman/__init__.py b/fpakman/__init__.py index e69de29b..7fd229a3 100644 --- a/fpakman/__init__.py +++ b/fpakman/__init__.py @@ -0,0 +1 @@ +__version__ = '0.2.0' diff --git a/fpakman/app.py b/fpakman/app.py index 81a90d00..0ee4c73c 100755 --- a/fpakman/app.py +++ b/fpakman/app.py @@ -2,13 +2,15 @@ import argparse import os import sys -from PyQt5.QtWidgets import QApplication, QWidget +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QApplication +from fpakman import __version__ +from fpakman.core import resource +from fpakman.core import util from fpakman.core.controller import FlatpakController from fpakman.core.model import FlatpakManager from fpakman.view.qt.systray import TrayIcon -from fpakman.core import __version__ -from fpakman.core import util parser = argparse.ArgumentParser(prog='fpakman', description="GUI for Flatpak applications management") parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__)) @@ -17,13 +19,12 @@ args = parser.parse_args() locale_keys = util.get_locale_keys() app = QApplication(sys.argv) +app.setWindowIcon(QIcon(resource.get_path('img/flathub_45.svg'))) manager = FlatpakManager() manager.load_database_async() controller = FlatpakController(manager) -hidden_widget = QWidget() trayIcon = TrayIcon(locale_keys=locale_keys, - parent=hidden_widget, controller=controller, check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60))) trayIcon.show() diff --git a/fpakman/core/__init__.py b/fpakman/core/__init__.py index b794fd40..8b137891 100755 --- a/fpakman/core/__init__.py +++ b/fpakman/core/__init__.py @@ -1 +1 @@ -__version__ = '0.1.0' + diff --git a/fpakman/core/controller.py b/fpakman/core/controller.py index 3e43630b..84ed8233 100755 --- a/fpakman/core/controller.py +++ b/fpakman/core/controller.py @@ -11,8 +11,8 @@ class FlatpakController: def refresh(self) -> List[dict]: return self.model.read_installed() - def update(self, package_refs: List[str]) -> List[dict]: - return self.model.update_apps(package_refs) + def update(self, app_ref: str): + return self.model.update_app(app_ref) def check_installed(self) -> bool: version = self.model.get_version() diff --git a/fpakman/core/flatpak.py b/fpakman/core/flatpak.py index 823c146a..fa9ca0bf 100755 --- a/fpakman/core/flatpak.py +++ b/fpakman/core/flatpak.py @@ -23,7 +23,7 @@ def app_str_to_json(line: str, version: str) -> dict: app['name'] = ref_data[0].split('.')[-1] app['version'] = None elif '1.2' <= version < '1.3': - app = {'name': app_array[0][1].split('.')[-1], + app = {'name': app_array[1].strip().split('.')[-1], 'id': app_array[1], 'version': app_array[2], 'branch': app_array[3], @@ -68,8 +68,17 @@ def list_installed() -> List[dict]: return [] -def update(ref: str): - return bool(system.run_cmd('flatpak update -y ' + ref)) +def update(app_ref: str) -> bool: + return bool(system.run_cmd('flatpak update -y ' + app_ref)) + + +def update_and_stream(app_ref: str): + """ + Updates the app reference and streams Flatpak output, + :param app_ref: + :return: + """ + return system.stream_cmd(['flatpak', 'update', '-y', app_ref]) def list_updates_as_str(): diff --git a/fpakman/core/model.py b/fpakman/core/model.py index 115976c6..8346a4ad 100755 --- a/fpakman/core/model.py +++ b/fpakman/core/model.py @@ -90,3 +90,19 @@ class FlatpakManager: return [*self.apps] return [] + + def update_app(self, ref: str): + + """ + :param ref: + :return: the update command stream + """ + + if self.apps: + + package_found = [app for app in self.apps if app['ref'] == ref] + + if package_found: + return flatpak.update_and_stream(ref) + + return None diff --git a/fpakman/core/resource.py b/fpakman/core/resource.py index 0918e299..e9378c6d 100755 --- a/fpakman/core/resource.py +++ b/fpakman/core/resource.py @@ -1,10 +1,7 @@ import os +app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + def get_path(resource_path): - app_dir = get_app_dir() return app_dir + '/resources/' + resource_path - - -def get_app_dir(): - return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) diff --git a/fpakman/core/system.py b/fpakman/core/system.py index dcd9a138..ddd5edfa 100644 --- a/fpakman/core/system.py +++ b/fpakman/core/system.py @@ -1,6 +1,11 @@ import subprocess +from typing import List def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False) -> str: res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE) return res.stdout.decode() if ignore_return_code or res.returncode == expected_code else None + + +def stream_cmd(cmd: List[str]): + return subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout diff --git a/fpakman/core/util.py b/fpakman/core/util.py index f502ceee..f66016ca 100644 --- a/fpakman/core/util.py +++ b/fpakman/core/util.py @@ -8,10 +8,11 @@ def get_locale_keys(): current_locale = locale.getdefaultlocale() locale_path = None + if current_locale: current_locale = current_locale[0] - locale_dir = '{}/resources/locale'.format(resource.get_app_dir()) + locale_dir = resource.get_path('locale') for locale_file in glob.glob(locale_dir + '/*'): name = locale_file.split('/')[-1] @@ -21,7 +22,7 @@ def get_locale_keys(): break if not locale_path: - locale_path = '{}/resources/locale/en'.format(resource.get_app_dir()) + locale_path = resource.get_path('locale/en') with open(locale_path, 'r') as f: locale_keys = f.readlines() diff --git a/fpakman/resources/img/flathub_10.svg b/fpakman/resources/img/flathub_10.svg deleted file mode 100644 index 2311083c..00000000 --- a/fpakman/resources/img/flathub_10.svg +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fpakman/resources/locale/pt b/fpakman/resources/locale/pt index 5b817152..61db2c38 100644 --- a/fpakman/resources/locale/pt +++ b/fpakman/resources/locale/pt @@ -7,7 +7,7 @@ manage_window.columns.ref=Ref manage_window.columns.origin=Origem manage_window.columns.update=Atualizar ? manage_window.checkbox.only_apps=Apps -manage_window.label.updates=Actualizaciones +manage_window.label.updates=Atualizações manage_window.status.refreshing=Recarregando manage_window.status.updating=Atualizando popup.flatpak_not_installed.title=Erro diff --git a/fpakman/view/qt/systray.py b/fpakman/view/qt/systray.py index f81d6a21..c97a4834 100755 --- a/fpakman/view/qt/systray.py +++ b/fpakman/view/qt/systray.py @@ -34,15 +34,16 @@ class UpdateCheck(QThread): class TrayIcon(QSystemTrayIcon): - def __init__(self, locale_keys: dict, controller: FlatpakController, check_interval: int = 60, parent=None): + def __init__(self, locale_keys: dict, controller: FlatpakController, check_interval: int = 60): + super(TrayIcon, self).__init__() self.locale_keys = locale_keys self.controller = controller self.icon_default = QIcon(resource.get_path('img/flathub_45.svg')) self.icon_update = QIcon(resource.get_path('img/update_logo.svg')) - QSystemTrayIcon.__init__(self, self.icon_default, parent) + self.setIcon(self.icon_default) - self.menu = QMenu(parent) + self.menu = QMenu() self.action_manage = self.menu.addAction(self.locale_keys['tray.action.manage']) self.action_manage.triggered.connect(self.show_manage_window) self.action_exit = self.menu.addAction(self.locale_keys['tray.action.exit']) @@ -63,7 +64,7 @@ class TrayIcon(QSystemTrayIcon): self.setToolTip(msg) if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)): - os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_10.svg'), msg)) + os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_45.svg'), msg)) if self.manage_window: self.manage_window.refresh() diff --git a/fpakman/view/qt/window.py b/fpakman/view/qt/window.py index 2c3a1dc4..978a2ed8 100755 --- a/fpakman/view/qt/window.py +++ b/fpakman/view/qt/window.py @@ -8,9 +8,9 @@ from PyQt5.QtGui import QIcon, QColor, QPixmap, QWindowStateChangeEvent from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QTableWidget, \ QTableWidgetItem, QTableView, QCheckBox, QHeaderView, QToolButton, QToolBar, \ - QSizePolicy, QLabel, QMessageBox + QSizePolicy, QLabel, QMessageBox, QPlainTextEdit -from fpakman.core import __version__ +from fpakman import __version__ from fpakman.core import resource from fpakman.core.controller import FlatpakController @@ -86,6 +86,7 @@ class ManageWindow(QWidget): self.label_status = QLabel() self.label_status.setText('') + self.label_status.setStyleSheet("color: orange") toolbar.addWidget(self.label_status) spacer = QWidget() @@ -94,7 +95,7 @@ class ManageWindow(QWidget): self.bt_refresh = QToolButton() self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg'))) - self.bt_refresh.clicked.connect(self.refresh) + self.bt_refresh.clicked.connect(lambda: self.refresh(clear_output=True)) toolbar.addWidget(self.bt_refresh) self.bt_update = QToolButton() @@ -118,8 +119,16 @@ class ManageWindow(QWidget): self.layout.addWidget(self.table_apps) + self.textarea_output = QPlainTextEdit(self) + self.textarea_output.resize(self.table_apps.size()) + self.textarea_output.setStyleSheet("background: black; color: white;") + self.layout.addWidget(self.textarea_output) + self.textarea_output.setVisible(False) + self.textarea_output.setReadOnly(True) + self.thread_update = UpdateSelectedApps(self.controller) - self.thread_update.signal.connect(self._finish_update_selected) + self.thread_update.signal_output.connect(self._update_action_output) + self.thread_update.signal_finished.connect(self._finish_update_selected) self.thread_refresh = RefreshApps(self.controller) self.thread_refresh.signal.connect(self._finish_refresh) @@ -202,11 +211,16 @@ class ManageWindow(QWidget): self.thread_lock.release() - def refresh(self): + def refresh(self, clear_output: bool = True): if self._acquire_lock(): self._check_flatpak_installed() self._begin_action(self.locale_keys['manage_window.status.refreshing'] + '...') + + if clear_output: + self.textarea_output.clear() + self.textarea_output.hide() + self.thread_refresh.start() def _finish_refresh(self): @@ -248,6 +262,8 @@ class ManageWindow(QWidget): self.bt_update.setEnabled(enable_bt_update) + self.tray_icon.notify_updates(updates) + def centralize(self): geo = self.frameGeometry() screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) @@ -338,21 +354,24 @@ class ManageWindow(QWidget): if self._acquire_lock(): if self.apps: + to_update = [pak['model']['ref'] for pak in self.apps if pak['visible'] and pak['update_checked']] if to_update: + self.textarea_output.clear() + self.textarea_output.setVisible(True) + self._begin_action(self.locale_keys['manage_window.status.updating'] + '...') self.thread_update.refs_to_update = to_update self.thread_update.start() def _finish_update_selected(self): - self.update_apps(self.thread_update.updated_apps) self.finish_action() - - if self.tray_icon and self.thread_update.updated_apps: - self.tray_icon.notify_updates(len([app for app in self.thread_update.updated_apps if app['update']])) - self._release_lock() + self.refresh(clear_output=False) + + def _update_action_output(self, output: str): + self.textarea_output.appendPlainText(output) def _begin_action(self, action_label: str): self.label_status.setText(action_label) @@ -369,20 +388,25 @@ class ManageWindow(QWidget): # Threaded actions - class UpdateSelectedApps(QThread): - signal = pyqtSignal() + signal_finished = pyqtSignal() + signal_output = pyqtSignal(str) def __init__(self, controller: FlatpakController): super(UpdateSelectedApps, self).__init__() self.controller = controller self.refs_to_update = [] - self.updated_apps = None def run(self): - self.updated_apps = self.controller.update(self.refs_to_update) - self.signal.emit() + + for app_ref in self.refs_to_update: + for output in self.controller.update(app_ref): + line = output.decode().strip() + if line: + self.signal_output.emit(line) + + self.signal_finished.emit() class RefreshApps(QThread): diff --git a/setup.py b/setup.py index 10344000..2de123dc 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ with open(file_dir + '/requirements.txt', 'r') as f: requirements = [line.strip() for line in f.readlines() if line] -with open(file_dir + '/fpakman/core/__init__.py', 'r') as f: +with open(file_dir + '/fpakman/__init__.py', 'r') as f: exec(f.readlines()[0])