Caching installed application data to disk

This commit is contained in:
Vinícius Moreira
2019-07-11 15:44:07 -03:00
committed by GitHub
parent 238a9d38ae
commit 0ef1fa678d
16 changed files with 410 additions and 129 deletions

View File

@@ -1,3 +1,4 @@
import os
from threading import Lock
from typing import List
@@ -8,7 +9,7 @@ from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidg
QHeaderView, QLabel
from fpakman.core import resource
from fpakman.core.model import FlatpakApplication, ApplicationStatus
from fpakman.core.model import ApplicationStatus
from fpakman.util.cache import Cache
from fpakman.view.qt import dialog
from fpakman.view.qt.view_model import ApplicationView, ApplicationViewStatus
@@ -39,10 +40,11 @@ class UpdateToggleButton(QToolButton):
class AppsTable(QTableWidget):
def __init__(self, parent: QWidget, icon_cache: Cache):
def __init__(self, parent: QWidget, icon_cache: Cache, disk_cache: bool):
super(AppsTable, self).__init__()
self.setParent(parent)
self.window = parent
self.disk_cache = disk_cache
self.column_names = [parent.locale_keys[key].capitalize() for key in ['name',
'version',
'latest_version',
@@ -61,7 +63,7 @@ class AppsTable(QTableWidget):
self.icon_logo = QIcon(resource.get_path('img/logo.svg'))
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon)
self.network_man.finished.connect(self._load_icon_and_cache)
self.icon_cache = icon_cache
self.lock_async_data = Lock()
@@ -114,23 +116,21 @@ class AppsTable(QTableWidget):
if self.window.apps:
visible, ready = 0, 0
for idx, app_v in enumerate(self.window.apps):
if app_v.visible:
visible += 1
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)))
if 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)
self._set_col_description(self.item(idx, 3), app_v)
app_v.status = ApplicationViewStatus.READY
app_name = self.item(idx, 0).text()
if app_v.status == ApplicationViewStatus.READY:
ready += 1
if not app_name or app_name == '...':
self.item(idx, 0).setText(app_v.model.base_data.name)
if ready == visible:
self.window.resize_and_center()
self.item(idx, 2).setText(app_v.model.base_data.latest_version)
self._set_col_description(self.item(idx, 3), app_v)
app_v.status = ApplicationViewStatus.READY
self.window.resize_and_center()
self.lock_async_data.release()
@@ -165,19 +165,29 @@ class AppsTable(QTableWidget):
def _install_app(self):
self.window.install_app(self.get_selected_app())
def _load_icon(self, http_response):
def _load_icon_and_cache(self, http_response):
icon_url = http_response.url().toString()
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)
icon_data = self.icon_cache.get(icon_url)
icon_was_cached = True
for idx, app in enumerate(self.window.apps):
if app.model.base_data.icon_url == icon_url:
self.item(idx, 0).setIcon(icon)
break
if not icon_data:
icon_was_cached = False
pixmap = QPixmap()
icon_bytes = http_response.readAll()
pixmap.loadFromData(icon_bytes)
icon = QIcon(pixmap)
icon_data = {'icon': icon, 'bytes': icon_bytes}
self.icon_cache.add(icon_url, icon_data)
for idx, app in enumerate(self.window.apps):
if app.model.base_data.icon_url == icon_url:
col_name = self.item(idx, 0)
col_name.setIcon(icon_data['icon'])
if self.disk_cache and app.model.supports_disk_cache():
if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()):
self.window.manager.cache_to_disk(app=app.model, icon_bytes=icon_data['bytes'], only_icon=True)
def update_apps(self, app_views: List[ApplicationView]):
self.setEnabled(True)
@@ -185,21 +195,7 @@ class AppsTable(QTableWidget):
if app_views:
for idx, app_v in enumerate(app_views):
col_name = QTableWidgetItem()
col_name.setText(app_v.model.base_data.name)
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if not app_v.model.base_data.icon_url:
col_name.setIcon(QIcon(app_v.model.get_default_logo_path()))
else:
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(QIcon(app_v.model.get_default_logo_path()))
col_name = self._gen_col_name(app_v)
self.setItem(idx, 0, col_name)
col_version = QTableWidgetItem()
@@ -235,6 +231,22 @@ class AppsTable(QTableWidget):
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, 6, col_update)
def _gen_col_name(self, app_v: ApplicationView):
col = QTableWidgetItem()
col.setText(app_v.model.base_data.name if app_v.model.base_data.name else '...')
col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if app_v.model.supports_disk_cache() and os.path.exists(app_v.model.get_disk_icon_path()):
icon = QIcon(app_v.model.get_disk_icon_path())
elif not app_v.model.base_data.icon_url:
icon = QIcon(app_v.model.get_default_icon_path())
else:
icon_data = self.icon_cache.get(app_v.model.base_data.icon_url)
icon = icon_data['icon'] if icon_data else QIcon(app_v.model.get_default_icon_path())
col.setIcon(icon)
return col
def _set_col_description(self, col: QTableWidgetItem, app_v: ApplicationView):
desc = app_v.get_async_attr('description', strip_html=True)

View File

@@ -39,11 +39,12 @@ class UpdateCheck(QThread):
class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, manager: ApplicationManager, icon_cache: Cache, check_interval: int = 60, update_notification: bool = True):
def __init__(self, locale_keys: dict, manager: ApplicationManager, icon_cache: Cache, disk_cache: bool, 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.disk_cache = disk_cache
self.icon_default = QIcon(resource.get_path('img/logo.png'))
self.icon_update = QIcon(resource.get_path('img/logo_update.png'))
@@ -108,7 +109,8 @@ class TrayIcon(QSystemTrayIcon):
self.manage_window = ManageWindow(locale_keys=self.locale_keys,
manager=self.manager,
icon_cache=self.icon_cache,
tray_icon=self)
tray_icon=self,
disk_cache=self.disk_cache)
if self.manage_window.isMinimized():
self.manage_window.setWindowState(Qt.WindowNoState)

View File

@@ -56,13 +56,25 @@ class UninstallApp(QThread):
def run(self):
if self.app:
for output in self.manager.uninstall_and_stream(self.app.model):
subproc = self.manager.uninstall_and_stream(self.app.model)
for output in subproc.stdout:
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)
error = False
for output in subproc.stderr:
line = output.decode().strip()
if line:
error = True
self.signal_output.emit(line)
if not error:
self.icon_cache.delete(self.app.model.base_data.icon_url)
self.manager.clean_cache_for(self.app.model)
self.signal_finished.emit()
@@ -147,19 +159,39 @@ class InstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, app: ApplicationView = None):
def __init__(self, manager: ApplicationManager, disk_cache: bool, icon_cache: Cache, app: ApplicationView = None):
super(InstallApp, self).__init__()
self.app = app
self.manager = manager
self.icon_cache = icon_cache
self.disk_cache = disk_cache
def run(self):
if self.app:
for output in self.manager.install_and_stream(self.app.model):
subproc = self.manager.install_and_stream(self.app.model)
for output in subproc.stdout:
line = output.decode().strip()
if line:
self.signal_output.emit(line)
error = False
for output in subproc.stderr:
line = output.decode().strip()
if line:
error = True
self.signal_output.emit(line)
if not error and self.disk_cache:
self.app.model.installed = True
if self.app.model.supports_disk_cache():
self.manager.cache_to_disk(app=self.app.model,
icon_bytes=self.icon_cache.get(self.app.model.base_data.icon_url)['bytes'],
only_icon=False)
self.app = None
self.signal_finished.emit()
@@ -224,3 +256,5 @@ class VerifyModels(QThread):
if stop_at <= datetime.utcnow():
break
self.apps = None

View File

@@ -26,7 +26,7 @@ DARK_ORANGE = '#FF4500'
class ManageWindow(QWidget):
__BASE_HEIGHT__ = 400
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, tray_icon=None):
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, tray_icon=None):
super(ManageWindow, self).__init__()
self.locale_keys = locale_keys
self.manager = manager
@@ -36,6 +36,7 @@ class ManageWindow(QWidget):
self.apps = []
self.label_flatpak = None
self.icon_cache = icon_cache
self.disk_cache = disk_cache
self.icon_flathub = QIcon(resource.get_path('img/logo.svg'))
self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
@@ -103,7 +104,7 @@ class ManageWindow(QWidget):
self.layout.addWidget(toolbar)
self.table_apps = AppsTable(self, self.icon_cache)
self.table_apps = AppsTable(self, self.icon_cache, disk_cache=self.disk_cache)
self.table_apps.change_headers_policy()
self.layout.addWidget(self.table_apps)
@@ -150,7 +151,7 @@ class ManageWindow(QWidget):
self.thread_search = SearchApps(self.manager)
self.thread_search.signal_finished.connect(self._finish_search)
self.thread_install = InstallApp(self.manager)
self.thread_install = InstallApp(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache)
self.thread_install.signal_output.connect(self._update_action_output)
self.thread_install.signal_finished.connect(self._finish_install)
@@ -236,6 +237,7 @@ class ManageWindow(QWidget):
def refresh(self):
if self._acquire_lock():
self.input_search.clear()
self._begin_action(self.locale_keys['manage_window.status.refreshing'])
self.thread_refresh.start()