improving initialization time and reducing async overload

This commit is contained in:
Vinícius Moreira
2019-07-08 12:15:15 -03:00
committed by GitHub
parent 735e590756
commit 74fa85a2bb
18 changed files with 577 additions and 240 deletions

View File

@@ -1,20 +1,24 @@
from threading import Lock
from typing import List
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QPixmap, QIcon, QColor, QCursor
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QHeaderView, QLabel
from fpakman.core import resource, util
from fpakman.core import resource
from fpakman.core.model import FlatpakApplication, ApplicationStatus
from fpakman.util.cache import Cache
from fpakman.view.qt import dialog
from fpakman.view.qt.view_model import ApplicationView, ApplicationViewStatus
class UpdateToggleButton(QToolButton):
def __init__(self, model: dict, root: QWidget, locale_keys: dict, checked: bool = True):
def __init__(self, app_view: ApplicationView, root: QWidget, locale_keys: dict, checked: bool = True):
super(UpdateToggleButton, self).__init__()
self.app = model
self.app_view = app_view
self.root = root
self.setCheckable(True)
self.clicked.connect(self.change_state)
@@ -28,14 +32,14 @@ class UpdateToggleButton(QToolButton):
self.click()
def change_state(self, not_checked: bool):
self.app['update_checked'] = not not_checked
self.app_view.updated_checked = not not_checked
self.setIcon(self.icon_on if not not_checked else self.icon_off)
self.root.change_update_state()
class AppsTable(QTableWidget):
def __init__(self, parent: QWidget):
def __init__(self, parent: QWidget, icon_cache: Cache):
super(AppsTable, self).__init__()
self.setParent(parent)
self.window = parent
@@ -44,7 +48,7 @@ class AppsTable(QTableWidget):
'manage_window.columns.latest_version',
'flatpak.info.branch',
'flatpak.info.arch',
'flatpak.info.id',
'flatpak.info.description',
'flatpak.info.origin',
'manage_window.columns.installed',
'manage_window.columns.update']]
@@ -60,7 +64,8 @@ class AppsTable(QTableWidget):
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon)
self.icon_cache = {}
self.icon_cache = icon_cache
self.lock_async_data = Lock()
def contextMenuEvent(self, QContextMenuEvent): # selected row right click event
@@ -68,7 +73,7 @@ class AppsTable(QTableWidget):
menu_row = QMenu()
if app['model']['installed']:
if app.model.installed:
action_info = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.info"])
action_info.setIcon(QIcon(resource.get_path('img/info.svg')))
action_info.triggered.connect(self._get_app_info)
@@ -84,7 +89,7 @@ class AppsTable(QTableWidget):
action_uninstall.triggered.connect(self._uninstall_app)
menu_row.addAction(action_uninstall)
if not app['model']['runtime']: # not available for runtimes
if isinstance(app.model, FlatpakApplication) and not app.model.runtime: # not available for runtimes
action_downgrade = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.downgrade"])
action_downgrade.triggered.connect(self._downgrade_app)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
@@ -99,25 +104,54 @@ class AppsTable(QTableWidget):
menu_row.popup(QCursor.pos())
menu_row.exec_()
def get_selected_app(self):
def fill_async_data(self):
self.lock_async_data.acquire()
if self.window.apps:
for idx, app_v in enumerate(self.window.apps):
if app_v.visible and app_v.status == ApplicationViewStatus.LOADING and app_v.model.status == ApplicationStatus.READY:
self.network_man.get(QNetworkRequest(QUrl(app_v.model.base_data.icon_url)))
self.item(idx, 2).setText(app_v.model.base_data.latest_version)
desc = app_v.get_async_attr('description', strip_html=True)
self.item(idx, 5).setText(desc)
self.item(idx, 5).setToolTip(desc)
app_v.status = ApplicationViewStatus.READY
visible, ready = 0, 0
for app_v in self.window.apps:
if app_v.visible:
visible += 1
if app_v.status == ApplicationViewStatus.READY:
ready += 1
if ready == visible:
self.window.resize_and_center()
self.lock_async_data.release()
def get_selected_app(self) -> ApplicationView:
return self.window.apps[self.currentRow()]
def get_selected_app_icon(self):
def get_selected_app_icon(self) -> QIcon:
return self.item(self.currentRow(), 0).icon()
def _uninstall_app(self):
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app['model']['name']),
body=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app.model.base_data.name),
locale_keys=self.window.locale_keys):
self.window.uninstall_app(selected_app['model']['ref'])
self.window.uninstall_app(selected_app)
def _downgrade_app(self):
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app['model']['name']),
body=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app.model.base_data.name),
locale_keys=self.window.locale_keys):
self.window.downgrade_app(selected_app)
@@ -132,94 +166,92 @@ class AppsTable(QTableWidget):
def _load_icon(self, http_response):
icon_url = http_response.url().toString()
pixmap = QPixmap()
pixmap.loadFromData(http_response.readAll())
icon = QIcon(pixmap)
self.icon_cache[icon_url] = icon
for idx, app in enumerate(self.window.apps):
if app['model']['icon'] == icon_url:
self.item(idx, 0).setIcon(icon)
self.window.resize_and_center()
break
if not self.icon_cache.get(icon_url):
pixmap = QPixmap()
pixmap.loadFromData(http_response.readAll())
icon = QIcon(pixmap)
self.icon_cache.add(icon_url, icon)
def update_apps(self, apps: List[dict]):
for idx, app in enumerate(self.window.apps):
if app.model.base_data.icon_url == icon_url:
self.item(idx, 0).setIcon(icon)
break
def update_apps(self, app_views: List[ApplicationView]):
self.setEnabled(True)
self.setRowCount(len(apps) if apps else 0)
self.setRowCount(len(app_views) if app_views else 0)
if apps:
for idx, app in enumerate(apps):
tooltip = util.strip_html(app['model']['description']) if app['model']['description'] else None
if app_views:
for idx, app_v in enumerate(app_views):
col_name = QTableWidgetItem()
col_name.setText(app['model']['name'])
col_name.setText(app_v.model.base_data.name)
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_name.setToolTip(tooltip)
if not app['model']['icon']:
if not app_v.model.base_data.icon_url:
col_name.setIcon(self.icon_flathub)
else:
cached_icon = self.icon_cache.get(app['model']['icon'])
cached_icon = self.icon_cache.get(app_v.model.base_data.icon_url)
if cached_icon:
col_name.setIcon(cached_icon)
else:
col_name.setIcon(self.icon_flathub)
self.network_man.get(QNetworkRequest(QUrl(app['model']['icon'])))
self.setItem(idx, 0, col_name)
col_version = QTableWidgetItem()
col_version.setText(app['model']['version'])
col_version.setText(app_v.model.base_data.version)
col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_version.setToolTip(tooltip)
self.setItem(idx, 1, col_version)
col_release = QTableWidgetItem()
col_release.setText(app['model']['latest_version'])
col_release.setText(app_v.get_async_attr('latest_version'))
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_release.setToolTip(tooltip)
self.setItem(idx, 2, col_release)
if app['model']['version'] and app['model']['latest_version'] and app['model']['version'] < app['model']['latest_version']:
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:
col_release.setForeground(QColor('orange'))
col_branch = QTableWidgetItem()
col_branch.setText(app['model']['branch'])
col_branch.setText(app_v.model.branch if isinstance(app_v.model, FlatpakApplication) else '')
col_branch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_branch.setToolTip(tooltip)
self.setItem(idx, 3, col_branch)
col_arch = QTableWidgetItem()
col_arch.setText(app['model']['arch'])
col_arch.setText(app_v.model.arch if isinstance(app_v.model, FlatpakApplication) else '')
col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_arch.setToolTip(tooltip)
self.setItem(idx, 4, col_arch)
col_id = QTableWidgetItem()
col_id.setText(app['model']['id'])
col_id.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_id.setToolTip(tooltip)
self.setItem(idx, 5, col_id)
desc = app_v.get_async_attr('description', strip_html=True)
col_description = QTableWidgetItem()
col_description.setText(desc)
col_description.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if app_v.model.status == ApplicationStatus.READY:
col_description.setToolTip(desc)
self.setItem(idx, 5, col_description)
col_origin = QTableWidgetItem()
col_origin.setText(app['model']['origin'])
col_origin.setText(app_v.model.origin if isinstance(app_v.model, FlatpakApplication) else '')
col_origin.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_origin.setToolTip(tooltip)
self.setItem(idx, 6, col_origin)
col_installed = QLabel()
col_installed.setPixmap((QPixmap(resource.get_path('img/{}.svg'.format('checked' if app['model']['installed'] else 'red_cross')))))
col_installed.setToolTip(tooltip)
col_installed.setPixmap((QPixmap(resource.get_path('img/{}.svg'.format('checked' if app_v.model.installed else 'red_cross')))))
col_installed.setAlignment(Qt.AlignCenter)
self.setCellWidget(idx, 7, col_installed)
col_update = UpdateToggleButton(app, self.window, self.window.locale_keys, app['model']['update']) if app['model']['update'] else None
col_update = UpdateToggleButton(app_v, self.window, self.window.locale_keys, app_v.model.update) if app_v.model.update else None
self.setCellWidget(idx, 8, col_update)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
header_horizontal = self.horizontalHeader()
for i in range(self.columnCount()):
header_horizontal.setSectionResizeMode(i, policy)
if i == 5:
header_horizontal.setSectionResizeMode(i, QHeaderView.Stretch)
else:
header_horizontal.setSectionResizeMode(i, policy)

View File

@@ -11,7 +11,7 @@ class HistoryDialog(QDialog):
def __init__(self, app: dict, app_icon: QIcon, locale_keys: dict):
super(HistoryDialog, self).__init__()
self.setWindowTitle('{} - {} '.format(locale_keys['popup.history.title'], app['model']['name']))
self.setWindowTitle('{} - {} '.format(locale_keys['popup.history.title'], app['model'].base_data.name))
self.setWindowIcon(app_icon)
layout = QVBoxLayout()
@@ -29,7 +29,7 @@ class HistoryDialog(QDialog):
for row, commit in enumerate(app['commits']):
current_app_commit = app['model']['commit'] == commit['commit']
current_app_commit = app['model'].commit == commit['commit']
for col, key in enumerate(sorted(commit.keys())):
item = QTableWidgetItem()

View File

@@ -1,4 +1,5 @@
import time
from threading import Lock
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication, Qt
@@ -7,6 +8,8 @@ from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from fpakman.core import resource, system
from fpakman.core.controller import FlatpakManager
from fpakman.core.model import Application
from fpakman.util.cache import Cache
from fpakman.view.qt.about import AboutDialog
from fpakman.view.qt.window import ManageWindow
@@ -26,7 +29,7 @@ class UpdateCheck(QThread):
apps = self.manager.read_installed()
updates = [app for app in apps if app['update']]
updates = [app for app in apps if app.update]
if updates:
self.signal.emit(updates)
@@ -34,25 +37,13 @@ class UpdateCheck(QThread):
time.sleep(self.check_interval)
class LoadDatabase(QThread):
signal_finished = pyqtSignal()
def __init__(self, manager: FlatpakManager, parent=None):
super(LoadDatabase, self).__init__(parent)
self.manager = manager
def run(self):
self.manager.load_full_database()
self.signal_finished.emit()
class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, manager: FlatpakManager, check_interval: int = 60, update_notification: bool = True):
def __init__(self, locale_keys: dict, manager: FlatpakManager, icon_cache: Cache, check_interval: int = 60, update_notification: bool = True):
super(TrayIcon, self).__init__()
self.locale_keys = locale_keys
self.manager = manager
self.icon_cache = icon_cache
self.icon_default = QIcon(resource.get_path('img/logo.png'))
self.icon_update = QIcon(resource.get_path('img/logo_update.png'))
@@ -60,12 +51,8 @@ class TrayIcon(QSystemTrayIcon):
self.menu = QMenu()
self.action_refreshing = self.menu.addAction(self.locale_keys['tray.action.refreshing'] + '...')
self.action_refreshing.setEnabled(False)
self.action_manage = self.menu.addAction(self.locale_keys['tray.action.manage'])
self.action_manage.triggered.connect(self.show_manage_window)
self.action_manage.setVisible(False)
self.action_about = self.menu.addAction(self.locale_keys['tray.action.about'])
self.action_about.triggered.connect(self.show_about)
@@ -82,46 +69,45 @@ class TrayIcon(QSystemTrayIcon):
self.dialog_about = None
self.thread_database = LoadDatabase(manager)
self.thread_database.signal_finished.connect(self._update_menu)
self.last_updates = set()
self.update_notification = update_notification
self.lock_notify = Lock()
def load_database(self):
self.thread_database.start()
def notify_updates(self, updates: List[Application]):
def _update_menu(self):
self.action_refreshing.setVisible(False)
self.action_manage.setVisible(True)
self.lock_notify.acquire()
def notify_updates(self, updates: List[dict]):
try:
if len(updates) > 0:
if len(updates) > 0:
update_keys = {'{}:{}'.format(app.base_data.id, app.base_data.version) for app in updates}
update_keys = {'{}:{}'.format(app['id'], app['latest_version']) for app in updates}
new_icon = self.icon_update
new_icon = self.icon_update
if update_keys.difference(self.last_updates):
self.last_updates = update_keys
msg = '{}: {}'.format(self.locale_keys['notification.new_updates'].format('Flatpak'), len(updates))
self.setToolTip(msg)
if update_keys.difference(self.last_updates):
self.last_updates = update_keys
msg = '{}: {}'.format(self.locale_keys['notification.new_updates'].format('Flatpak'), len(updates))
self.setToolTip(msg)
if self.update_notification:
system.notify_user(msg)
if self.update_notification:
system.notify_user(msg)
else:
new_icon = self.icon_default
self.setToolTip(None)
else:
new_icon = self.icon_default
self.setToolTip(None)
if self.icon().cacheKey() != new_icon.cacheKey(): # changes the icon if needed
self.setIcon(new_icon)
if self.icon().cacheKey() != new_icon.cacheKey(): # changes the icon if needed
self.setIcon(new_icon)
finally:
self.lock_notify.release()
def show_manage_window(self):
if self.manage_window is None:
self.manage_window = ManageWindow(locale_keys=self.locale_keys,
manager=self.manager,
icon_cache=self.icon_cache,
tray_icon=self)
if self.manage_window.isMinimized():

View File

@@ -1,10 +1,15 @@
import time
from datetime import datetime, timedelta
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal
from fpakman.core import flatpak
from fpakman.core.controller import FlatpakManager
from fpakman.core.model import ApplicationStatus, FlatpakApplication
from fpakman.util.cache import Cache
from fpakman.view.qt import dialog
from fpakman.view.qt.view_model import ApplicationView, ApplicationViewStatus
class UpdateSelectedApps(QThread):
@@ -43,17 +48,21 @@ class UninstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self):
def __init__(self, manager: FlatpakManager, icon_cache: Cache, app: ApplicationView = None):
super(UninstallApp, self).__init__()
self.app_ref = None
self.app = app
self.manager = manager
self.icon_cache = icon_cache
def run(self):
if self.app_ref:
for output in flatpak.uninstall_and_stream(self.app_ref):
if self.app and isinstance(self.app.model, FlatpakApplication):
for output in flatpak.uninstall_and_stream(self.app.model.ref):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.icon_cache.delete(self.app.model.base_data.icon_url)
self.manager.clean_cache_for(self.app.model.base_data.id)
self.signal_finished.emit()
@@ -61,23 +70,23 @@ class DowngradeApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, manager: FlatpakManager, locale_keys: dict):
def __init__(self, manager: FlatpakManager, locale_keys: dict, app: ApplicationView = None):
super(DowngradeApp, self).__init__()
self.manager = manager
self.app = None
self.root_password = None
self.app = app
self.locale_keys = locale_keys
self.root_password = None
def run(self):
if self.app:
if self.app and isinstance(self.app.model, FlatpakApplication):
stream = self.manager.downgrade_app(self.app['model'], self.root_password)
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):
for output in self.manager.downgrade_app(self.app.model, self.root_password):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
@@ -90,16 +99,16 @@ class DowngradeApp(QThread):
class GetAppInfo(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
def __init__(self, app: ApplicationView = None):
super(GetAppInfo, self).__init__()
self.app = None
self.app = app
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'
app_info['description'] = self.app['model']['description']
if self.app and isinstance(self.app.model, FlatpakApplication):
app_info = flatpak.get_app_info_fields(self.app.model.base_data.id, self.app.model.branch)
app_info['name'] = self.app.model.base_data.name
app_info['type'] = 'runtime' if self.app.model.runtime else 'app'
app_info['description'] = self.app.model.base_data.description
self.signal_finished.emit(app_info)
self.app = None
@@ -107,14 +116,14 @@ class GetAppInfo(QThread):
class GetAppHistory(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
def __init__(self, app: ApplicationView = None):
super(GetAppHistory, self).__init__()
self.app = None
self.app = app
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})
if self.app and isinstance(self.app.model, FlatpakApplication):
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
@@ -141,14 +150,14 @@ class InstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self):
def __init__(self, app: ApplicationView = None):
super(InstallApp, self).__init__()
self.app = None
self.app = app
def run(self):
if self.app:
for output in flatpak.install_and_stream(self.app['model']['id'], self.app['model']['origin']):
if self.app and isinstance(self.app.model, FlatpakApplication):
for output in flatpak.install_and_stream(self.app.model.base_data.id, self.app.model.origin):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
@@ -184,3 +193,36 @@ class AnimateProgress(QThread):
time.sleep(0.05)
self.progress_value = 0
class VerifyModels(QThread):
signal_updates = pyqtSignal()
def __init__(self, apps: List[ApplicationView] = None):
super(VerifyModels, self).__init__()
self.apps = apps
def run(self):
if self.apps:
stop_at = datetime.utcnow() + timedelta(seconds=30)
last_ready = 0
while True:
current_ready = 0
for app in self.apps:
current_ready += 1 if app.model.status == ApplicationStatus.READY else 0
if current_ready > last_ready:
last_ready = current_ready
self.signal_updates.emit()
if current_ready == len(self.apps):
self.signal_updates.emit()
break
if stop_at <= datetime.utcnow():
break

View File

@@ -0,0 +1,27 @@
from enum import Enum
from fpakman.util import util
from fpakman.core.model import Application, ApplicationStatus, FlatpakApplication
class ApplicationViewStatus(Enum):
LOADING = 0
READY = 1
class ApplicationView:
def __init__(self, model: Application, visible: bool = True):
self.model = model
self.update_checked = model.update
self.visible = visible
self.status = ApplicationViewStatus.LOADING
def get_async_attr(self, attr: str, strip_html: bool = False, default: str = '...'):
if isinstance(self.model, FlatpakApplication) and self.model.runtime:
res = getattr(self.model.base_data, attr)
else:
res = getattr(self.model.base_data, attr) if self.model.status == ApplicationStatus.READY and getattr(self.model.base_data, attr) else default
return util.strip_html(res) if res and strip_html else res

View File

@@ -10,13 +10,16 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHead
from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakManager
from fpakman.core.model import Application, FlatpakApplication
from fpakman.util.cache import Cache
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, SearchApps, InstallApp, AnimateProgress
GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels
from fpakman.view.qt.view_model import ApplicationView
DARK_ORANGE = '#FF4500'
@@ -24,7 +27,7 @@ DARK_ORANGE = '#FF4500'
class ManageWindow(QWidget):
__BASE_HEIGHT__ = 400
def __init__(self, locale_keys: dict, manager: FlatpakManager, tray_icon=None):
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: FlatpakManager, tray_icon=None):
super(ManageWindow, self).__init__()
self.locale_keys = locale_keys
self.manager = manager
@@ -33,6 +36,7 @@ class ManageWindow(QWidget):
self.working = False # restrict the number of threaded actions
self.apps = []
self.label_flatpak = None
self.icon_cache = icon_cache
self.icon_flathub = QIcon(resource.get_path('img/logo.svg'))
self._check_flatpak_installed()
@@ -101,7 +105,7 @@ class ManageWindow(QWidget):
self.layout.addWidget(toolbar)
self.table_apps = AppsTable(self)
self.table_apps = AppsTable(self, self.icon_cache)
self.table_apps.change_headers_policy()
self.layout.addWidget(self.table_apps)
@@ -131,7 +135,7 @@ class ManageWindow(QWidget):
self.thread_refresh = RefreshApps(self.manager)
self.thread_refresh.signal.connect(self._finish_refresh)
self.thread_uninstall = UninstallApp()
self.thread_uninstall = UninstallApp(self.manager, self.icon_cache)
self.thread_uninstall.signal_output.connect(self._update_action_output)
self.thread_uninstall.signal_finished.connect(self._finish_uninstall)
@@ -155,6 +159,9 @@ class ManageWindow(QWidget):
self.thread_animate_progress = AnimateProgress()
self.thread_animate_progress.signal_change.connect(self._update_progress)
self.thread_verify_models = VerifyModels()
self.thread_verify_models.signal_updates.connect(self._notify_model_data_change)
self.toolbar_bottom = QToolBar()
self.label_updates = QLabel('')
self.label_updates.setStyleSheet("color: {}; font-weight: bold".format(DARK_ORANGE))
@@ -175,6 +182,9 @@ class ManageWindow(QWidget):
self.centralize()
def _notify_model_data_change(self):
self.table_apps.fill_async_data()
def _new_spacer(self):
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -250,20 +260,20 @@ class ManageWindow(QWidget):
self.thread_refresh.start()
def _finish_refresh(self, apps: List[dict]):
def _finish_refresh(self, apps: List[Application]):
self.update_apps(apps)
self.finish_action()
self._release_lock()
def uninstall_app(self, app_ref: str):
def uninstall_app(self, app: ApplicationView):
self._check_flatpak_installed()
if self._acquire_lock():
self._handle_console_option(True)
self._begin_action(self.locale_keys['manage_window.status.uninstalling'])
self.thread_uninstall.app_ref = app_ref
self.thread_uninstall.app = app
self.thread_uninstall.start()
def _finish_uninstall(self):
@@ -281,10 +291,10 @@ class ManageWindow(QWidget):
if self.apps:
show_only_apps = True if only_apps == 2 else False
for idx, app in enumerate(self.apps):
hidden = show_only_apps and app['model']['runtime']
for idx, app_v in enumerate(self.apps):
hidden = show_only_apps and isinstance(app_v.model, FlatpakApplication) and app_v.model.runtime
self.table_apps.setRowHidden(idx, hidden)
app['visible'] = not hidden
app_v.visible = not hidden
self.change_update_state()
self.table_apps.change_headers_policy(QHeaderView.Stretch)
@@ -297,9 +307,9 @@ class ManageWindow(QWidget):
app_updates, runtime_updates = 0, 0
for app in self.apps:
if app['model']['update']:
if app['model']['runtime']:
for app_v in self.apps:
if app_v.model.update:
if app_v.model.runtime:
runtime_updates += 1
else:
app_updates += 1
@@ -314,13 +324,13 @@ class ManageWindow(QWidget):
else:
self.label_updates.setText('')
for app in self.apps:
if app['visible'] and app['update_checked']:
for app_v in self.apps:
if app_v.visible and app_v.update_checked:
enable_bt_update = True
break
self.bt_upgrade.setEnabled(enable_bt_update)
self.tray_icon.notify_updates([app['model'] for app in self.apps if app['model']['update']])
self.tray_icon.notify_updates([app.model for app in self.apps if app.model.update])
def centralize(self):
geo = self.frameGeometry()
@@ -329,7 +339,7 @@ class ManageWindow(QWidget):
geo.moveCenter(center_point)
self.move(geo.topLeft())
def update_apps(self, apps: List[dict]):
def update_apps(self, apps: List[Application]):
self._check_flatpak_installed()
self.apps = []
@@ -338,11 +348,10 @@ class ManageWindow(QWidget):
if apps:
for app in apps:
app_model = {'model': app,
'update_checked': app['update'],
'visible': not app['runtime'] or not self.checkbox_only_apps.isChecked()}
app_model = ApplicationView(model=app,
visible=(isinstance(app, FlatpakApplication) and not app.runtime) or not self.checkbox_only_apps.isChecked())
napps += 1 if not app['runtime'] else 0
napps += 1 if isinstance(app, FlatpakApplication) and not app.runtime else 0
self.apps.append(app_model)
if napps == 0:
@@ -357,9 +366,11 @@ class ManageWindow(QWidget):
self.filter_only_apps(2 if self.checkbox_only_apps.isChecked() else 0)
self.resize_and_center()
self.thread_verify_models.apps = self.apps
self.thread_verify_models.start()
def resize_and_center(self):
new_width = reduce(operator.add,
[self.table_apps.columnWidth(i) for i in range(len(self.table_apps.column_names))]) * 1.05
new_width = reduce(operator.add, [self.table_apps.columnWidth(i) for i in range(len(self.table_apps.column_names))]) * 1.05
self.resize(new_width, self.height())
self.centralize()
@@ -368,7 +379,7 @@ 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']]
to_update = [app_v.model.ref for app_v in self.apps if app_v.visible and app_v.update_checked and isinstance(app_v.model, FlatpakApplication)]
if to_update:
self._handle_console_option(True)
@@ -390,7 +401,7 @@ class ManageWindow(QWidget):
self.thread_animate_progress.stop = False
self.thread_animate_progress.start()
self.ref_progress_bar.setVisible(True)
self.progress_bar.setValue(50)
self.label_status.setText(action_label + "...")
self.toolbar_search.setVisible(False)
self.bt_upgrade.setEnabled(False)
@@ -398,12 +409,11 @@ class ManageWindow(QWidget):
self.checkbox_only_apps.setEnabled(False)
self.table_apps.setEnabled(False)
def finish_action(self, clear_search: bool = True):
def finish_action(self):
self.ref_progress_bar.setVisible(False)
self.ref_label_updates.setVisible(True)
self.thread_animate_progress.stop = True
self.ref_progress_bar.setVisible(False)
self.progress_bar.setValue(0)
self.progress_bar.setVisible(False)
self.bt_refresh.setEnabled(True)
self.toolbar_search.setVisible(True)
self.checkbox_only_apps.setEnabled(True)
@@ -411,9 +421,6 @@ class ManageWindow(QWidget):
self.input_search.setEnabled(True)
self.label_status.setText('')
if clear_search:
self.input_search.setText('')
def downgrade_app(self, app: dict):
self._check_flatpak_installed()
@@ -477,10 +484,10 @@ class ManageWindow(QWidget):
self.thread_search.word = word
self.thread_search.start()
def _finish_search(self, apps_found: List[dict]):
def _finish_search(self, apps_found: List[Application]):
self._release_lock()
self.finish_action(clear_search=False)
self.finish_action()
self.update_apps(apps_found)
def install_app(self, app: dict):