mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-08 10:54:15 +02:00
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:
@@ -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.
|
- Allows app uninstall and downgrade by right clicking it.
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
- Performance and memory usage
|
||||||
- Adding tooltips to toolbar buttons
|
- Adding tooltips to toolbar buttons
|
||||||
- "Update ?" column renamed to "Upgrade ?"
|
- "Update ?" column renamed to "Upgrade ?"
|
||||||
- Management panel title renamed
|
- Management panel title renamed
|
||||||
- Showing runtime apps when no app is available
|
- 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
|
## [0.2.1] - 2019-06-24
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -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_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_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_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
|
### Roadmap
|
||||||
- Search and install applications
|
- Search and install applications
|
||||||
- Uninstall applications
|
|
||||||
- Downgrade applications
|
|
||||||
@@ -8,8 +8,7 @@ from PyQt5.QtWidgets import QApplication
|
|||||||
from fpakman import __version__
|
from fpakman import __version__
|
||||||
from fpakman.core import resource
|
from fpakman.core import resource
|
||||||
from fpakman.core import util
|
from fpakman.core import util
|
||||||
from fpakman.core.controller import FlatpakController
|
from fpakman.core.controller import FlatpakManager
|
||||||
from fpakman.core.model import FlatpakManager
|
|
||||||
from fpakman.view.qt.systray import TrayIcon
|
from fpakman.view.qt.systray import TrayIcon
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog='fpakman', description="GUI for Flatpak applications management")
|
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 = QApplication(sys.argv)
|
||||||
app.setWindowIcon(QIcon(resource.get_path('img/flathub_45.svg')))
|
app.setWindowIcon(QIcon(resource.get_path('img/flathub_45.svg')))
|
||||||
manager = FlatpakManager()
|
|
||||||
manager.load_database_async()
|
manager = FlatpakManager(cache_expire=int(os.getenv('FPAKMAN_CACHE_EXPIRATION', 60 * 60)))
|
||||||
controller = FlatpakController(manager)
|
|
||||||
|
|
||||||
trayIcon = TrayIcon(locale_keys=locale_keys,
|
trayIcon = TrayIcon(locale_keys=locale_keys,
|
||||||
controller=controller,
|
|
||||||
manager=manager,
|
manager=manager,
|
||||||
check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60)))
|
check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60)))
|
||||||
trayIcon.show()
|
trayIcon.show()
|
||||||
|
|||||||
@@ -1,25 +1,100 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
from threading import Lock, Thread
|
||||||
from typing import List
|
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):
|
def __init__(self, cache_expire: int = 60 * 60):
|
||||||
self.model = model
|
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]:
|
# TODO remove if not necessary for future releases
|
||||||
return self.model.read_installed()
|
def load_full_database_async(self):
|
||||||
|
Thread(target=self.load_full_database, daemon=True).start()
|
||||||
|
|
||||||
def update(self, app_ref: str):
|
# TODO remove if not necessary for future releases
|
||||||
return self.model.update_app(app_ref)
|
def load_full_database(self):
|
||||||
|
|
||||||
def check_installed(self) -> bool:
|
self.lock_db_read.acquire()
|
||||||
version = self.model.get_version()
|
|
||||||
return False if version is None else version
|
|
||||||
|
|
||||||
def get_version(self) -> str:
|
try:
|
||||||
return self.model.get_version()
|
res = self.http_session.get(__FLATHUB_API_URL__ + '/apps')
|
||||||
|
|
||||||
def uninstall(self, app_ref: str):
|
if res.status_code == 200:
|
||||||
return self.model.remove_app(app_ref)
|
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)
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def is_installed():
|
||||||
|
version = get_version()
|
||||||
|
return False if version is None else True
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
res = system.run_cmd('{} --version'.format(BASE_CMD))
|
res = system.run_cmd('{} --version'.format(BASE_CMD))
|
||||||
return res.split(' ')[1].strip() if res else None
|
return res.split(' ')[1].strip() if res else None
|
||||||
@@ -86,10 +91,6 @@ def list_installed() -> List[dict]:
|
|||||||
return []
|
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):
|
def update_and_stream(app_ref: str):
|
||||||
"""
|
"""
|
||||||
Updates the app reference and streams Flatpak output,
|
Updates the app reference and streams Flatpak output,
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -24,8 +24,8 @@ popup.root.title=Requires root privileges
|
|||||||
popup.root.password=Password
|
popup.root.password=Password
|
||||||
popup.root.bad_password.title=Error
|
popup.root.bad_password.title=Error
|
||||||
popup.root.bad_password.body=Wrong password
|
popup.root.bad_password.body=Wrong password
|
||||||
popup.downgrade.impossible.title=Impossible to downgrade
|
popup.downgrade.impossible.title=Error
|
||||||
popup.downgrade.impossible.body=The app is in its first version
|
popup.downgrade.impossible.body=Impossible to downgrade: the app is in its first version
|
||||||
popup.history.title=History
|
popup.history.title=History
|
||||||
popup.history.selected.tooltip=Current version
|
popup.history.selected.tooltip=Current version
|
||||||
popup.button.yes=Yes
|
popup.button.yes=Yes
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ popup.root.title=Requiere privilegios de root
|
|||||||
popup.root.password=Contraseña
|
popup.root.password=Contraseña
|
||||||
popup.root.bad_password.title=Error
|
popup.root.bad_password.title=Error
|
||||||
popup.root.bad_password.body=Contraseña incorrecta
|
popup.root.bad_password.body=Contraseña incorrecta
|
||||||
popup.downgrade.impossible.title=Imposible revertir la versión
|
popup.downgrade.impossible.title=Error
|
||||||
popup.downgrade.impossible.body=El aplicativo está en su primera versión
|
popup.downgrade.impossible.body=Imposible revertir la versión: el aplicativo está en su primera versión
|
||||||
popup.history.title=Historia
|
popup.history.title=Historia
|
||||||
popup.history.selected.tooltip=Versión actual
|
popup.history.selected.tooltip=Versión actual
|
||||||
popup.button.yes=Sí
|
popup.button.yes=Sí
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ popup.root.title=Requer privilégios de root
|
|||||||
popup.root.password=Senha
|
popup.root.password=Senha
|
||||||
popup.root.bad_password.title=Erro
|
popup.root.bad_password.title=Erro
|
||||||
popup.root.bad_password.body=Senha incorreta
|
popup.root.bad_password.body=Senha incorreta
|
||||||
popup.downgrade.impossible.title=Impossível reverter a versão
|
popup.downgrade.impossible.title=Erro
|
||||||
popup.downgrade.impossible.body=O aplicativo está na sua primeira versão
|
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.title=Histórico
|
||||||
popup.history.selected.tooltip=Versão atual
|
popup.history.selected.tooltip=Versão atual
|
||||||
popup.button.yes=Sim
|
popup.button.yes=Sim
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import json
|
|
||||||
from typing import List
|
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.QtGui import QPixmap, QIcon, QColor, QCursor
|
||||||
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
|
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
|
||||||
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
|
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 import resource
|
||||||
from fpakman.core.controller import FlatpakController
|
|
||||||
from fpakman.view.qt import dialog
|
from fpakman.view.qt import dialog
|
||||||
|
|
||||||
|
|
||||||
@@ -36,10 +34,9 @@ class UpdateToggleButton(QToolButton):
|
|||||||
|
|
||||||
class AppsTable(QTableWidget):
|
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__()
|
super(AppsTable, self).__init__()
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.controller = controller
|
|
||||||
self.columns = columns
|
self.columns = columns
|
||||||
self.setColumnCount(len(columns))
|
self.setColumnCount(len(columns))
|
||||||
self.setFocusPolicy(Qt.NoFocus)
|
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'],
|
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']),
|
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):
|
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):
|
def _get_app_info(self):
|
||||||
self.parent.get_app_info(self.get_selected_app())
|
self.parent.get_app_info(self.get_selected_app())
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ def show_error(title: str, body: str, icon: QIcon = QIcon(resource.get_path('img
|
|||||||
|
|
||||||
if icon:
|
if icon:
|
||||||
error_msg.setWindowIcon(icon)
|
error_msg.setWindowIcon(icon)
|
||||||
|
|
||||||
error_msg.exec_()
|
error_msg.exec_()
|
||||||
|
|
||||||
|
|
||||||
def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIcon(resource.get_path('img/flathub_45.svg'))):
|
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 = QMessageBox()
|
||||||
dialog_confirmation.setIcon(QMessageBox.Critical)
|
dialog_confirmation.setIcon(QMessageBox.Question)
|
||||||
dialog_confirmation.setWindowTitle(title)
|
dialog_confirmation.setWindowTitle(title)
|
||||||
dialog_confirmation.setText(body)
|
dialog_confirmation.setText(body)
|
||||||
bt_yes = dialog_confirmation.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)
|
bt_yes = dialog_confirmation.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@@ -32,8 +33,13 @@ def ask_root_password(locale_keys: dict):
|
|||||||
|
|
||||||
|
|
||||||
def validate_password(password: str) -> bool:
|
def validate_password(password: str) -> bool:
|
||||||
f = os.popen('echo {} | sudo -S whoami'.format(password))
|
proc = subprocess.Popen('echo {} | sudo -S whoami'.format(password),
|
||||||
res = f.read()
|
shell=True,
|
||||||
f.close()
|
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())
|
return bool(res.strip())
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import time
|
|||||||
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication
|
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
|
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 import resource
|
||||||
from fpakman.core.controller import FlatpakController
|
|
||||||
from fpakman.view.qt.window import ManageWindow
|
from fpakman.view.qt.window import ManageWindow
|
||||||
|
|
||||||
|
|
||||||
@@ -15,16 +14,16 @@ class UpdateCheck(QThread):
|
|||||||
|
|
||||||
signal = pyqtSignal(int)
|
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)
|
super(UpdateCheck, self).__init__(parent)
|
||||||
self.controller = controller
|
|
||||||
self.check_interval = check_interval
|
self.check_interval = check_interval
|
||||||
|
self.manager = manager
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
apps = self.controller.refresh()
|
apps = self.manager.read_installed()
|
||||||
|
|
||||||
updates = len([app for app in apps if app['update']])
|
updates = len([app for app in apps if app['update']])
|
||||||
|
|
||||||
@@ -35,10 +34,9 @@ class UpdateCheck(QThread):
|
|||||||
|
|
||||||
class TrayIcon(QSystemTrayIcon):
|
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__()
|
super(TrayIcon, self).__init__()
|
||||||
self.locale_keys = locale_keys
|
self.locale_keys = locale_keys
|
||||||
self.controller = controller
|
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
|
|
||||||
self.icon_default = QIcon(resource.get_path('img/flathub_45.svg'))
|
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.action_exit.triggered.connect(lambda: QCoreApplication.exit())
|
||||||
self.setContextMenu(self.menu)
|
self.setContextMenu(self.menu)
|
||||||
|
|
||||||
self.manage_window = ManageWindow(locale_keys=self.locale_keys, controller=controller, manager=self.manager, tray_icon=self)
|
self.manage_window = ManageWindow(locale_keys=self.locale_keys, manager=self.manager, tray_icon=self)
|
||||||
self.check_thread = UpdateCheck(check_interval=check_interval, controller=self.controller)
|
self.check_thread = UpdateCheck(check_interval=check_interval, manager=self.manager)
|
||||||
self.check_thread.signal.connect(self.notify_updates)
|
self.check_thread.signal.connect(self.notify_updates)
|
||||||
self.check_thread.start()
|
self.check_thread.start()
|
||||||
|
|
||||||
@@ -78,7 +76,9 @@ class TrayIcon(QSystemTrayIcon):
|
|||||||
def show_manage_window(self):
|
def show_manage_window(self):
|
||||||
|
|
||||||
if not self.manage_window:
|
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.refresh()
|
||||||
self.manage_window.show()
|
self.manage_window.show()
|
||||||
|
|||||||
115
fpakman/view/qt/thread.py
Normal file
115
fpakman/view/qt/thread.py
Normal 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
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import json
|
|
||||||
import operator
|
import operator
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import List
|
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.QtGui import QIcon, QWindowStateChangeEvent
|
||||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
|
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 import __version__
|
||||||
from fpakman.core import resource, flatpak
|
from fpakman.core import resource, flatpak
|
||||||
from fpakman.core.controller import FlatpakController
|
from fpakman.core.controller import FlatpakManager
|
||||||
from fpakman.core.model import FlatpakManager, ImpossibleDowngradeException
|
|
||||||
from fpakman.view.qt import dialog
|
from fpakman.view.qt import dialog
|
||||||
from fpakman.view.qt.apps_table import AppsTable
|
from fpakman.view.qt.apps_table import AppsTable
|
||||||
from fpakman.view.qt.history import HistoryDialog
|
from fpakman.view.qt.history import HistoryDialog
|
||||||
from fpakman.view.qt.info import InfoDialog
|
from fpakman.view.qt.info import InfoDialog
|
||||||
from fpakman.view.qt.root import is_root, ask_root_password
|
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):
|
class ManageWindow(QWidget):
|
||||||
|
|
||||||
__BASE_HEIGHT__ = 400
|
__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__()
|
super(ManageWindow, self).__init__()
|
||||||
self.locale_keys = locale_keys
|
self.locale_keys = locale_keys
|
||||||
self.column_names = [locale_keys[key].capitalize() for key in ['flatpak.info.name',
|
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.ref',
|
||||||
'flatpak.info.origin',
|
'flatpak.info.origin',
|
||||||
'manage_window.columns.update']]
|
'manage_window.columns.update']]
|
||||||
self.controller = controller
|
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.tray_icon = tray_icon
|
self.tray_icon = tray_icon
|
||||||
self.thread_lock = Lock()
|
self.thread_lock = Lock()
|
||||||
@@ -88,7 +87,7 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
self.layout.addWidget(toolbar)
|
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.table_apps.change_headers_policy()
|
||||||
|
|
||||||
self.layout.addWidget(self.table_apps)
|
self.layout.addWidget(self.table_apps)
|
||||||
@@ -100,18 +99,18 @@ class ManageWindow(QWidget):
|
|||||||
self.textarea_output.setVisible(False)
|
self.textarea_output.setVisible(False)
|
||||||
self.textarea_output.setReadOnly(True)
|
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_output.connect(self._update_action_output)
|
||||||
self.thread_update.signal_finished.connect(self._finish_update_selected)
|
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_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_output.connect(self._update_action_output)
|
||||||
self.thread_uninstall.signal_finished.connect(self._finish_uninstall)
|
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_output.connect(self._update_action_output)
|
||||||
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
|
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
|
||||||
|
|
||||||
@@ -148,7 +147,7 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
def _check_flatpak_installed(self):
|
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'],
|
dialog.show_error(title=self.locale_keys['popup.flatpak_not_installed.title'],
|
||||||
body=self.locale_keys['popup.flatpak_not_installed.msg'] + '...',
|
body=self.locale_keys['popup.flatpak_not_installed.msg'] + '...',
|
||||||
icon=self.icon_flathub)
|
icon=self.icon_flathub)
|
||||||
@@ -158,7 +157,7 @@ class ManageWindow(QWidget):
|
|||||||
self.label_flatpak.setText(self._get_flatpak_label())
|
self.label_flatpak.setText(self._get_flatpak_label())
|
||||||
|
|
||||||
def _get_flatpak_label(self):
|
def _get_flatpak_label(self):
|
||||||
return 'flatpak: ' + self.controller.get_version()
|
return 'flatpak: ' + flatpak.get_version()
|
||||||
|
|
||||||
def _acquire_lock(self):
|
def _acquire_lock(self):
|
||||||
|
|
||||||
@@ -191,9 +190,9 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
self.thread_refresh.start()
|
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.finish_action()
|
||||||
self._release_lock()
|
self._release_lock()
|
||||||
|
|
||||||
@@ -337,7 +336,7 @@ class ManageWindow(QWidget):
|
|||||||
self.table_apps.setEnabled(True)
|
self.table_apps.setEnabled(True)
|
||||||
self.label_status.setText('')
|
self.label_status.setText('')
|
||||||
|
|
||||||
def downgrade_app(self, app_ref: str):
|
def downgrade_app(self, app: dict):
|
||||||
|
|
||||||
self._check_flatpak_installed()
|
self._check_flatpak_installed()
|
||||||
|
|
||||||
@@ -356,7 +355,7 @@ class ManageWindow(QWidget):
|
|||||||
self.textarea_output.setVisible(True)
|
self.textarea_output.setVisible(True)
|
||||||
self._begin_action(self.locale_keys['manage_window.status.downgrading'])
|
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.root_password = pwd
|
||||||
self.thread_downgrade.start()
|
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 = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys)
|
||||||
dialog_history.exec_()
|
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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user