Refactoring and improvements (#12)

* Removing controller

* Refactoring FlatpakManager

* Retrieving only installed apps data from the API

* Removing wrong password logs
This commit is contained in:
Vinícius Moreira
2019-06-26 18:32:49 -03:00
committed by GitHub
parent c2e59ba757
commit 97477c4274
15 changed files with 268 additions and 330 deletions

View File

@@ -10,11 +10,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Allows app uninstall and downgrade by right clicking it.
### Improvements
- Performance and memory usage
- Adding tooltips to toolbar buttons
- "Update ?" column renamed to "Upgrade ?"
- Management panel title renamed
- Showing runtime apps when no app is available
- Allowing to specify a custom app translation with the environment variable FPAKMAN_LOCALE
- Allowing to specify a custom app translation with the environment variable **FPAKMAN_LOCALE**
- Adding expiration time for cached app data. Default to 1 hour. The environment variable **FPAKMAN_CACHE_EXPIRATION** can change this value.
- Retrieving only the installed applications data from the origin API.
## [0.2.1] - 2019-06-24
### Features

View File

@@ -43,8 +43,7 @@ You can change some application settings via environment variables:
- **FPAKMAN_UPDATE_NOTIFICATION**: enable or disable system updates notifications. Use **0** (disable) or **1** (enable, default).
- **FPAKMAN_CHECK_INTERVAL**: define the updates check interval in seconds. Default: 60.
- **FPAKMAN_LOCALE**: define a custom app translation for a given locale key (e.g: 'pt', 'en', 'es', ...). Default: system locale.
- **FPAKMAN_CACHE_EXPIRATION**: define a custom expiration time in SECONDS for application data retrieved from the origin API. Default: 3600 (1 hour).
### Roadmap
- Search and install applications
- Uninstall applications
- Downgrade applications
- Search and install applications

View File

@@ -8,8 +8,7 @@ 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.core.controller import FlatpakManager
from fpakman.view.qt.systray import TrayIcon
parser = argparse.ArgumentParser(prog='fpakman', description="GUI for Flatpak applications management")
@@ -20,12 +19,10 @@ locale_keys = util.get_locale_keys(os.getenv('FPAKMAN_LOCALE', None))
app = QApplication(sys.argv)
app.setWindowIcon(QIcon(resource.get_path('img/flathub_45.svg')))
manager = FlatpakManager()
manager.load_database_async()
controller = FlatpakController(manager)
manager = FlatpakManager(cache_expire=int(os.getenv('FPAKMAN_CACHE_EXPIRATION', 60 * 60)))
trayIcon = TrayIcon(locale_keys=locale_keys,
controller=controller,
manager=manager,
check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60)))
trayIcon.show()

View File

@@ -1,25 +1,100 @@
from datetime import datetime, timedelta
from threading import Lock, Thread
from typing import List
from fpakman.core.model import FlatpakManager
import requests
from fpakman.core import flatpak
__FLATHUB_URL__ = 'https://flathub.org'
__FLATHUB_API_URL__ = __FLATHUB_URL__ + '/api/v1'
class FlatpakController:
class FlatpakManager:
def __init__(self, model: FlatpakManager):
self.model = model
def __init__(self, cache_expire: int = 60 * 60):
self.cache_apps = {}
self.cache_expire = cache_expire
self.http_session = requests.Session()
self.lock_db_read = Lock()
self.lock_read = Lock()
def refresh(self) -> List[dict]:
return self.model.read_installed()
# TODO remove if not necessary for future releases
def load_full_database_async(self):
Thread(target=self.load_full_database, daemon=True).start()
def update(self, app_ref: str):
return self.model.update_app(app_ref)
# TODO remove if not necessary for future releases
def load_full_database(self):
def check_installed(self) -> bool:
version = self.model.get_version()
return False if version is None else version
self.lock_db_read.acquire()
def get_version(self) -> str:
return self.model.get_version()
try:
res = self.http_session.get(__FLATHUB_API_URL__ + '/apps')
def uninstall(self, app_ref: str):
return self.model.remove_app(app_ref)
if res.status_code == 200:
for app in res.json():
self.cache_apps[app['flatpakAppId']] = app
finally:
self.lock_db_read.release()
def _request_app_data(self, app_id: str):
try:
res = self.http_session.get('{}/apps/{}'.format(__FLATHUB_API_URL__, app_id), timeout=60)
if res.status_code == 200:
return res.json()
else:
print("Could not retrieve app data for id '{}'. Server response: {}".format(app_id, res.status_code))
except:
print("Could not retrieve app data for id '{}'. Timeout".format(app_id))
return None
def read_installed(self) -> List[dict]:
self.lock_read.acquire()
try:
installed = flatpak.list_installed()
if installed:
installed.sort(key=lambda p: p['name'].lower())
available_updates = flatpak.list_updates_as_str()
for app in installed:
app_data = self.cache_apps.get(app['id'])
if (not app['runtime'] and not app_data) or (app_data and app_data['expires_at'] <= datetime.utcnow()): # if data is not cached or expired, tries to retrieve it
app_data = self._request_app_data(app['id'])
app_data['expires_at'] = datetime.utcnow() + timedelta(seconds=self.cache_expire)
self.cache_apps[app['id']] = app_data
if not app_data:
app['latest_version'] = None
app['icon'] = None
else:
app['latest_version'] = app_data['currentReleaseVersion']
app['icon'] = app_data['iconMobileUrl']
if app['icon'].startswith('/'):
app['icon'] = __FLATHUB_URL__ + app['icon']
app['update'] = app['id'] in available_updates
return installed
finally:
self.lock_read.release()
def downgrade_app(self, app: dict, root_password: str):
commits = flatpak.get_app_commits(app['ref'], app['origin'])
commit_idx = commits.index(app['commit'])
# downgrade is not possible if the app current commit in the first one:
if commit_idx == len(commits) - 1:
return None
return flatpak.downgrade_and_stream(app['ref'], commits[commit_idx + 1], root_password)

View File

@@ -66,6 +66,11 @@ def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_
return data
def is_installed():
version = get_version()
return False if version is None else True
def get_version():
res = system.run_cmd('{} --version'.format(BASE_CMD))
return res.split(' ')[1].strip() if res else None
@@ -86,10 +91,6 @@ def list_installed() -> List[dict]:
return []
def update(app_ref: str) -> bool:
return bool(system.run_cmd('{} update -y {}'.format(BASE_CMD, app_ref)))
def update_and_stream(app_ref: str):
"""
Updates the app reference and streams Flatpak output,

View File

@@ -1,142 +0,0 @@
from threading import Lock, Thread
from typing import List
import requests
from fpakman.core import flatpak
__FLATHUB_URL__ = 'https://flathub.org'
__FLATHUB_API_URL__ = __FLATHUB_URL__ + '/api/v1'
class ImpossibleDowngradeException(Exception):
pass
class FlatpakManager:
def __init__(self):
self.apps = []
self.apps_db = {}
self.http_session = requests.Session()
self.lock_db_read = Lock()
def load_database_async(self):
Thread(target=self.load_database, daemon=True).start()
def load_database(self):
self.lock_db_read.acquire()
try:
res = self.http_session.get(__FLATHUB_API_URL__ + '/apps')
if res.status_code == 200:
for app in res.json():
self.apps_db[app['flatpakAppId']] = app
finally:
self.lock_db_read.release()
def get_version(self):
return flatpak.get_version()
def read_installed(self) -> List[dict]:
installed = flatpak.list_installed()
if installed:
installed.sort(key=lambda p: p['name'].lower())
available_updates = flatpak.list_updates_as_str()
for app in installed:
if not self.apps_db:
self.load_database()
if self.apps_db:
app_data = self.apps_db.get(app['id'], None)
else:
app_data = None
if not app_data:
app['latest_version'] = None
app['icon'] = None
else:
app['latest_version'] = app_data['currentReleaseVersion']
app['icon'] = app_data['iconMobileUrl']
if app['icon'].startswith('/'):
app['icon'] = __FLATHUB_URL__ + app['icon']
app['update'] = app['id'] in available_updates
self.apps = installed
return [*self.apps]
def update_apps(self, refs: List[str]) -> List[dict]:
if self.apps:
for ref in refs:
package_found = [app for app in self.apps if app['ref'] == ref]
if package_found:
package_found = package_found[0]
updated = flatpak.update(ref)
if updated:
package_found['update'] = not updated
return [*self.apps]
return []
def _find_by_ref(self, ref: str):
package_found = [app for app in self.apps if app['ref'] == ref]
if package_found:
return package_found[0]
def update_app(self, ref: str):
"""
:param ref:
:return: the update command stream
"""
if self.apps:
app_found = self._find_by_ref(ref)
if app_found:
return flatpak.update_and_stream(ref)
def remove_app(self, ref: str):
"""
:param ref:
:return: the uninstall command stream
"""
if self.apps:
app_found = self._find_by_ref(ref)
if app_found:
return flatpak.uninstall_and_stream(ref)
def downgrade_app(self, ref: str, root_password: str):
if self.apps:
app_found = self._find_by_ref(ref)
if app_found:
commits = flatpak.get_app_commits(app_found['ref'], app_found['origin'])
commit_idx = commits.index(app_found['commit'])
# downgrade is not possible if the app current commit in the first one:
if commit_idx == len(commits) - 1:
raise ImpossibleDowngradeException()
return flatpak.downgrade_and_stream(app_found['ref'], commits[commit_idx + 1], root_password)

View File

@@ -24,8 +24,8 @@ popup.root.title=Requires root privileges
popup.root.password=Password
popup.root.bad_password.title=Error
popup.root.bad_password.body=Wrong password
popup.downgrade.impossible.title=Impossible to downgrade
popup.downgrade.impossible.body=The app is in its first version
popup.downgrade.impossible.title=Error
popup.downgrade.impossible.body=Impossible to downgrade: the app is in its first version
popup.history.title=History
popup.history.selected.tooltip=Current version
popup.button.yes=Yes

View File

@@ -24,8 +24,8 @@ popup.root.title=Requiere privilegios de root
popup.root.password=Contraseña
popup.root.bad_password.title=Error
popup.root.bad_password.body=Contraseña incorrecta
popup.downgrade.impossible.title=Imposible revertir la versión
popup.downgrade.impossible.body=El aplicativo está en su primera versión
popup.downgrade.impossible.title=Error
popup.downgrade.impossible.body=Imposible revertir la versión: el aplicativo está en su primera versión
popup.history.title=Historia
popup.history.selected.tooltip=Versión actual
popup.button.yes=Sí

View File

@@ -24,8 +24,8 @@ popup.root.title=Requer privilégios de root
popup.root.password=Senha
popup.root.bad_password.title=Erro
popup.root.bad_password.body=Senha incorreta
popup.downgrade.impossible.title=Impossível reverter a versão
popup.downgrade.impossible.body=O aplicativo está na sua primeira versão
popup.downgrade.impossible.title=Erro
popup.downgrade.impossible.body=Impossível reverter a versão: o aplicativo está na sua primeira versão
popup.history.title=Histórico
popup.history.selected.tooltip=Versão atual
popup.button.yes=Sim

View File

@@ -1,14 +1,12 @@
import json
from typing import List
from PyQt5.QtCore import Qt, QUrl, QThread, pyqtSignal
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QPixmap, QIcon, QColor, QCursor
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QMessageBox, QHeaderView, QDialog, QLineEdit, QVBoxLayout, QPlainTextEdit
QHeaderView
from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakController
from fpakman.core import resource
from fpakman.view.qt import dialog
@@ -36,10 +34,9 @@ class UpdateToggleButton(QToolButton):
class AppsTable(QTableWidget):
def __init__(self, parent: QWidget, controller: FlatpakController, columns: List[str]):
def __init__(self, parent: QWidget, columns: List[str]):
super(AppsTable, self).__init__()
self.parent = parent
self.controller = controller
self.columns = columns
self.setColumnCount(len(columns))
self.setFocusPolicy(Qt.NoFocus)
@@ -105,7 +102,7 @@ class AppsTable(QTableWidget):
if dialog.ask_confirmation(title=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app['model']['name']),
locale_keys=self.parent.locale_keys):
self.parent.downgrade_app(selected_app['model']['ref'])
self.parent.downgrade_app(selected_app)
def _get_app_info(self):
self.parent.get_app_info(self.get_selected_app())

View File

@@ -12,12 +12,13 @@ def show_error(title: str, body: str, icon: QIcon = QIcon(resource.get_path('img
if icon:
error_msg.setWindowIcon(icon)
error_msg.exec_()
def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIcon(resource.get_path('img/flathub_45.svg'))):
dialog_confirmation = QMessageBox()
dialog_confirmation.setIcon(QMessageBox.Critical)
dialog_confirmation.setIcon(QMessageBox.Question)
dialog_confirmation.setWindowTitle(title)
dialog_confirmation.setText(body)
bt_yes = dialog_confirmation.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)

View File

@@ -1,3 +1,4 @@
import io
import os
import subprocess
@@ -32,8 +33,13 @@ def ask_root_password(locale_keys: dict):
def validate_password(password: str) -> bool:
f = os.popen('echo {} | sudo -S whoami'.format(password))
res = f.read()
f.close()
proc = subprocess.Popen('echo {} | sudo -S whoami'.format(password),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
bufsize=-1)
stream = os._wrap_close(io.TextIOWrapper(proc.stdout), proc)
res = stream.read()
stream.close()
return bool(res.strip())

View File

@@ -4,10 +4,9 @@ import time
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from fpakman.core.model import FlatpakManager
from fpakman.core.controller import FlatpakManager
from fpakman.core import resource
from fpakman.core.controller import FlatpakController
from fpakman.view.qt.window import ManageWindow
@@ -15,16 +14,16 @@ class UpdateCheck(QThread):
signal = pyqtSignal(int)
def __init__(self, check_interval: int, controller: FlatpakController, parent=None):
def __init__(self, manager: FlatpakManager, check_interval: int, parent=None):
super(UpdateCheck, self).__init__(parent)
self.controller = controller
self.check_interval = check_interval
self.manager = manager
def run(self):
while True:
apps = self.controller.refresh()
apps = self.manager.read_installed()
updates = len([app for app in apps if app['update']])
@@ -35,10 +34,9 @@ class UpdateCheck(QThread):
class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, check_interval: int = 60):
def __init__(self, locale_keys: dict, manager: FlatpakManager, check_interval: int = 60):
super(TrayIcon, self).__init__()
self.locale_keys = locale_keys
self.controller = controller
self.manager = manager
self.icon_default = QIcon(resource.get_path('img/flathub_45.svg'))
@@ -52,8 +50,8 @@ class TrayIcon(QSystemTrayIcon):
self.action_exit.triggered.connect(lambda: QCoreApplication.exit())
self.setContextMenu(self.menu)
self.manage_window = ManageWindow(locale_keys=self.locale_keys, controller=controller, manager=self.manager, tray_icon=self)
self.check_thread = UpdateCheck(check_interval=check_interval, controller=self.controller)
self.manage_window = ManageWindow(locale_keys=self.locale_keys, manager=self.manager, tray_icon=self)
self.check_thread = UpdateCheck(check_interval=check_interval, manager=self.manager)
self.check_thread.signal.connect(self.notify_updates)
self.check_thread.start()
@@ -78,7 +76,9 @@ class TrayIcon(QSystemTrayIcon):
def show_manage_window(self):
if not self.manage_window:
self.manage_window = ManageWindow(controller=self.controller)
self.manage_window = ManageWindow(locale_keys=self.locale_keys,
manager=self.manager,
tray_icon=self)
self.manage_window.refresh()
self.manage_window.show()

115
fpakman/view/qt/thread.py Normal file
View File

@@ -0,0 +1,115 @@
from PyQt5.QtCore import QThread, pyqtSignal
from fpakman.core import flatpak
from fpakman.core.controller import FlatpakManager
from fpakman.view.qt import dialog
class UpdateSelectedApps(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self):
super(UpdateSelectedApps, self).__init__()
self.refs_to_update = []
def run(self):
for app_ref in self.refs_to_update:
for output in flatpak.update_and_stream(app_ref):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.signal_finished.emit()
class RefreshApps(QThread):
signal = pyqtSignal(list)
def __init__(self, manager: FlatpakManager):
super(RefreshApps, self).__init__()
self.manager = manager
def run(self):
self.signal.emit(self.manager.read_installed())
class UninstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self):
super(UninstallApp, self).__init__()
self.app_ref = None
def run(self):
if self.app_ref:
for output in flatpak.uninstall_and_stream(self.app_ref):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.signal_finished.emit()
class DowngradeApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, manager: FlatpakManager, locale_keys: dict):
super(DowngradeApp, self).__init__()
self.manager = manager
self.app = None
self.root_password = None
self.locale_keys = locale_keys
def run(self):
if self.app:
stream = self.manager.downgrade_app(self.app['model'], self.root_password)
if stream is None:
dialog.show_error(title=self.locale_keys['popup.downgrade.impossible.title'],
body=self.locale_keys['popup.downgrade.impossible.body'])
else:
for output in self.manager.downgrade_app(self.app['model'], self.root_password):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.app = None
self.root_password = None
self.signal_finished.emit()
class GetAppInfo(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppInfo, self).__init__()
self.app = None
def run(self):
if self.app:
app_info = flatpak.get_app_info_fields(self.app['model']['id'], self.app['model']['branch'])
app_info['name'] = self.app['model']['name']
app_info['type'] = 'runtime' if self.app['model']['runtime'] else 'app'
self.signal_finished.emit(app_info)
self.app = None
class GetAppHistory(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppHistory, self).__init__()
self.app = None
def run(self):
if self.app:
commits = flatpak.get_app_commits_data(self.app['model']['ref'], self.app['model']['origin'])
self.signal_finished.emit({'model': self.app['model'], 'commits': commits})
self.app = None

View File

@@ -1,30 +1,30 @@
import json
import operator
from functools import reduce
from threading import Lock
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal, QEvent, Qt
from PyQt5.QtCore import QEvent
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QPlainTextEdit, QDialog, QGroupBox, QFormLayout, QLineEdit, QTableWidget, QTableWidgetItem
QSizePolicy, QLabel, QPlainTextEdit
from fpakman import __version__
from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakController
from fpakman.core.model import FlatpakManager, ImpossibleDowngradeException
from fpakman.core.controller import FlatpakManager
from fpakman.view.qt import dialog
from fpakman.view.qt.apps_table import AppsTable
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
class ManageWindow(QWidget):
__BASE_HEIGHT__ = 400
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, tray_icon=None):
def __init__(self, locale_keys: dict, manager: FlatpakManager, tray_icon=None):
super(ManageWindow, self).__init__()
self.locale_keys = locale_keys
self.column_names = [locale_keys[key].capitalize() for key in ['flatpak.info.name',
@@ -35,7 +35,6 @@ class ManageWindow(QWidget):
'flatpak.info.ref',
'flatpak.info.origin',
'manage_window.columns.update']]
self.controller = controller
self.manager = manager
self.tray_icon = tray_icon
self.thread_lock = Lock()
@@ -88,7 +87,7 @@ class ManageWindow(QWidget):
self.layout.addWidget(toolbar)
self.table_apps = AppsTable(self, self.controller, self.column_names)
self.table_apps = AppsTable(self, self.column_names)
self.table_apps.change_headers_policy()
self.layout.addWidget(self.table_apps)
@@ -100,18 +99,18 @@ class ManageWindow(QWidget):
self.textarea_output.setVisible(False)
self.textarea_output.setReadOnly(True)
self.thread_update = UpdateSelectedApps(self.controller)
self.thread_update = UpdateSelectedApps()
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 = RefreshApps(self.manager)
self.thread_refresh.signal.connect(self._finish_refresh)
self.thread_uninstall = UninstallApp(self.controller)
self.thread_uninstall = UninstallApp()
self.thread_uninstall.signal_output.connect(self._update_action_output)
self.thread_uninstall.signal_finished.connect(self._finish_uninstall)
self.thread_downgrade = DowngradeApp(self.manager)
self.thread_downgrade = DowngradeApp(self.manager, self.locale_keys)
self.thread_downgrade.signal_output.connect(self._update_action_output)
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
@@ -148,7 +147,7 @@ class ManageWindow(QWidget):
def _check_flatpak_installed(self):
if not self.controller.check_installed():
if not flatpak.is_installed():
dialog.show_error(title=self.locale_keys['popup.flatpak_not_installed.title'],
body=self.locale_keys['popup.flatpak_not_installed.msg'] + '...',
icon=self.icon_flathub)
@@ -158,7 +157,7 @@ class ManageWindow(QWidget):
self.label_flatpak.setText(self._get_flatpak_label())
def _get_flatpak_label(self):
return 'flatpak: ' + self.controller.get_version()
return 'flatpak: ' + flatpak.get_version()
def _acquire_lock(self):
@@ -191,9 +190,9 @@ class ManageWindow(QWidget):
self.thread_refresh.start()
def _finish_refresh(self):
def _finish_refresh(self, apps: List[dict]):
self.update_apps(self.thread_refresh.apps)
self.update_apps(apps)
self.finish_action()
self._release_lock()
@@ -337,7 +336,7 @@ class ManageWindow(QWidget):
self.table_apps.setEnabled(True)
self.label_status.setText('')
def downgrade_app(self, app_ref: str):
def downgrade_app(self, app: dict):
self._check_flatpak_installed()
@@ -356,7 +355,7 @@ class ManageWindow(QWidget):
self.textarea_output.setVisible(True)
self._begin_action(self.locale_keys['manage_window.status.downgrading'])
self.thread_downgrade.app_ref = app_ref
self.thread_downgrade.app = app
self.thread_downgrade.root_password = pwd
self.thread_downgrade.start()
@@ -391,116 +390,3 @@ class ManageWindow(QWidget):
dialog_history = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys)
dialog_history.exec_()
# Threaded actions
class UpdateSelectedApps(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, controller: FlatpakController):
super(UpdateSelectedApps, self).__init__()
self.controller = controller
self.refs_to_update = []
def run(self):
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):
signal = pyqtSignal()
def __init__(self, controller: FlatpakController):
super(RefreshApps, self).__init__()
self.controller = controller
self.apps = None
def run(self):
self.apps = self.controller.refresh()
self.signal.emit()
class UninstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, controller: FlatpakController):
super(UninstallApp, self).__init__()
self.controller = controller
self.app_ref = None
def run(self):
if self.app_ref:
for output in self.controller.uninstall(self.app_ref):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.signal_finished.emit()
class DowngradeApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, manager: FlatpakManager):
super(DowngradeApp, self).__init__()
self.manager = manager
self.app_ref = None
self.root_password = None
def run(self):
if self.app_ref:
try:
for output in self.manager.downgrade_app(self.app_ref, self.root_password):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
except ImpossibleDowngradeException:
dialog.show_error(title=self.locale_keys['popup.downgrade.impossible.title'],
body=self.locale_keys['popup.downgrade.impossible.body'],
icon=self.icon_flathub)
self.app_ref = None
self.root_password = None
self.signal_finished.emit()
class GetAppInfo(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppInfo, self).__init__()
self.app = None
def run(self):
if self.app:
app_info = flatpak.get_app_info_fields(self.app['model']['id'], self.app['model']['branch'])
app_info['name'] = self.app['model']['name']
app_info['type'] = 'runtime' if self.app['model']['runtime'] else 'app'
self.signal_finished.emit(app_info)
self.app = None
class GetAppHistory(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppHistory, self).__init__()
self.app = None
def run(self):
if self.app:
commits = flatpak.get_app_commits_data(self.app['model']['ref'], self.app['model']['origin'])
self.signal_finished.emit({'model': self.app['model'], 'commits': commits})
self.app = None