refactoring async work

This commit is contained in:
Vinícius Moreira
2019-07-24 16:01:44 -03:00
committed by GitHub
parent e35b515b5b
commit c7a111174a
10 changed files with 263 additions and 222 deletions

View File

@@ -1,11 +1,9 @@
import time
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from argparse import Namespace from argparse import Namespace
from threading import Lock
from typing import List, Dict from typing import List, Dict
from fpakman.core.disk import DiskCacheLoader, DiskCacheLoaderFactory from fpakman.core.disk import DiskCacheLoader, DiskCacheLoaderFactory
from fpakman.core.model import Application from fpakman.core.model import Application, ApplicationUpdate
from fpakman.core.system import FpakmanProcess from fpakman.core.system import FpakmanProcess
@@ -82,6 +80,10 @@ class ApplicationManager(ABC):
""" """
pass pass
@abstractmethod
def list_updates(self) -> List[ApplicationUpdate]:
pass
class GenericApplicationManager(ApplicationManager): class GenericApplicationManager(ApplicationManager):
@@ -90,7 +92,6 @@ class GenericApplicationManager(ApplicationManager):
self.managers = managers self.managers = managers
self.map = {m.get_app_type(): m for m in self.managers} self.map = {m.get_app_type(): m for m in self.managers}
self.disk_loader_factory = disk_loader_factory self.disk_loader_factory = disk_loader_factory
self.lock_read = Lock()
self._enabled_map = {} if app_args.check_packaging_once else None self._enabled_map = {} if app_args.check_packaging_once else None
self.prepare() self.prepare()
@@ -131,57 +132,48 @@ class GenericApplicationManager(ApplicationManager):
return man.is_enabled() return man.is_enabled()
def search(self, word: str, disk_loader: DiskCacheLoader = None) -> Dict[str, List[Application]]: def search(self, word: str, disk_loader: DiskCacheLoader = None) -> Dict[str, List[Application]]:
self.lock_read.acquire() res = {'installed': [], 'new': []}
try:
res = {'installed': [], 'new': []}
norm_word = word.strip().lower() norm_word = word.strip().lower()
disk_loader = None disk_loader = None
for man in self.managers: for man in self.managers:
if self._is_enabled(man): if self._is_enabled(man):
if not disk_loader: if not disk_loader:
disk_loader = self.disk_loader_factory.new() disk_loader = self.disk_loader_factory.new()
disk_loader.start() disk_loader.start()
apps_found = man.search(word=norm_word, disk_loader=disk_loader) apps_found = man.search(word=norm_word, disk_loader=disk_loader)
res['installed'].extend(apps_found['installed']) res['installed'].extend(apps_found['installed'])
res['new'].extend(apps_found['new']) res['new'].extend(apps_found['new'])
disk_loader.stop = True disk_loader.stop = True
disk_loader.join() disk_loader.join()
for key in res: for key in res:
res[key] = self._sort(res[key], norm_word) res[key] = self._sort(res[key], norm_word)
return res return res
finally:
self.lock_read.release()
def read_installed(self, disk_loader: DiskCacheLoader = None) -> List[Application]: def read_installed(self, disk_loader: DiskCacheLoader = None) -> List[Application]:
self.lock_read.acquire() installed = []
try: disk_loader = None
installed = []
disk_loader = None for man in self.managers:
if self._is_enabled(man):
if not disk_loader:
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
for man in self.managers: installed.extend(man.read_installed(disk_loader=disk_loader))
if self._is_enabled(man):
if not disk_loader:
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
installed.extend(man.read_installed(disk_loader=disk_loader)) disk_loader.stop = True
disk_loader.join()
disk_loader.stop = True installed.sort(key=lambda a: a.base_data.name.lower())
disk_loader.join()
installed.sort(key=lambda a: a.base_data.name.lower()) return installed
return installed
finally:
self.lock_read.release()
def can_downgrade(self): def can_downgrade(self):
return True return True
@@ -260,8 +252,17 @@ class GenericApplicationManager(ApplicationManager):
return man.refresh(app, root_password) return man.refresh(app, root_password)
def prepare(self): def prepare(self):
if self.managers: if self.managers:
for man in self.managers: for man in self.managers:
if self._is_enabled(man): if self._is_enabled(man):
man.prepare() man.prepare()
def list_updates(self) -> List[ApplicationUpdate]:
updates = []
if self.managers:
for man in self.managers:
if self._is_enabled(man):
updates.extend(man.list_updates())
return updates

View File

@@ -9,8 +9,8 @@ from fpakman.core.controller import ApplicationManager
from fpakman.core.disk import DiskCacheLoader from fpakman.core.disk import DiskCacheLoader
from fpakman.core.flatpak import flatpak from fpakman.core.flatpak import flatpak
from fpakman.core.flatpak.model import FlatpakApplication from fpakman.core.flatpak.model import FlatpakApplication
from fpakman.core.flatpak.worker import FlatpakAsyncDataLoader from fpakman.core.flatpak.worker import FlatpakAsyncDataLoader, FlatpakUpdateLoader
from fpakman.core.model import ApplicationData from fpakman.core.model import ApplicationData, ApplicationUpdate
from fpakman.core.system import FpakmanProcess from fpakman.core.system import FpakmanProcess
from fpakman.util.cache import Cache from fpakman.util.cache import Cache
@@ -148,3 +148,33 @@ class FlatpakManager(ApplicationManager):
def prepare(self): def prepare(self):
flatpak.set_default_remotes() flatpak.set_default_remotes()
def list_updates(self) -> List[ApplicationUpdate]:
updates = []
installed = flatpak.list_installed(extra_fields=False)
if installed:
available_updates = flatpak.list_updates_as_str()
if available_updates:
loaders = None
for app_json in installed:
if app_json['id'] in available_updates:
loader = FlatpakUpdateLoader(app=app_json, http_session=self.http_session)
loader.start()
if loaders is None:
loaders = []
loaders.append(loader)
if loaders:
for loader in loaders:
loader.join()
app = loader.app
updates.append(ApplicationUpdate(app_id='{}:{}'.format(app['id'], app['branch']),
app_type='flatpak',
version=app.get('version')))
return updates

View File

@@ -1,6 +1,6 @@
import re import re
import subprocess import subprocess
from typing import List from typing import List, Set
from fpakman.core import system from fpakman.core import system
from fpakman.core.exception import NoInternetException from fpakman.core.exception import NoInternetException
@@ -8,7 +8,7 @@ from fpakman.core.exception import NoInternetException
BASE_CMD = 'flatpak' BASE_CMD = 'flatpak'
def app_str_to_json(line: str, version: str) -> dict: def app_str_to_json(line: str, version: str, extra_fields: bool = True) -> dict:
app_array = line.split('\t') app_array = line.split('\t')
@@ -17,18 +17,21 @@ def app_str_to_json(line: str, version: str) -> dict:
'id': app_array[1], 'id': app_array[1],
'version': app_array[2], 'version': app_array[2],
'branch': app_array[3]} 'branch': app_array[3]}
elif '1.0' <= version < '1.1':
app = {'ref': app_array[0], 'options': app_array[1]}
ref_data = app['ref'].split('/') elif '1.0' <= version < '1.1':
app['id'] = ref_data[0]
app['arch'] = ref_data[1] ref_data = app_array[0].split('/')
app['branch'] = ref_data[2]
app['name'] = ref_data[0].split('.')[-1] app = {'id': ref_data[0],
app['version'] = None 'arch': ref_data[1],
'name': ref_data[0].split('.')[-1],
'ref': app_array[0],
'options': app_array[1],
'branch': ref_data[2],
'version': None}
elif '1.2' <= version < '1.3': elif '1.2' <= version < '1.3':
app = {'name': app_array[1].strip().split('.')[-1], app = {'id': app_array[1],
'id': app_array[1], 'name': app_array[1].strip().split('.')[-1],
'version': app_array[2], 'version': app_array[2],
'branch': app_array[3], 'branch': app_array[3],
'arch': app_array[4], 'arch': app_array[4],
@@ -36,8 +39,9 @@ def app_str_to_json(line: str, version: str) -> dict:
else: else:
raise Exception('Unsupported version') raise Exception('Unsupported version')
extra_fields = get_app_info_fields(app['id'], app['branch'], ['origin', 'arch', 'ref', 'commit'], check_runtime=True) if extra_fields:
app.update(extra_fields) extra_fields = get_app_info_fields(app['id'], app['branch'], ['origin', 'arch', 'ref', 'commit'], check_runtime=True)
app.update(extra_fields)
return app return app
@@ -81,13 +85,13 @@ def get_app_info(app_id: str, branch: str):
return system.run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch)) return system.run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch))
def list_installed() -> List[dict]: def list_installed(extra_fields: bool = True) -> List[dict]:
apps_str = system.run_cmd('{} list'.format(BASE_CMD)) apps_str = system.run_cmd('{} list'.format(BASE_CMD))
if apps_str: if apps_str:
version = get_version() version = get_version()
app_lines = apps_str.split('\n') app_lines = apps_str.split('\n')
return [app_str_to_json(line, version) for line in app_lines if line] return [app_str_to_json(line, version, extra_fields=extra_fields) for line in app_lines if line]
return [] return []
@@ -215,4 +219,3 @@ def install_and_stream(app_id: str, origin: str):
def set_default_remotes(): def set_default_remotes():
system.run_cmd('flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo') system.run_cmd('flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo')

View File

@@ -1,9 +1,11 @@
import time import time
import traceback import traceback
from threading import Thread
from colorama import Fore from colorama import Fore
from fpakman.core.controller import ApplicationManager from fpakman.core.controller import ApplicationManager
from fpakman.core.flatpak import flatpak
from fpakman.core.flatpak.constants import FLATHUB_API_URL, FLATHUB_URL from fpakman.core.flatpak.constants import FLATHUB_API_URL, FLATHUB_URL
from fpakman.core.flatpak.model import FlatpakApplication from fpakman.core.flatpak.model import FlatpakApplication
from fpakman.core.model import ApplicationStatus from fpakman.core.model import ApplicationStatus
@@ -82,3 +84,41 @@ class FlatpakAsyncDataLoader(AsyncDataLoader):
http_session=self.http_session, http_session=self.http_session,
timeout=self.timeout, timeout=self.timeout,
app=self.app) app=self.app)
class FlatpakUpdateLoader(Thread):
def __init__(self, app: dict, http_session, attempts: int = 2, timeout: int = 20):
super(FlatpakUpdateLoader, self).__init__(daemon=True)
self.app = app
self.http_session = http_session
self.attempts = attempts
self.timeout = timeout
def run(self):
if self.app.get('ref') is None:
self.app.update(flatpak.get_app_info_fields(self.app['id'], self.app['branch'], fields=['ref'], check_runtime=True))
else:
self.app['runtime'] = self.app['ref'].startswith('runtime/')
if not self.app['runtime']:
current_attempts = 0
while current_attempts < self.attempts:
current_attempts += 1
try:
res = self.http_session.get('{}/apps/{}'.format(FLATHUB_API_URL, self.app['id']), timeout=self.timeout)
if res.status_code == 200 and res.text:
data = res.json()
if data.get('currentReleaseVersion'):
self.app['version'] = data['currentReleaseVersion']
break
except:
traceback.print_exc()
time.sleep(0.5)

View File

@@ -86,3 +86,14 @@ class Application(ABC):
def __str__(self): def __str__(self):
return '{} (id={}, name={})'.format(self.__class__.__name__, self.base_data.id, self.base_data.name) return '{} (id={}, name={})'.format(self.__class__.__name__, self.base_data.id, self.base_data.name)
class ApplicationUpdate:
def __init__(self, app_id: str, version: str, app_type: str):
self.id = app_id
self.version = version
self.type = app_type
def __str__(self):
return '{} (id={}, type={}, new_version={})'.format(self.__class__.__name__, self.id, self.type, self.type)

View File

@@ -7,7 +7,7 @@ from typing import Dict, List
from fpakman.core import disk from fpakman.core import disk
from fpakman.core.controller import ApplicationManager from fpakman.core.controller import ApplicationManager
from fpakman.core.disk import DiskCacheLoader from fpakman.core.disk import DiskCacheLoader
from fpakman.core.model import ApplicationData, Application from fpakman.core.model import ApplicationData, Application, ApplicationUpdate
from fpakman.core.snap import snap from fpakman.core.snap import snap
from fpakman.core.snap.model import SnapApplication from fpakman.core.snap.model import SnapApplication
from fpakman.core.snap.worker import SnapAsyncDataLoader from fpakman.core.snap.worker import SnapAsyncDataLoader
@@ -131,3 +131,6 @@ class SnapManager(ApplicationManager):
def prepare(self): def prepare(self):
pass pass
def list_updates(self) -> List[ApplicationUpdate]:
return []

View File

@@ -119,9 +119,6 @@ class AppsTable(QTableWidget):
menu_row.exec_() menu_row.exec_()
def fill_async_data(self): def fill_async_data(self):
self.lock_async_data.acquire()
if self.window.apps: if self.window.apps:
for idx, app_v in enumerate(self.window.apps): for idx, app_v in enumerate(self.window.apps):
@@ -142,8 +139,6 @@ class AppsTable(QTableWidget):
self.window.resize_and_center() self.window.resize_and_center()
self.lock_async_data.release()
def get_selected_app(self) -> ApplicationView: def get_selected_app(self) -> ApplicationView:
return self.window.apps[self.currentRow()] return self.window.apps[self.currentRow()]
@@ -202,9 +197,9 @@ class AppsTable(QTableWidget):
if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()): 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) 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]): def update_apps(self, app_views: List[ApplicationView], update_check_enabled: bool = True):
self.setEnabled(True)
self.setRowCount(len(app_views) if app_views else 0) self.setRowCount(len(app_views) if app_views else 0)
self.setEnabled(True)
if app_views: if app_views:
for idx, app_v in enumerate(app_views): for idx, app_v in enumerate(app_views):
@@ -214,7 +209,11 @@ class AppsTable(QTableWidget):
self._set_col_type(idx, app_v) self._set_col_type(idx, app_v)
self._set_col_installed(idx, app_v) self._set_col_installed(idx, app_v)
col_update = UpdateToggleButton(app_v, self.window, self.window.locale_keys, app_v.model.update) if app_v.model.update else None col_update = None
if update_check_enabled and app_v.model.update:
col_update = UpdateToggleButton(app_v, self.window, self.window.locale_keys, app_v.model.update)
self.setCellWidget(idx, 5, col_update) self.setCellWidget(idx, 5, col_update)
def _set_col_installed(self, idx: int, app_v: ApplicationView): def _set_col_installed(self, idx: int, app_v: ApplicationView):

View File

@@ -6,9 +6,10 @@ from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication, Qt, QSize
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from fpakman import __app_name__
from fpakman.core import resource, system from fpakman.core import resource, system
from fpakman.core.controller import ApplicationManager from fpakman.core.controller import ApplicationManager
from fpakman.core.model import Application from fpakman.core.model import ApplicationUpdate
from fpakman.util.cache import Cache from fpakman.util.cache import Cache
from fpakman.view.qt.about import AboutDialog from fpakman.view.qt.about import AboutDialog
from fpakman.view.qt.window import ManageWindow from fpakman.view.qt.window import ManageWindow
@@ -27,9 +28,7 @@ class UpdateCheck(QThread):
while True: while True:
apps = self.manager.read_installed() updates = self.manager.list_updates()
updates = [app for app in apps if app.update]
if updates: if updates:
self.signal.emit(updates) self.signal.emit(updates)
@@ -66,30 +65,32 @@ class TrayIcon(QSystemTrayIcon):
self.setContextMenu(self.menu) self.setContextMenu(self.menu)
self.manage_window = None self.manage_window = None
self.dialog_about = None
self.check_thread = UpdateCheck(check_interval=check_interval, manager=self.manager) 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()
self.dialog_about = None
self.last_updates = set() self.last_updates = set()
self.update_notification = update_notification self.update_notification = update_notification
self.lock_notify = Lock() self.lock_notify = Lock()
self.activated.connect(self.handle_click) self.activated.connect(self.handle_click)
self.set_default_tooltip()
def set_default_tooltip(self):
self.setToolTip('{} ({})'.format(self.locale_keys['manage_window.title'], __app_name__))
def handle_click(self, reason): def handle_click(self, reason):
if reason == self.Trigger: if reason == self.Trigger:
self.show_manage_window() self.show_manage_window()
def notify_updates(self, updates: List[Application]): def notify_updates(self, updates: List[ApplicationUpdate]):
self.lock_notify.acquire() self.lock_notify.acquire()
try: try:
if len(updates) > 0: if len(updates) > 0:
update_keys = {'{}:{}:{}'.format(up.type, up.id, up.version) for up in updates}
update_keys = {'{}:{}'.format(app.base_data.id, app.base_data.version) for app in updates}
new_icon = self.icon_update new_icon = self.icon_update
@@ -103,7 +104,7 @@ class TrayIcon(QSystemTrayIcon):
else: else:
new_icon = self.icon_default new_icon = self.icon_default
self.setToolTip(None) self.set_default_tooltip()
if self.icon().cacheKey() != new_icon.cacheKey(): # changes the icon if needed if self.icon().cacheKey() != new_icon.cacheKey(): # changes the icon if needed
self.setIcon(new_icon) self.setIcon(new_icon)

View File

@@ -302,6 +302,8 @@ class VerifyModels(QThread):
if stop_at <= datetime.utcnow(): if stop_at <= datetime.utcnow():
break break
time.sleep(0.1)
self.apps = None self.apps = None

View File

@@ -1,6 +1,5 @@
import operator import operator
from functools import reduce from functools import reduce
from threading import Lock
from typing import List, Set from typing import List, Set
from PyQt5.QtCore import QEvent, Qt from PyQt5.QtCore import QEvent, Qt
@@ -31,7 +30,6 @@ class ManageWindow(QWidget):
self.locale_keys = locale_keys self.locale_keys = locale_keys
self.manager = manager self.manager = manager
self.tray_icon = tray_icon self.tray_icon = tray_icon
self.thread_lock = Lock()
self.working = False # restrict the number of threaded actions self.working = False # restrict the number of threaded actions
self.apps = [] self.apps = []
self.label_flatpak = None self.label_flatpak = None
@@ -42,7 +40,6 @@ class ManageWindow(QWidget):
self.icon_flathub = QIcon(resource.get_path('img/logo.svg')) self.icon_flathub = QIcon(resource.get_path('img/logo.svg'))
self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__) self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
self.setWindowTitle(locale_keys['manage_window.title'])
self.setWindowIcon(self.icon_flathub) self.setWindowIcon(self.icon_flathub)
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
@@ -111,7 +108,7 @@ class ManageWindow(QWidget):
self.bt_upgrade.setIcon(QIcon(resource.get_path('img/update_green.svg'))) self.bt_upgrade.setIcon(QIcon(resource.get_path('img/update_green.svg')))
self.bt_upgrade.setEnabled(False) self.bt_upgrade.setEnabled(False)
self.bt_upgrade.clicked.connect(self.update_selected) self.bt_upgrade.clicked.connect(self.update_selected)
toolbar.addWidget(self.bt_upgrade) self.ref_bt_upgrade = toolbar.addWidget(self.bt_upgrade)
self.layout.addWidget(toolbar) self.layout.addWidget(toolbar)
@@ -235,25 +232,6 @@ class ManageWindow(QWidget):
self.hide() self.hide()
self._handle_console_option(False) self._handle_console_option(False)
def _acquire_lock(self):
self.thread_lock.acquire()
if not self.working:
self.working = True
self.thread_lock.release()
return self.working
def _release_lock(self):
self.thread_lock.acquire()
if self.working:
self.working = False
self.thread_lock.release()
def _handle_console(self, checked: bool): def _handle_console(self, checked: bool):
if checked: if checked:
@@ -271,69 +249,59 @@ class ManageWindow(QWidget):
self.textarea_output.hide() self.textarea_output.hide()
def refresh_apps(self, keep_console: bool = True): def refresh_apps(self, keep_console: bool = True):
self.filter_types.clear()
self.input_search.clear()
if self._acquire_lock(): if not keep_console:
self.filter_types.clear() self._handle_console_option(False)
self.input_search.clear()
if not keep_console: self.ref_checkbox_updates.setVisible(False)
self._handle_console_option(False) self.ref_checkbox_only_apps.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.refreshing'], clear_filters=True)
self.ref_checkbox_updates.setVisible(False) self.thread_refresh.start()
self.ref_checkbox_only_apps.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.refreshing'], clear_filters=True)
self.thread_refresh.start()
def _finish_refresh_apps(self, apps: List[Application]): def _finish_refresh_apps(self, apps: List[Application]):
self.update_apps(apps) self.update_apps(apps)
self.finish_action() self.finish_action()
self.ref_checkbox_only_apps.setVisible(True) self.ref_checkbox_only_apps.setVisible(True)
self._release_lock() self.ref_bt_upgrade.setVisible(True)
def uninstall_app(self, app: ApplicationView): def uninstall_app(self, app: ApplicationView):
if self._acquire_lock(): pwd = None
requires_root = self.manager.requires_root('uninstall', self.table_apps.get_selected_app().model)
pwd = None if not is_root() and requires_root:
requires_root = self.manager.requires_root('uninstall', self.table_apps.get_selected_app().model) pwd, ok = ask_root_password(self.locale_keys)
if not is_root() and requires_root: if not ok:
pwd, ok = ask_root_password(self.locale_keys) return
if not ok: self._handle_console_option(True)
self._release_lock() self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.uninstalling'], app.model.base_data.name))
return
self._handle_console_option(True) self.thread_uninstall.app = app
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.uninstalling'], app.model.base_data.name)) self.thread_uninstall.root_password = pwd
self.thread_uninstall.start()
self.thread_uninstall.app = app
self.thread_uninstall.root_password = pwd
self.thread_uninstall.start()
def refresh(self, app: ApplicationView): def refresh(self, app: ApplicationView):
if self._acquire_lock(): pwd = None
requires_root = self.manager.requires_root('refresh', self.table_apps.get_selected_app().model)
pwd = None if not is_root() and requires_root:
requires_root = self.manager.requires_root('refresh', self.table_apps.get_selected_app().model) pwd, ok = ask_root_password(self.locale_keys)
if not is_root() and requires_root: if not ok:
pwd, ok = ask_root_password(self.locale_keys) return
if not ok: self._handle_console_option(True)
self._release_lock() self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.refreshing'], app.model.base_data.name))
return
self._handle_console_option(True) self.thread_refresh_app.app = app
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.refreshing'], app.model.base_data.name)) self.thread_refresh_app.root_password = pwd
self.thread_refresh_app.start()
self.thread_refresh_app.app = app
self.thread_refresh_app.root_password = pwd
self.thread_refresh_app.start()
def _finish_uninstall(self, success: bool): def _finish_uninstall(self, success: bool):
self.finish_action() self.finish_action()
self._release_lock()
if success: if success:
if self._can_notify_user(): if self._can_notify_user():
@@ -353,7 +321,6 @@ class ManageWindow(QWidget):
def _finish_downgrade(self, success: bool): def _finish_downgrade(self, success: bool):
self.finish_action() self.finish_action()
self._release_lock()
if success: if success:
if self._can_notify_user(): if self._can_notify_user():
@@ -365,17 +332,16 @@ class ManageWindow(QWidget):
if self._can_notify_user(): if self._can_notify_user():
system.notify_user(self.locale_keys['notification.downgrade.failed']) system.notify_user(self.locale_keys['notification.downgrade.failed'])
self.change_update_state(notify_tray=False) self.change_update_state()
self.checkbox_console.setChecked(True) self.checkbox_console.setChecked(True)
def _finish_refresh(self, success: bool): def _finish_refresh(self, success: bool):
self.finish_action() self.finish_action()
self._release_lock()
if success: if success:
self.refresh_apps() self.refresh_apps()
else: else:
self.change_update_state(notify_tray=False) self.change_update_state()
self.checkbox_console.setChecked(True) self.checkbox_console.setChecked(True)
def apply_filters(self): def apply_filters(self):
@@ -399,11 +365,9 @@ class ManageWindow(QWidget):
self.table_apps.change_headers_policy() self.table_apps.change_headers_policy()
self.resize_and_center(accept_lower_width=visible_apps > 0) self.resize_and_center(accept_lower_width=visible_apps > 0)
def change_update_state(self, notify_tray: bool = True, change_filters: bool = True): def change_update_state(self, change_filters: bool = True):
enable_bt_update = False enable_bt_update = False
app_updates, library_updates, not_installed = 0, 0, 0
app_updates, library_updates = 0, 0
for app_v in self.apps: for app_v in self.apps:
if app_v.model.update: if app_v.model.update:
@@ -412,28 +376,33 @@ class ManageWindow(QWidget):
else: else:
app_updates += 1 app_updates += 1
if not app_v.model.installed:
not_installed += 1
for app_v in self.apps: for app_v in self.apps:
if app_v.visible and app_v.update_checked: if not_installed == 0 and app_v.visible and app_v.update_checked:
enable_bt_update = True enable_bt_update = True
break break
self.bt_upgrade.setEnabled(enable_bt_update) self.bt_upgrade.setEnabled(enable_bt_update)
total_updates = app_updates + library_updates total_updates = app_updates + library_updates
if total_updates > 0: if total_updates > 0:
self.label_updates.setPixmap(QPixmap(resource.get_path('img/exclamation.svg')).scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.label_updates.setPixmap(QPixmap(resource.get_path('img/exclamation.svg')).scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation))
self.label_updates.setToolTip('{}: {} ( {} {} | {} {} )'.format(self.locale_keys['manage_window.label.updates'], self.label_updates.setToolTip('{}: {} ( {} {} | {} {} )'.format(self.locale_keys['manage_window.label.updates'],
total_updates, total_updates,
app_updates, app_updates,
self.locale_keys['manage_window.checkbox.only_apps'].lower(), self.locale_keys['manage_window.checkbox.only_apps'].lower(),
library_updates, library_updates,
self.locale_keys['others'].lower())) self.locale_keys['others'].lower()))
if not self.ref_checkbox_updates.isVisible(): if not_installed == 0:
self.ref_checkbox_updates.setVisible(True) if not self.ref_checkbox_updates.isVisible():
self.ref_checkbox_updates.setVisible(True)
if change_filters and not self.checkbox_updates.isChecked(): if change_filters and not self.checkbox_updates.isChecked():
self.checkbox_updates.setChecked(True) self.checkbox_updates.setChecked(True)
if change_filters and library_updates > 0 and self.checkbox_only_apps.isChecked(): if change_filters and library_updates > 0 and self.checkbox_only_apps.isChecked():
self.checkbox_only_apps.setChecked(False) self.checkbox_only_apps.setChecked(False)
@@ -442,9 +411,6 @@ class ManageWindow(QWidget):
self.ref_checkbox_updates.setVisible(False) self.ref_checkbox_updates.setVisible(False)
self.label_updates.setPixmap(QPixmap()) self.label_updates.setPixmap(QPixmap())
if notify_tray:
self.tray_icon.notify_updates([app.model for app in self.apps if app.model.update])
def centralize(self): def centralize(self):
geo = self.frameGeometry() geo = self.frameGeometry()
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
@@ -452,7 +418,7 @@ class ManageWindow(QWidget):
geo.moveCenter(center_point) geo.moveCenter(center_point)
self.move(geo.topLeft()) self.move(geo.topLeft())
def update_apps(self, apps: List[Application]): def update_apps(self, apps: List[Application], update_check_enabled: bool = True):
self.apps = [] self.apps = []
napps = 0 # number of apps (not libraries) napps = 0 # number of apps (not libraries)
@@ -473,7 +439,7 @@ class ManageWindow(QWidget):
self.checkbox_only_apps.setChecked(True) self.checkbox_only_apps.setChecked(True)
self._update_type_filters(available_types) self._update_type_filters(available_types)
self.table_apps.update_apps(self.apps) self.table_apps.update_apps(self.apps, update_check_enabled=update_check_enabled)
self.apply_filters() self.apply_filters()
self.change_update_state() self.change_update_state()
self.resize_and_center() self.resize_and_center()
@@ -510,22 +476,19 @@ class ManageWindow(QWidget):
self.centralize() self.centralize()
def update_selected(self): def update_selected(self):
if self.apps:
if self._acquire_lock(): to_update = [app_v for app_v in self.apps if app_v.visible and app_v.update_checked]
if self.apps:
to_update = [app_v for app_v in self.apps if app_v.visible and app_v.update_checked] if to_update:
self._handle_console_option(True)
if to_update: self._begin_action(self.locale_keys['manage_window.status.upgrading'])
self._handle_console_option(True) self.thread_update.apps_to_update = to_update
self.thread_update.start()
self._begin_action(self.locale_keys['manage_window.status.upgrading'])
self.thread_update.apps_to_update = to_update
self.thread_update.start()
def _finish_update_selected(self, success: bool, updated: int): def _finish_update_selected(self, success: bool, updated: int):
self.finish_action() self.finish_action()
self._release_lock()
if success: if success:
if self._can_notify_user(): if self._can_notify_user():
@@ -581,51 +544,43 @@ class ManageWindow(QWidget):
self.checkbox_updates.setEnabled(True) self.checkbox_updates.setEnabled(True)
def downgrade_app(self, app: ApplicationView): def downgrade_app(self, app: ApplicationView):
if self._acquire_lock(): pwd = None
requires_root = self.manager.requires_root('downgrade', self.table_apps.get_selected_app().model)
pwd = None if not is_root() and requires_root:
requires_root = self.manager.requires_root('downgrade', self.table_apps.get_selected_app().model) pwd, ok = ask_root_password(self.locale_keys)
if not is_root() and requires_root: if not ok:
pwd, ok = ask_root_password(self.locale_keys) return
if not ok: self._handle_console_option(True)
self._release_lock() self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.downgrading'], app.model.base_data.name))
return
self._handle_console_option(True) self.thread_downgrade.app = app
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.downgrading'], app.model.base_data.name)) self.thread_downgrade.root_password = pwd
self.thread_downgrade.start()
self.thread_downgrade.app = app
self.thread_downgrade.root_password = pwd
self.thread_downgrade.start()
def get_app_info(self, app: dict): def get_app_info(self, app: dict):
self._handle_console_option(False)
self._begin_action(self.locale_keys['manage_window.status.info'])
if self._acquire_lock(): self.thread_get_info.app = app
self._handle_console_option(False) self.thread_get_info.start()
self._begin_action(self.locale_keys['manage_window.status.info'])
self.thread_get_info.app = app
self.thread_get_info.start()
def get_app_history(self, app: dict): def get_app_history(self, app: dict):
if self._acquire_lock(): self._handle_console_option(False)
self._handle_console_option(False) self._begin_action(self.locale_keys['manage_window.status.history'])
self._begin_action(self.locale_keys['manage_window.status.history'])
self.thread_get_history.app = app self.thread_get_history.app = app
self.thread_get_history.start() self.thread_get_history.start()
def _finish_get_info(self, app_info: dict): def _finish_get_info(self, app_info: dict):
self._release_lock()
self.finish_action() self.finish_action()
self.change_update_state() self.change_update_state()
dialog_info = InfoDialog(app_info, self.table_apps.get_selected_app_icon(), self.locale_keys, self.table_apps.get_selected_app().model.get_type(), self.screen_size) dialog_info = InfoDialog(app_info, self.table_apps.get_selected_app_icon(), self.locale_keys, self.table_apps.get_selected_app().model.get_type(), self.screen_size)
dialog_info.exec_() dialog_info.exec_()
def _finish_get_history(self, app: dict): def _finish_get_history(self, app: dict):
self._release_lock()
self.finish_action() self.finish_action()
self.change_update_state() self.change_update_state()
@@ -641,7 +596,7 @@ class ManageWindow(QWidget):
word = self.input_search.text().strip() word = self.input_search.text().strip()
if word and self._acquire_lock(): if word:
self._handle_console_option(False) self._handle_console_option(False)
self.ref_checkbox_only_apps.setVisible(False) self.ref_checkbox_only_apps.setVisible(False)
self.ref_checkbox_updates.setVisible(False) self.ref_checkbox_updates.setVisible(False)
@@ -651,34 +606,30 @@ class ManageWindow(QWidget):
self.thread_search.start() self.thread_search.start()
def _finish_search(self, apps_found: List[Application]): def _finish_search(self, apps_found: List[Application]):
self._release_lock()
self.finish_action() self.finish_action()
self.update_apps(apps_found) self.ref_bt_upgrade.setVisible(False)
self.update_apps(apps_found, update_check_enabled=False)
def install_app(self, app: ApplicationView): def install_app(self, app: ApplicationView):
if self._acquire_lock(): pwd = None
requires_root = self.manager.requires_root('install', self.table_apps.get_selected_app().model)
pwd = None if not is_root() and requires_root:
requires_root = self.manager.requires_root('install', self.table_apps.get_selected_app().model) pwd, ok = ask_root_password(self.locale_keys)
if not is_root() and requires_root: if not ok:
pwd, ok = ask_root_password(self.locale_keys) return
if not ok: self._handle_console_option(True)
self._release_lock() self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.installing'], app.model.base_data.name))
return
self._handle_console_option(True) self.thread_install.app = app
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.installing'], app.model.base_data.name)) self.thread_install.root_password = pwd
self.thread_install.start()
self.thread_install.app = app
self.thread_install.root_password = pwd
self.thread_install.start()
def _finish_install(self, success: bool): def _finish_install(self, success: bool):
self.input_search.setText('') self.input_search.setText('')
self.finish_action() self.finish_action()
self._release_lock()
if success: if success:
if self._can_notify_user(): if self._can_notify_user():