mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-08 03:54:15 +02:00
supporting snaps
This commit is contained in:
@@ -3,10 +3,10 @@ from threading import Lock
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QColor, QCursor
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QCursor
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
|
||||
QHeaderView, QLabel
|
||||
QHeaderView, QLabel, QHBoxLayout
|
||||
|
||||
from fpakman.core import resource
|
||||
from fpakman.core.model import ApplicationStatus
|
||||
@@ -26,7 +26,7 @@ class UpdateToggleButton(QToolButton):
|
||||
self.icon_on = QIcon(resource.get_path('img/toggle_on.svg'))
|
||||
self.icon_off = QIcon(resource.get_path('img/toggle_off.svg'))
|
||||
self.setIcon(self.icon_on)
|
||||
self.setStyleSheet('border: 0px;')
|
||||
self.setStyleSheet('QToolButton { border: 0px; }')
|
||||
self.setToolTip(locale_keys['manage_window.apps_table.upgrade_toggle.tooltip'])
|
||||
|
||||
if not checked:
|
||||
@@ -40,15 +40,15 @@ class UpdateToggleButton(QToolButton):
|
||||
|
||||
class AppsTable(QTableWidget):
|
||||
|
||||
def __init__(self, parent: QWidget, icon_cache: Cache, disk_cache: bool):
|
||||
def __init__(self, parent: QWidget, icon_cache: Cache, disk_cache: bool, download_icons: bool):
|
||||
super(AppsTable, self).__init__()
|
||||
self.setParent(parent)
|
||||
self.window = parent
|
||||
self.disk_cache = disk_cache
|
||||
self.download_icons = download_icons
|
||||
self.column_names = [parent.locale_keys[key].capitalize() for key in ['name',
|
||||
'version',
|
||||
'latest_version',
|
||||
'flatpak.info.description',
|
||||
'description',
|
||||
'type',
|
||||
'installed',
|
||||
'manage_window.columns.update']]
|
||||
@@ -56,6 +56,7 @@ class AppsTable(QTableWidget):
|
||||
self.setFocusPolicy(Qt.NoFocus)
|
||||
self.setShowGrid(False)
|
||||
self.verticalHeader().setVisible(False)
|
||||
self.horizontalHeader().setVisible(False)
|
||||
self.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.setHorizontalHeaderLabels(self.column_names)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
@@ -74,13 +75,19 @@ class AppsTable(QTableWidget):
|
||||
|
||||
menu_row = QMenu()
|
||||
|
||||
if app.model.has_info():
|
||||
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)
|
||||
menu_row.addAction(action_info)
|
||||
|
||||
if app.model.installed:
|
||||
|
||||
if app.model.has_info():
|
||||
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)
|
||||
menu_row.addAction(action_info)
|
||||
if app.model.can_be_refreshed():
|
||||
action_history = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.refresh"])
|
||||
action_history.setIcon(QIcon(resource.get_path('img/refresh.svg')))
|
||||
action_history.triggered.connect(self._refresh_app)
|
||||
menu_row.addAction(action_history)
|
||||
|
||||
if app.model.has_history():
|
||||
action_history = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.history"])
|
||||
@@ -99,6 +106,7 @@ class AppsTable(QTableWidget):
|
||||
action_downgrade.triggered.connect(self._downgrade_app)
|
||||
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
|
||||
menu_row.addAction(action_downgrade)
|
||||
|
||||
else:
|
||||
if app.model.can_be_installed():
|
||||
action_install = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.install"])
|
||||
@@ -119,15 +127,17 @@ class AppsTable(QTableWidget):
|
||||
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)))
|
||||
|
||||
if self.download_icons:
|
||||
self.network_man.get(QNetworkRequest(QUrl(app_v.model.base_data.icon_url)))
|
||||
|
||||
app_name = self.item(idx, 0).text()
|
||||
|
||||
if not app_name or app_name == '...':
|
||||
self.item(idx, 0).setText(app_v.model.base_data.name)
|
||||
|
||||
self.cellWidget(idx, 2).setText(app_v.model.base_data.latest_version)
|
||||
self._set_col_description(self.item(idx, 3), app_v)
|
||||
self._set_col_version(idx, app_v)
|
||||
self._set_col_description(idx, app_v)
|
||||
app_v.status = ApplicationViewStatus.READY
|
||||
|
||||
self.window.resize_and_center()
|
||||
@@ -156,6 +166,9 @@ class AppsTable(QTableWidget):
|
||||
locale_keys=self.window.locale_keys):
|
||||
self.window.downgrade_app(selected_app)
|
||||
|
||||
def _refresh_app(self):
|
||||
self.window.refresh(self.get_selected_app())
|
||||
|
||||
def _get_app_info(self):
|
||||
self.window.get_app_info(self.get_selected_app())
|
||||
|
||||
@@ -195,43 +208,67 @@ class AppsTable(QTableWidget):
|
||||
|
||||
if app_views:
|
||||
for idx, app_v in enumerate(app_views):
|
||||
col_name = self._gen_col_name(app_v)
|
||||
self.setItem(idx, 0, col_name)
|
||||
|
||||
col_version = QLabel(app_v.model.base_data.version)
|
||||
col_version.setAlignment(Qt.AlignCenter)
|
||||
self.setCellWidget(idx, 1, col_version)
|
||||
|
||||
col_release = QLabel(app_v.get_async_attr('latest_version'))
|
||||
col_release.setAlignment(Qt.AlignCenter)
|
||||
self.setCellWidget(idx, 2, col_release)
|
||||
|
||||
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.setStyleSheet("color: orange")
|
||||
|
||||
col_description = QTableWidgetItem()
|
||||
col_description.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||
self._set_col_description(col_description, app_v)
|
||||
|
||||
self.setItem(idx, 3, col_description)
|
||||
|
||||
col_type = QLabel(app_v.model.get_type())
|
||||
col_type.setAlignment(Qt.AlignCenter)
|
||||
self.setCellWidget(idx, 4, col_type)
|
||||
|
||||
col_installed = QLabel()
|
||||
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, 5, col_installed)
|
||||
self._set_col_name(idx, app_v)
|
||||
self._set_col_version(idx, app_v)
|
||||
self._set_col_description(idx, app_v)
|
||||
self._set_col_type(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
|
||||
self.setCellWidget(idx, 6, col_update)
|
||||
self.setCellWidget(idx, 5, col_update)
|
||||
|
||||
def _gen_col_name(self, app_v: ApplicationView):
|
||||
def _set_col_installed(self, idx: int, app_v: ApplicationView):
|
||||
col_installed = QLabel()
|
||||
|
||||
if app_v.model.installed:
|
||||
img_name = 'checked'
|
||||
tooltip = self.window.locale_keys['installed']
|
||||
else:
|
||||
img_name = 'red_cross'
|
||||
tooltip = self.window.locale_keys['uninstalled']
|
||||
|
||||
col_installed.setPixmap((QPixmap(resource.get_path('img/{}.svg'.format(img_name)))))
|
||||
col_installed.setAlignment(Qt.AlignCenter)
|
||||
col_installed.setToolTip(tooltip)
|
||||
|
||||
self.setCellWidget(idx, 4, col_installed)
|
||||
|
||||
def _set_col_type(self, idx: int, app_v: ApplicationView):
|
||||
col_type = QLabel()
|
||||
pixmap = QPixmap(app_v.model.get_default_icon_path())
|
||||
col_type.setPixmap(pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
col_type.setAlignment(Qt.AlignCenter)
|
||||
col_type.setToolTip('{}: {}'.format(self.window.locale_keys['type'], app_v.model.get_type()))
|
||||
self.setCellWidget(idx, 3, col_type)
|
||||
|
||||
def _set_col_version(self, idx: int, app_v: ApplicationView):
|
||||
label_version = QLabel(app_v.model.base_data.version if app_v.model.base_data.version else '?')
|
||||
label_version.setAlignment(Qt.AlignCenter)
|
||||
|
||||
col_version = QWidget()
|
||||
col_version.setLayout(QHBoxLayout())
|
||||
col_version.layout().addWidget(label_version)
|
||||
|
||||
if app_v.model.base_data.version:
|
||||
tooltip = self.window.locale_keys['version.installed'] if app_v.model.installed else self.window.locale_keys['version']
|
||||
else:
|
||||
tooltip = self.window.locale_keys['version.unknown']
|
||||
|
||||
if app_v.model.update:
|
||||
label_version.setStyleSheet("color: #32CD32")
|
||||
tooltip = self.window.locale_keys['version.installed_outdated']
|
||||
|
||||
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:
|
||||
tooltip = '{}. {}: {}'.format(tooltip, self.window.locale_keys['version.latest'], app_v.model.base_data.latest_version)
|
||||
|
||||
col_version.setToolTip(tooltip)
|
||||
self.setCellWidget(idx, 1, col_version)
|
||||
|
||||
def _set_col_name(self, idx: int, 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)
|
||||
col.setToolTip(self.window.locale_keys['app.name'].lower())
|
||||
|
||||
if self.disk_cache and app_v.model.supports_disk_cache() and os.path.exists(app_v.model.get_disk_icon_path()):
|
||||
with open(app_v.model.get_disk_icon_path(), 'rb') as f:
|
||||
@@ -240,6 +277,7 @@ class AppsTable(QTableWidget):
|
||||
pixmap.loadFromData(icon_bytes)
|
||||
icon = QIcon(pixmap)
|
||||
self.icon_cache.add_non_existing(app_v.model.base_data.icon_url, {'icon': icon, 'bytes': icon_bytes})
|
||||
|
||||
elif not app_v.model.base_data.icon_url:
|
||||
icon = QIcon(app_v.model.get_default_icon_path())
|
||||
else:
|
||||
@@ -247,9 +285,11 @@ class AppsTable(QTableWidget):
|
||||
icon = icon_data['icon'] if icon_data else QIcon(app_v.model.get_default_icon_path())
|
||||
|
||||
col.setIcon(icon)
|
||||
return col
|
||||
self.setItem(idx, 0, col)
|
||||
|
||||
def _set_col_description(self, col: QTableWidgetItem, app_v: ApplicationView):
|
||||
def _set_col_description(self, idx: int, app_v: ApplicationView):
|
||||
col = QTableWidgetItem()
|
||||
col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||
desc = app_v.get_async_attr('description', strip_html=True)
|
||||
|
||||
if desc:
|
||||
@@ -258,6 +298,8 @@ class AppsTable(QTableWidget):
|
||||
if app_v.model.status == ApplicationStatus.READY:
|
||||
col.setToolTip(desc)
|
||||
|
||||
self.setItem(idx, 2, col)
|
||||
|
||||
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
|
||||
header_horizontal = self.horizontalHeader()
|
||||
for i in range(self.columnCount()):
|
||||
|
||||
@@ -2,7 +2,7 @@ import operator
|
||||
from functools import reduce
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtGui import QIcon, QColor
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class HistoryDialog(QDialog):
|
||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||
|
||||
if current_app_commit:
|
||||
item.setBackground(Qt.darkYellow if row != 0 else Qt.darkGreen)
|
||||
item.setBackground(QColor('#ffbf00' if row != 0 else '#32CD32'))
|
||||
tip = '{}. {}.'.format(locale_keys['popup.history.selected.tooltip'], locale_keys['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
|
||||
|
||||
item.setToolTip(tip)
|
||||
|
||||
@@ -1,37 +1,47 @@
|
||||
from PyQt5.QtCore import QSize
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QFormLayout, QGroupBox, \
|
||||
QLineEdit, QLabel, QPlainTextEdit
|
||||
QLineEdit, QLabel
|
||||
|
||||
from fpakman.util import util
|
||||
|
||||
|
||||
class InfoDialog(QDialog):
|
||||
|
||||
def __init__(self, app: dict, app_icon: QIcon, locale_keys: dict):
|
||||
def __init__(self, app: dict, app_icon: QIcon, locale_keys: dict, app_type: str, screen_size: QSize()):
|
||||
super(InfoDialog, self).__init__()
|
||||
self.setWindowTitle(app['name'])
|
||||
self.setWindowIcon(app_icon)
|
||||
self.screen_size = screen_size
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
gbox_info_layout = QFormLayout()
|
||||
gbox_info = QGroupBox()
|
||||
gbox_info.setMaximumHeight(self.screen_size.height() - self.screen_size.height() * 0.1)
|
||||
gbox_info_layout = QFormLayout()
|
||||
gbox_info.setLayout(gbox_info_layout)
|
||||
|
||||
layout.addWidget(gbox_info)
|
||||
|
||||
for attr in sorted(app.keys()):
|
||||
|
||||
if attr != 'name' and app[attr]:
|
||||
if attr == 'description':
|
||||
text = QPlainTextEdit()
|
||||
text.appendHtml(app[attr])
|
||||
else:
|
||||
text = QLineEdit()
|
||||
text.setText(app[attr])
|
||||
text.setCursorPosition(0)
|
||||
text.setStyleSheet("width: 400px")
|
||||
val = app[attr]
|
||||
text = QLineEdit()
|
||||
text.setToolTip(val)
|
||||
|
||||
if attr == 'license' and val.strip() == 'unset':
|
||||
val = locale_keys['license.unset']
|
||||
|
||||
if attr == 'description':
|
||||
val = util.strip_html(val)
|
||||
val = val[0:40] + '...'
|
||||
|
||||
text.setText(val)
|
||||
text.setCursorPosition(0)
|
||||
text.setStyleSheet("width: 400px")
|
||||
text.setReadOnly(True)
|
||||
|
||||
label = QLabel("{}: ".format(locale_keys.get('flatpak.info.' + attr, attr)).capitalize())
|
||||
label = QLabel("{}: ".format(locale_keys.get(app_type + '.info.' + attr, attr)).capitalize())
|
||||
label.setStyleSheet("font-weight: bold")
|
||||
|
||||
gbox_info_layout.addRow(label, text)
|
||||
|
||||
@@ -2,14 +2,15 @@ import time
|
||||
from threading import Lock
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication, Qt
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication, Qt, QSize
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
|
||||
|
||||
from fpakman.core import resource, system
|
||||
from fpakman.core.controller import FlatpakManager, ApplicationManager
|
||||
from fpakman.core.controller import ApplicationManager
|
||||
from fpakman.core.model import Application
|
||||
from fpakman.util.cache import Cache
|
||||
from fpakman.view.qt import dialog
|
||||
from fpakman.view.qt.about import AboutDialog
|
||||
from fpakman.view.qt.window import ManageWindow
|
||||
|
||||
@@ -39,12 +40,14 @@ class UpdateCheck(QThread):
|
||||
|
||||
class TrayIcon(QSystemTrayIcon):
|
||||
|
||||
def __init__(self, locale_keys: dict, manager: ApplicationManager, icon_cache: Cache, disk_cache: bool, check_interval: int = 60, update_notification: bool = True):
|
||||
def __init__(self, locale_keys: dict, manager: ApplicationManager, icon_cache: Cache, disk_cache: bool, download_icons: bool, screen_size: QSize, 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.download_icons = download_icons
|
||||
self.screen_size = screen_size
|
||||
|
||||
self.icon_default = QIcon(resource.get_path('img/logo.png'))
|
||||
self.icon_update = QIcon(resource.get_path('img/logo_update.png'))
|
||||
@@ -74,6 +77,12 @@ class TrayIcon(QSystemTrayIcon):
|
||||
self.update_notification = update_notification
|
||||
self.lock_notify = Lock()
|
||||
|
||||
self.activated.connect(self.handle_click)
|
||||
|
||||
def handle_click(self, reason):
|
||||
if reason == self.Trigger:
|
||||
self.show_manage_window()
|
||||
|
||||
def notify_updates(self, updates: List[Application]):
|
||||
|
||||
self.lock_notify.acquire()
|
||||
@@ -110,12 +119,14 @@ class TrayIcon(QSystemTrayIcon):
|
||||
manager=self.manager,
|
||||
icon_cache=self.icon_cache,
|
||||
tray_icon=self,
|
||||
disk_cache=self.disk_cache)
|
||||
disk_cache=self.disk_cache,
|
||||
download_icons=self.download_icons,
|
||||
screen_size=self.screen_size)
|
||||
|
||||
if self.manage_window.isMinimized():
|
||||
self.manage_window.setWindowState(Qt.WindowNoState)
|
||||
else:
|
||||
self.manage_window.refresh()
|
||||
elif not self.manage_window.isVisible():
|
||||
self.manage_window.refresh_apps()
|
||||
self.manage_window.show()
|
||||
|
||||
def show_about(self):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import subprocess
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
@@ -8,14 +9,51 @@ from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from fpakman.core.controller import ApplicationManager
|
||||
from fpakman.core.exception import NoInternetException
|
||||
from fpakman.core.model import ApplicationStatus
|
||||
from fpakman.core.system import FpakmanProcess
|
||||
from fpakman.util.cache import Cache
|
||||
from fpakman.view.qt import dialog
|
||||
from fpakman.view.qt.view_model import ApplicationView
|
||||
|
||||
|
||||
class UpdateSelectedApps(QThread):
|
||||
class AsyncAction(QThread):
|
||||
|
||||
signal_finished = pyqtSignal(bool)
|
||||
def notify_subproc_outputs(self, proc: FpakmanProcess, signal) -> bool:
|
||||
"""
|
||||
:param subproc:
|
||||
:param signal:
|
||||
:param success:
|
||||
:return: if the subprocess succeeded
|
||||
"""
|
||||
signal.emit(' '.join(proc.subproc.args) + '\n')
|
||||
|
||||
success, already_succeeded = True, False
|
||||
|
||||
for output in proc.subproc.stdout:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
signal.emit(line)
|
||||
|
||||
if proc.success_pgrase and proc.success_pgrase in line:
|
||||
already_succeeded = True
|
||||
|
||||
if already_succeeded:
|
||||
return True
|
||||
|
||||
for output in proc.subproc.stderr:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
if proc.wrong_error_phrase and proc.wrong_error_phrase in line:
|
||||
continue
|
||||
else:
|
||||
success = False
|
||||
signal.emit(line)
|
||||
|
||||
return success
|
||||
|
||||
|
||||
class UpdateSelectedApps(AsyncAction):
|
||||
|
||||
signal_finished = pyqtSignal(bool, int)
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, manager: ApplicationManager, apps_to_update: List[ApplicationView] = None):
|
||||
@@ -25,31 +63,20 @@ class UpdateSelectedApps(QThread):
|
||||
|
||||
def run(self):
|
||||
|
||||
error = False
|
||||
success = False
|
||||
|
||||
for app in self.apps_to_update:
|
||||
|
||||
subproc = self.manager.update_and_stream(app.model)
|
||||
process = self.manager.update_and_stream(app.model)
|
||||
success = self.notify_subproc_outputs(process, self.signal_output)
|
||||
|
||||
self.signal_output.emit(' '.join(subproc.args) + '\n')
|
||||
|
||||
for output in subproc.stdout:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
self.signal_output.emit(line)
|
||||
|
||||
for output in subproc.stderr:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
error = True
|
||||
self.signal_output.emit(line)
|
||||
|
||||
self.signal_output.emit('\n')
|
||||
|
||||
if error:
|
||||
if not success:
|
||||
break
|
||||
else:
|
||||
self.signal_output.emit('\n')
|
||||
|
||||
self.signal_finished.emit(not error)
|
||||
self.signal_finished.emit(success, len(self.apps_to_update))
|
||||
self.apps_to_update = None
|
||||
|
||||
|
||||
class RefreshApps(QThread):
|
||||
@@ -64,8 +91,8 @@ class RefreshApps(QThread):
|
||||
self.signal.emit(self.manager.read_installed())
|
||||
|
||||
|
||||
class UninstallApp(QThread):
|
||||
signal_finished = pyqtSignal()
|
||||
class UninstallApp(AsyncAction):
|
||||
signal_finished = pyqtSignal(bool)
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, manager: ApplicationManager, icon_cache: Cache, app: ApplicationView = None):
|
||||
@@ -73,33 +100,23 @@ class UninstallApp(QThread):
|
||||
self.app = app
|
||||
self.manager = manager
|
||||
self.icon_cache = icon_cache
|
||||
self.root_password = None
|
||||
|
||||
def run(self):
|
||||
if self.app:
|
||||
subproc = self.manager.uninstall_and_stream(self.app.model)
|
||||
self.signal_output.emit(' '.join(subproc.args) + '\n')
|
||||
process = self.manager.uninstall_and_stream(self.app.model, self.root_password)
|
||||
success = self.notify_subproc_outputs(process, self.signal_output)
|
||||
|
||||
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:
|
||||
if success:
|
||||
self.icon_cache.delete(self.app.model.base_data.icon_url)
|
||||
self.manager.clean_cache_for(self.app.model)
|
||||
|
||||
self.signal_finished.emit()
|
||||
self.signal_finished.emit(success)
|
||||
self.app = None
|
||||
self.root_password = None
|
||||
|
||||
|
||||
class DowngradeApp(QThread):
|
||||
class DowngradeApp(AsyncAction):
|
||||
signal_finished = pyqtSignal(bool)
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
@@ -113,18 +130,15 @@ class DowngradeApp(QThread):
|
||||
def run(self):
|
||||
if self.app:
|
||||
|
||||
success = True
|
||||
success = False
|
||||
try:
|
||||
stream = self.manager.downgrade_app(self.app.model, self.root_password)
|
||||
process = self.manager.downgrade_app(self.app.model, self.root_password)
|
||||
|
||||
if stream is None:
|
||||
if process 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 stream:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
self.signal_output.emit(line)
|
||||
success = self.notify_subproc_outputs(process, self.signal_output)
|
||||
except (requests.exceptions.ConnectionError, NoInternetException):
|
||||
success = False
|
||||
self.signal_output.emit(self.locale_keys['internet.required'])
|
||||
@@ -180,54 +194,51 @@ class SearchApps(QThread):
|
||||
apps_found = []
|
||||
|
||||
if self.word:
|
||||
apps_found = self.manager.search(self.word)
|
||||
res = self.manager.search(self.word)
|
||||
apps_found.extend(res['installed'])
|
||||
apps_found.extend(res['new'])
|
||||
|
||||
self.signal_finished.emit(apps_found)
|
||||
self.word = None
|
||||
|
||||
|
||||
class InstallApp(QThread):
|
||||
class InstallApp(AsyncAction):
|
||||
|
||||
signal_finished = pyqtSignal(bool)
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, manager: ApplicationManager, disk_cache: bool, icon_cache: Cache, app: ApplicationView = None):
|
||||
def __init__(self, manager: ApplicationManager, disk_cache: bool, icon_cache: Cache, locale_keys: dict, app: ApplicationView = None):
|
||||
super(InstallApp, self).__init__()
|
||||
self.app = app
|
||||
self.manager = manager
|
||||
self.icon_cache = icon_cache
|
||||
self.disk_cache = disk_cache
|
||||
self.locale_keys = locale_keys
|
||||
self.root_password = None
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.app:
|
||||
subproc = self.manager.install_and_stream(self.app.model)
|
||||
self.signal_output.emit(' '.join(subproc.args) + '\n')
|
||||
success = False
|
||||
|
||||
for output in subproc.stdout:
|
||||
line = output.decode().strip()
|
||||
if line:
|
||||
self.signal_output.emit(line)
|
||||
try:
|
||||
process = self.manager.install_and_stream(self.app.model, self.root_password)
|
||||
success = self.notify_subproc_outputs(process, self.signal_output)
|
||||
|
||||
error = False
|
||||
if success and self.disk_cache:
|
||||
self.app.model.installed = True
|
||||
|
||||
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():
|
||||
icon_data = self.icon_cache.get(self.app.model.base_data.icon_url)
|
||||
self.manager.cache_to_disk(app=self.app.model,
|
||||
icon_bytes=icon_data.get('bytes') if icon_data else None,
|
||||
only_icon=False)
|
||||
|
||||
self.app = None
|
||||
self.signal_finished.emit(not error)
|
||||
if self.app.model.supports_disk_cache():
|
||||
icon_data = self.icon_cache.get(self.app.model.base_data.icon_url)
|
||||
self.manager.cache_to_disk(app=self.app.model,
|
||||
icon_bytes=icon_data.get('bytes') if icon_data else None,
|
||||
only_icon=False)
|
||||
except (requests.exceptions.ConnectionError, NoInternetException):
|
||||
success = False
|
||||
self.signal_output.emit(self.locale_keys['internet.required'])
|
||||
finally:
|
||||
self.app = None
|
||||
self.signal_finished.emit(success)
|
||||
|
||||
|
||||
class AnimateProgress(QThread):
|
||||
@@ -292,3 +303,30 @@ class VerifyModels(QThread):
|
||||
break
|
||||
|
||||
self.apps = None
|
||||
|
||||
|
||||
class RefreshApp(AsyncAction):
|
||||
|
||||
signal_finished = pyqtSignal(bool)
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, manager: ApplicationManager, app: ApplicationView = None):
|
||||
super(RefreshApp, self).__init__()
|
||||
self.app = app
|
||||
self.manager = manager
|
||||
self.root_password = None
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.app:
|
||||
success = False
|
||||
|
||||
try:
|
||||
process = self.manager.refresh(self.app.model, self.root_password)
|
||||
success = self.notify_subproc_outputs(process, self.signal_output)
|
||||
except (requests.exceptions.ConnectionError, NoInternetException):
|
||||
success = False
|
||||
self.signal_output.emit(self.locale_keys['internet.required'])
|
||||
finally:
|
||||
self.app = None
|
||||
self.signal_finished.emit(success)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
from fpakman.core.model import Application, ApplicationStatus
|
||||
from fpakman.util import util
|
||||
from fpakman.core.model import Application, ApplicationStatus, FlatpakApplication
|
||||
|
||||
|
||||
class ApplicationViewStatus(Enum):
|
||||
@@ -19,7 +19,7 @@ class ApplicationView:
|
||||
|
||||
def get_async_attr(self, attr: str, strip_html: bool = False, default: str = '...'):
|
||||
|
||||
if isinstance(self.model, FlatpakApplication) and self.model.runtime:
|
||||
if self.model.is_library():
|
||||
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
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import operator
|
||||
from functools import reduce
|
||||
from threading import Lock
|
||||
from typing import List
|
||||
from typing import List, Set
|
||||
|
||||
from PyQt5.QtCore import QEvent
|
||||
from PyQt5.QtCore import QEvent, Qt
|
||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
|
||||
QSizePolicy, QLabel, QPlainTextEdit, QLineEdit, QProgressBar
|
||||
QSizePolicy, QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QHBoxLayout
|
||||
|
||||
from fpakman.core import resource
|
||||
from fpakman.core import resource, system
|
||||
from fpakman.core.controller import ApplicationManager
|
||||
from fpakman.core.model import Application
|
||||
from fpakman.util.cache import Cache
|
||||
@@ -17,7 +17,7 @@ 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, VerifyModels
|
||||
GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp
|
||||
from fpakman.view.qt.view_model import ApplicationView
|
||||
|
||||
DARK_ORANGE = '#FF4500'
|
||||
@@ -26,7 +26,7 @@ DARK_ORANGE = '#FF4500'
|
||||
class ManageWindow(QWidget):
|
||||
__BASE_HEIGHT__ = 400
|
||||
|
||||
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, tray_icon=None):
|
||||
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, download_icons: bool, screen_size, tray_icon=None):
|
||||
super(ManageWindow, self).__init__()
|
||||
self.locale_keys = locale_keys
|
||||
self.manager = manager
|
||||
@@ -37,6 +37,8 @@ class ManageWindow(QWidget):
|
||||
self.label_flatpak = None
|
||||
self.icon_cache = icon_cache
|
||||
self.disk_cache = disk_cache
|
||||
self.download_icons = download_icons
|
||||
self.screen_size = screen_size
|
||||
|
||||
self.icon_flathub = QIcon(resource.get_path('img/logo.svg'))
|
||||
self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
|
||||
@@ -46,14 +48,20 @@ class ManageWindow(QWidget):
|
||||
self.layout = QVBoxLayout()
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.toolbar_top = QToolBar()
|
||||
self.toolbar_top.addWidget(self._new_spacer())
|
||||
|
||||
self.label_status = QLabel()
|
||||
self.label_status.setText('')
|
||||
self.label_status.setStyleSheet("font-weight: bold")
|
||||
self.toolbar_top.addWidget(self.label_status)
|
||||
|
||||
self.toolbar_search = QToolBar()
|
||||
self.toolbar_search.setStyleSheet("spacing: 0px;")
|
||||
self.toolbar_search.setContentsMargins(0, 0, 0, 0)
|
||||
self.toolbar_search.addWidget(self._new_spacer())
|
||||
|
||||
label_pre_search = QLabel()
|
||||
label_pre_search.setStyleSheet(
|
||||
"background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;")
|
||||
label_pre_search.setStyleSheet("background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;")
|
||||
self.toolbar_search.addWidget(label_pre_search)
|
||||
|
||||
self.input_search = QLineEdit()
|
||||
@@ -69,30 +77,33 @@ class ManageWindow(QWidget):
|
||||
label_pos_search.setStyleSheet("background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;")
|
||||
self.toolbar_search.addWidget(label_pos_search)
|
||||
|
||||
self.toolbar_search.addWidget(self._new_spacer())
|
||||
self.layout.addWidget(self.toolbar_search)
|
||||
self.ref_toolbar_search = self.toolbar_top.addWidget(self.toolbar_search)
|
||||
self.toolbar_top.addWidget(self._new_spacer())
|
||||
self.layout.addWidget(self.toolbar_top)
|
||||
|
||||
toolbar = QToolBar()
|
||||
|
||||
self.checkbox_only_apps = QCheckBox()
|
||||
self.checkbox_only_apps.setText(self.locale_keys['manage_window.checkbox.only_apps'])
|
||||
self.checkbox_only_apps.setChecked(True)
|
||||
self.checkbox_only_apps.stateChanged.connect(self.filter_only_apps)
|
||||
toolbar.addWidget(self.checkbox_only_apps)
|
||||
self.checkbox_only_apps.stateChanged.connect(self._handle_filter_only_apps)
|
||||
self.ref_checkbox_only_apps = toolbar.addWidget(self.checkbox_only_apps)
|
||||
|
||||
toolbar.addWidget(self._new_spacer())
|
||||
self.checkbox_updates = QCheckBox()
|
||||
self.checkbox_updates.setText(self.locale_keys['updates'].capitalize())
|
||||
self.checkbox_updates.stateChanged.connect(self._handle_updates_filter)
|
||||
self.ref_checkbox_updates = toolbar.addWidget(self.checkbox_updates)
|
||||
|
||||
self.label_status = QLabel()
|
||||
self.label_status.setText('')
|
||||
self.label_status.setStyleSheet("font-weight: bold")
|
||||
toolbar.addWidget(self.label_status)
|
||||
self.extra_filters = QWidget()
|
||||
self.extra_filters.setLayout(QHBoxLayout())
|
||||
toolbar.addWidget(self.extra_filters)
|
||||
|
||||
toolbar.addWidget(self._new_spacer())
|
||||
|
||||
self.bt_refresh = QToolButton()
|
||||
self.bt_refresh.setToolTip(locale_keys['manage_window.bt.refresh.tooltip'])
|
||||
self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
|
||||
self.bt_refresh.clicked.connect(lambda: self.refresh(keep_console=False))
|
||||
self.bt_refresh.clicked.connect(lambda: self.refresh_apps(keep_console=False))
|
||||
toolbar.addWidget(self.bt_refresh)
|
||||
|
||||
self.bt_upgrade = QToolButton()
|
||||
@@ -104,7 +115,7 @@ class ManageWindow(QWidget):
|
||||
|
||||
self.layout.addWidget(toolbar)
|
||||
|
||||
self.table_apps = AppsTable(self, self.icon_cache, disk_cache=self.disk_cache)
|
||||
self.table_apps = AppsTable(self, self.icon_cache, disk_cache=self.disk_cache, download_icons=self.download_icons)
|
||||
self.table_apps.change_headers_policy()
|
||||
|
||||
self.layout.addWidget(self.table_apps)
|
||||
@@ -132,7 +143,7 @@ class ManageWindow(QWidget):
|
||||
self.thread_update.signal_finished.connect(self._finish_update_selected)
|
||||
|
||||
self.thread_refresh = RefreshApps(self.manager)
|
||||
self.thread_refresh.signal.connect(self._finish_refresh)
|
||||
self.thread_refresh.signal.connect(self._finish_refresh_apps)
|
||||
|
||||
self.thread_uninstall = UninstallApp(self.manager, self.icon_cache)
|
||||
self.thread_uninstall.signal_output.connect(self._update_action_output)
|
||||
@@ -151,7 +162,7 @@ class ManageWindow(QWidget):
|
||||
self.thread_search = SearchApps(self.manager)
|
||||
self.thread_search.signal_finished.connect(self._finish_search)
|
||||
|
||||
self.thread_install = InstallApp(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache)
|
||||
self.thread_install = InstallApp(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache, locale_keys=self.locale_keys)
|
||||
self.thread_install.signal_output.connect(self._update_action_output)
|
||||
self.thread_install.signal_finished.connect(self._finish_install)
|
||||
|
||||
@@ -161,10 +172,11 @@ class ManageWindow(QWidget):
|
||||
self.thread_verify_models = VerifyModels()
|
||||
self.thread_verify_models.signal_updates.connect(self._notify_model_data_change)
|
||||
|
||||
self.thread_refresh_app = RefreshApp(manager=self.manager)
|
||||
self.thread_refresh_app.signal_finished.connect(self._finish_refresh)
|
||||
self.thread_refresh_app.signal_output.connect(self._update_action_output)
|
||||
|
||||
self.toolbar_bottom = QToolBar()
|
||||
self.label_updates = QLabel('')
|
||||
self.label_updates.setStyleSheet("color: {}; font-weight: bold".format(DARK_ORANGE))
|
||||
self.ref_label_updates = self.toolbar_bottom.addWidget(self.label_updates)
|
||||
|
||||
self.toolbar_bottom.addWidget(self._new_spacer())
|
||||
|
||||
@@ -174,10 +186,34 @@ class ManageWindow(QWidget):
|
||||
|
||||
self.toolbar_bottom.addWidget(self._new_spacer())
|
||||
|
||||
self.label_updates = QLabel()
|
||||
self.ref_label_updates = self.toolbar_bottom.addWidget(self.label_updates)
|
||||
|
||||
self.layout.addWidget(self.toolbar_bottom)
|
||||
|
||||
self.centralize()
|
||||
|
||||
self.filter_only_apps = True
|
||||
self.filter_types = set()
|
||||
self.filter_updates = False
|
||||
|
||||
def _handle_updates_filter(self, status: int):
|
||||
self.filter_updates = status == 2
|
||||
self.apply_filters()
|
||||
|
||||
def _handle_filter_only_apps(self, status: int):
|
||||
self.filter_only_apps = status == 2
|
||||
self.apply_filters()
|
||||
|
||||
def _handle_type_filter(self, status: int, app_type: str):
|
||||
|
||||
if status == 2:
|
||||
self.filter_types.add(app_type)
|
||||
elif app_type in self.filter_types:
|
||||
self.filter_types.remove(app_type)
|
||||
|
||||
self.apply_filters()
|
||||
|
||||
def _notify_model_data_change(self):
|
||||
self.table_apps.fill_async_data()
|
||||
|
||||
@@ -234,61 +270,136 @@ class ManageWindow(QWidget):
|
||||
self.checkbox_console.setChecked(False)
|
||||
self.textarea_output.hide()
|
||||
|
||||
def refresh(self, keep_console: bool = True):
|
||||
def refresh_apps(self, keep_console: bool = True):
|
||||
|
||||
if self._acquire_lock():
|
||||
self.filter_types.clear()
|
||||
self.input_search.clear()
|
||||
|
||||
if not keep_console:
|
||||
self._handle_console_option(False)
|
||||
|
||||
self._begin_action(self.locale_keys['manage_window.status.refreshing'])
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
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(self, apps: List[Application]):
|
||||
def _finish_refresh_apps(self, apps: List[Application]):
|
||||
self.update_apps(apps)
|
||||
self.finish_action()
|
||||
self.ref_checkbox_only_apps.setVisible(True)
|
||||
self._release_lock()
|
||||
|
||||
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)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.locale_keys)
|
||||
|
||||
if not ok:
|
||||
self._release_lock()
|
||||
return
|
||||
|
||||
self._handle_console_option(True)
|
||||
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.uninstalling'], app.model.base_data.name))
|
||||
|
||||
self.thread_uninstall.app = app
|
||||
self.thread_uninstall.root_password = pwd
|
||||
self.thread_uninstall.start()
|
||||
|
||||
def _finish_uninstall(self):
|
||||
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)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.locale_keys)
|
||||
|
||||
if not ok:
|
||||
self._release_lock()
|
||||
return
|
||||
|
||||
self._handle_console_option(True)
|
||||
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.refreshing'], app.model.base_data.name))
|
||||
|
||||
self.thread_refresh_app.app = app
|
||||
self.thread_refresh_app.root_password = pwd
|
||||
self.thread_refresh_app.start()
|
||||
|
||||
def _finish_uninstall(self, success: bool):
|
||||
self.finish_action()
|
||||
self._release_lock()
|
||||
self.refresh()
|
||||
|
||||
if success:
|
||||
if self._can_notify_user():
|
||||
app = self.table_apps.get_selected_app()
|
||||
system.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['uninstalled']))
|
||||
|
||||
self.refresh_apps()
|
||||
else:
|
||||
if self._can_notify_user():
|
||||
app = self.table_apps.get_selected_app()
|
||||
system.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.uninstall.failed']))
|
||||
|
||||
self.checkbox_console.setChecked(True)
|
||||
|
||||
def _can_notify_user(self):
|
||||
return self.isHidden() or self.isMinimized()
|
||||
|
||||
def _finish_downgrade(self, success: bool):
|
||||
self.finish_action()
|
||||
self._release_lock()
|
||||
|
||||
if success:
|
||||
self.refresh()
|
||||
if self._can_notify_user():
|
||||
app = self.table_apps.get_selected_app()
|
||||
system.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['downgraded']))
|
||||
|
||||
self.refresh_apps()
|
||||
else:
|
||||
if self._can_notify_user():
|
||||
system.notify_user(self.locale_keys['notification.downgrade.failed'])
|
||||
|
||||
self.change_update_state(notify_tray=False)
|
||||
self.checkbox_console.setChecked(True)
|
||||
|
||||
def filter_only_apps(self, only_apps: int):
|
||||
def _finish_refresh(self, success: bool):
|
||||
self.finish_action()
|
||||
self._release_lock()
|
||||
|
||||
if success:
|
||||
self.refresh_apps()
|
||||
else:
|
||||
self.change_update_state(notify_tray=False)
|
||||
self.checkbox_console.setChecked(True)
|
||||
|
||||
def apply_filters(self):
|
||||
if self.apps:
|
||||
show_only_apps = True if only_apps == 2 else False
|
||||
|
||||
visible_apps = len(self.apps)
|
||||
for idx, app_v in enumerate(self.apps):
|
||||
hidden = show_only_apps and app_v.model.is_library()
|
||||
hidden = self.filter_only_apps and app_v.model.is_library()
|
||||
|
||||
if not hidden and self.filter_types is not None:
|
||||
hidden = app_v.model.get_type() not in self.filter_types
|
||||
|
||||
if not hidden and self.filter_updates:
|
||||
hidden = not app_v.model.update
|
||||
|
||||
self.table_apps.setRowHidden(idx, hidden)
|
||||
app_v.visible = not hidden
|
||||
visible_apps -= 1 if hidden else 0
|
||||
|
||||
self.change_update_state()
|
||||
self.change_update_state(change_filters=False)
|
||||
self.table_apps.change_headers_policy(QHeaderView.Stretch)
|
||||
self.table_apps.change_headers_policy()
|
||||
self.resize_and_center()
|
||||
self.resize_and_center(accept_lower_width=visible_apps > 0)
|
||||
|
||||
def change_update_state(self):
|
||||
def change_update_state(self, notify_tray: bool = True, change_filters: bool = True):
|
||||
|
||||
enable_bt_update = False
|
||||
|
||||
@@ -301,23 +412,38 @@ class ManageWindow(QWidget):
|
||||
else:
|
||||
app_updates += 1
|
||||
|
||||
total_updates = app_updates + library_updates
|
||||
if total_updates > 0:
|
||||
self.label_updates.setText('{}: {}'.format(self.locale_keys['manage_window.label.updates'], total_updates))
|
||||
self.label_updates.setToolTip('{} {} | {} {}'.format(app_updates,
|
||||
self.locale_keys['manage_window.checkbox.only_apps'].lower(),
|
||||
library_updates,
|
||||
self.locale_keys['others'].lower()))
|
||||
else:
|
||||
self.label_updates.setText('')
|
||||
|
||||
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])
|
||||
|
||||
total_updates = app_updates + library_updates
|
||||
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.setToolTip('{}: {} ( {} {} | {} {} )'.format(self.locale_keys['manage_window.label.updates'],
|
||||
total_updates,
|
||||
app_updates,
|
||||
self.locale_keys['manage_window.checkbox.only_apps'].lower(),
|
||||
library_updates,
|
||||
self.locale_keys['others'].lower()))
|
||||
|
||||
if not self.ref_checkbox_updates.isVisible():
|
||||
self.ref_checkbox_updates.setVisible(True)
|
||||
|
||||
if change_filters and not self.checkbox_updates.isChecked():
|
||||
self.checkbox_updates.setChecked(True)
|
||||
|
||||
if change_filters and library_updates > 0 and self.checkbox_only_apps.isChecked():
|
||||
self.checkbox_only_apps.setChecked(False)
|
||||
else:
|
||||
self.checkbox_updates.setChecked(False)
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
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):
|
||||
geo = self.frameGeometry()
|
||||
@@ -330,12 +456,12 @@ class ManageWindow(QWidget):
|
||||
self.apps = []
|
||||
|
||||
napps = 0 # number of apps (not libraries)
|
||||
available_types = set()
|
||||
|
||||
if apps:
|
||||
for app in apps:
|
||||
app_model = ApplicationView(model=app,
|
||||
visible=(not app.is_library()) or not self.checkbox_only_apps.isChecked())
|
||||
|
||||
app_model = ApplicationView(model=app, visible=(not app.is_library()) or not self.checkbox_only_apps.isChecked())
|
||||
available_types.add(app.get_type())
|
||||
napps += 1 if not app.is_library() else 0
|
||||
self.apps.append(app_model)
|
||||
|
||||
@@ -346,17 +472,41 @@ class ManageWindow(QWidget):
|
||||
self.checkbox_only_apps.setCheckable(True)
|
||||
self.checkbox_only_apps.setChecked(True)
|
||||
|
||||
self._update_type_filters(available_types)
|
||||
self.table_apps.update_apps(self.apps)
|
||||
self.apply_filters()
|
||||
self.change_update_state()
|
||||
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):
|
||||
def _update_type_filters(self, available_types: Set[str]):
|
||||
|
||||
self.filter_types = available_types
|
||||
|
||||
filters_layout = self.extra_filters.layout()
|
||||
for i in reversed(range(filters_layout.count())):
|
||||
filters_layout.itemAt(i).widget().setParent(None)
|
||||
|
||||
if available_types:
|
||||
for app_type in sorted(list(available_types)):
|
||||
checkbox_app_type = QCheckBox()
|
||||
checkbox_app_type.setChecked(True)
|
||||
checkbox_app_type.setText(app_type)
|
||||
|
||||
def handle_click(status: int, filter_type: str = app_type):
|
||||
self._handle_type_filter(status, filter_type)
|
||||
|
||||
checkbox_app_type.stateChanged.connect(handle_click)
|
||||
filters_layout.addWidget(checkbox_app_type)
|
||||
|
||||
def resize_and_center(self, accept_lower_width: bool = True):
|
||||
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())
|
||||
|
||||
if accept_lower_width or new_width > self.width():
|
||||
self.resize(new_width, self.height())
|
||||
|
||||
self.centralize()
|
||||
|
||||
def update_selected(self):
|
||||
@@ -373,20 +523,26 @@ class ManageWindow(QWidget):
|
||||
self.thread_update.apps_to_update = to_update
|
||||
self.thread_update.start()
|
||||
|
||||
def _finish_update_selected(self, success: bool):
|
||||
def _finish_update_selected(self, success: bool, updated: int):
|
||||
self.finish_action()
|
||||
self._release_lock()
|
||||
|
||||
if success:
|
||||
self.refresh()
|
||||
if self._can_notify_user():
|
||||
system.notify_user('{} {}'.format(updated, self.locale_keys['notification.update_selected.success']))
|
||||
|
||||
self.refresh_apps()
|
||||
else:
|
||||
if self._can_notify_user():
|
||||
system.notify_user(self.locale_keys['notification.update_selected.failed'])
|
||||
|
||||
self.bt_upgrade.setEnabled(True)
|
||||
self.checkbox_console.setChecked(True)
|
||||
|
||||
def _update_action_output(self, output: str):
|
||||
self.textarea_output.appendPlainText(output)
|
||||
|
||||
def _begin_action(self, action_label: str, keep_search: bool = False):
|
||||
def _begin_action(self, action_label: str, keep_search: bool = False, clear_filters: bool = False):
|
||||
self.ref_label_updates.setVisible(False)
|
||||
self.thread_animate_progress.stop = False
|
||||
self.thread_animate_progress.start()
|
||||
@@ -397,11 +553,17 @@ class ManageWindow(QWidget):
|
||||
self.bt_refresh.setEnabled(False)
|
||||
self.checkbox_only_apps.setEnabled(False)
|
||||
self.table_apps.setEnabled(False)
|
||||
self.checkbox_updates.setEnabled(False)
|
||||
|
||||
if keep_search:
|
||||
self.toolbar_search.setEnabled(False)
|
||||
self.ref_toolbar_search.setVisible(True)
|
||||
else:
|
||||
self.toolbar_search.setVisible(False)
|
||||
self.ref_toolbar_search.setVisible(False)
|
||||
|
||||
if clear_filters:
|
||||
self._update_type_filters(set())
|
||||
else:
|
||||
self.extra_filters.setEnabled(False)
|
||||
|
||||
def finish_action(self):
|
||||
self.ref_progress_bar.setVisible(False)
|
||||
@@ -413,15 +575,18 @@ class ManageWindow(QWidget):
|
||||
self.table_apps.setEnabled(True)
|
||||
self.input_search.setEnabled(True)
|
||||
self.label_status.setText('')
|
||||
self.toolbar_search.setVisible(True)
|
||||
self.toolbar_search.setEnabled(True)
|
||||
self.ref_toolbar_search.setVisible(True)
|
||||
self.ref_toolbar_search.setEnabled(True)
|
||||
self.extra_filters.setEnabled(True)
|
||||
self.checkbox_updates.setEnabled(True)
|
||||
|
||||
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)
|
||||
|
||||
if not is_root():
|
||||
if not is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.locale_keys)
|
||||
|
||||
if not ok:
|
||||
@@ -456,7 +621,7 @@ class ManageWindow(QWidget):
|
||||
self._release_lock()
|
||||
self.finish_action()
|
||||
self.change_update_state()
|
||||
dialog_info = InfoDialog(app_info, self.table_apps.get_selected_app_icon(), self.locale_keys)
|
||||
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_()
|
||||
|
||||
def _finish_get_history(self, app: dict):
|
||||
@@ -478,7 +643,10 @@ class ManageWindow(QWidget):
|
||||
|
||||
if word and self._acquire_lock():
|
||||
self._handle_console_option(False)
|
||||
self._begin_action(self.locale_keys['manage_window.status.searching'], keep_search=True)
|
||||
self.ref_checkbox_only_apps.setVisible(False)
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
self.filter_updates = False
|
||||
self._begin_action('{} "{}"'.format(self.locale_keys['manage_window.status.searching'], word), clear_filters=True)
|
||||
self.thread_search.word = word
|
||||
self.thread_search.start()
|
||||
|
||||
@@ -489,10 +657,22 @@ class ManageWindow(QWidget):
|
||||
|
||||
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)
|
||||
|
||||
if not is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.locale_keys)
|
||||
|
||||
if not ok:
|
||||
self._release_lock()
|
||||
return
|
||||
|
||||
self._handle_console_option(True)
|
||||
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.installing'], app.model.base_data.name))
|
||||
|
||||
self.thread_install.app = app
|
||||
self.thread_install.root_password = pwd
|
||||
self.thread_install.start()
|
||||
|
||||
def _finish_install(self, success: bool):
|
||||
@@ -501,8 +681,16 @@ class ManageWindow(QWidget):
|
||||
self._release_lock()
|
||||
|
||||
if success:
|
||||
self.refresh()
|
||||
if self._can_notify_user():
|
||||
app = self.table_apps.get_selected_app()
|
||||
system.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['installed']))
|
||||
|
||||
self.refresh_apps()
|
||||
else:
|
||||
if self._can_notify_user():
|
||||
app = self.table_apps.get_selected_app()
|
||||
system.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.install.failed']))
|
||||
|
||||
self.checkbox_console.setChecked(True)
|
||||
|
||||
def _update_progress(self, value: int):
|
||||
|
||||
Reference in New Issue
Block a user