Files
bearhub/fpakman/view/qt/apps_table.py
Vinícius Moreira 2d837804e3 0.3.0
### Features
- Applications search
- Now when you right-click a selected application you can:
    - retrieve its information
    - retrieve its commit history
    - downgrade
    - install and uninstall it
- "About" window available when right-clicking the tray icon.

### Improvements
- Performance and memory usage
- Adding tooltips to toolbar buttons
- "Update ?" column renamed to "Upgrade ?"
- Management panel title renamed
- Showing runtime apps when no app is available
- Allowing to specify a custom app translation with the environment variable **FPAKMAN_LOCALE**
- Adding expiration time for cached app data. Default to 1 hour. The environment variable **FPAKMAN_CACHE_EXPIRATION** can change this value.
- Now the application accepts arguments related to environment variables as well. Check 'README.md'.
- Minor GUI improvements
- Notifying only new updates
- New icon
- Progress bar
2019-07-02 10:53:04 -03:00

226 lines
10 KiB
Python

from typing import List
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QPixmap, QIcon, QColor, QCursor
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QHeaderView, QLabel
from fpakman.core import resource, util
from fpakman.view.qt import dialog
class UpdateToggleButton(QToolButton):
def __init__(self, model: dict, root: QWidget, locale_keys: dict, checked: bool = True):
super(UpdateToggleButton, self).__init__()
self.app = model
self.root = root
self.setCheckable(True)
self.clicked.connect(self.change_state)
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.setToolTip(locale_keys['manage_window.apps_table.upgrade_toggle.tooltip'])
if not checked:
self.click()
def change_state(self, not_checked: bool):
self.app['update_checked'] = not not_checked
self.setIcon(self.icon_on if not not_checked else self.icon_off)
self.root.change_update_state()
class AppsTable(QTableWidget):
def __init__(self, parent: QWidget):
super(AppsTable, self).__init__()
self.setParent(parent)
self.window = parent
self.column_names = [parent.locale_keys[key].capitalize() for key in ['flatpak.info.name',
'flatpak.info.version',
'manage_window.columns.latest_version',
'flatpak.info.branch',
'flatpak.info.arch',
'flatpak.info.id',
'flatpak.info.origin',
'manage_window.columns.installed',
'manage_window.columns.update']]
self.setColumnCount(len(self.column_names))
self.setFocusPolicy(Qt.NoFocus)
self.setShowGrid(False)
self.verticalHeader().setVisible(False)
self.setSelectionBehavior(QTableView.SelectRows)
self.setHorizontalHeaderLabels(self.column_names)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.icon_flathub = QIcon(resource.get_path('img/flathub.svg'))
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon)
self.icon_cache = {}
def contextMenuEvent(self, QContextMenuEvent): # selected row right click event
app = self.get_selected_app()
menu_row = QMenu()
if app['model']['installed']:
action_info = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.info"])
action_info.setIcon(QIcon(resource.get_path('img/info.svg')))
action_info.triggered.connect(self._get_app_info)
menu_row.addAction(action_info)
action_history = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.history"])
action_history.setIcon(QIcon(resource.get_path('img/history.svg')))
action_history.triggered.connect(self._get_app_history)
menu_row.addAction(action_history)
action_uninstall = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.uninstall"])
action_uninstall.setIcon(QIcon(resource.get_path('img/uninstall.svg')))
action_uninstall.triggered.connect(self._uninstall_app)
menu_row.addAction(action_uninstall)
if not app['model']['runtime']: # not available for runtimes
action_downgrade = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.downgrade"])
action_downgrade.triggered.connect(self._downgrade_app)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
menu_row.addAction(action_downgrade)
else:
action_install = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.install"])
action_install.setIcon(QIcon(resource.get_path('img/install.svg')))
action_install.triggered.connect(self._install_app)
menu_row.addAction(action_install)
menu_row.adjustSize()
menu_row.popup(QCursor.pos())
menu_row.exec_()
def get_selected_app(self):
return self.window.apps[self.currentRow()]
def get_selected_app_icon(self):
return self.item(self.currentRow(), 0).icon()
def _uninstall_app(self):
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app['model']['name']),
locale_keys=self.window.locale_keys):
self.window.uninstall_app(selected_app['model']['ref'])
def _downgrade_app(self):
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app['model']['name']),
locale_keys=self.window.locale_keys):
self.window.downgrade_app(selected_app)
def _get_app_info(self):
self.window.get_app_info(self.get_selected_app())
def _get_app_history(self):
self.window.get_app_history(self.get_selected_app())
def _install_app(self):
self.window.install_app(self.get_selected_app())
def _load_icon(self, http_response):
icon_url = http_response.url().toString()
pixmap = QPixmap()
pixmap.loadFromData(http_response.readAll())
icon = QIcon(pixmap)
self.icon_cache[icon_url] = icon
for idx, app in enumerate(self.window.apps):
if app['model']['icon'] == icon_url:
self.item(idx, 0).setIcon(icon)
self.window.resize_and_center()
break
def update_apps(self, apps: List[dict]):
self.setEnabled(True)
self.setRowCount(len(apps) if apps else 0)
if apps:
for idx, app in enumerate(apps):
tooltip = util.strip_html(app['model']['description']) if app['model']['description'] else None
col_name = QTableWidgetItem()
col_name.setText(app['model']['name'])
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_name.setToolTip(tooltip)
if not app['model']['icon']:
col_name.setIcon(self.icon_flathub)
else:
cached_icon = self.icon_cache.get(app['model']['icon'])
if cached_icon:
col_name.setIcon(cached_icon)
else:
col_name.setIcon(self.icon_flathub)
self.network_man.get(QNetworkRequest(QUrl(app['model']['icon'])))
self.setItem(idx, 0, col_name)
col_version = QTableWidgetItem()
col_version.setText(app['model']['version'])
col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_version.setToolTip(tooltip)
self.setItem(idx, 1, col_version)
col_release = QTableWidgetItem()
col_release.setText(app['model']['latest_version'])
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_release.setToolTip(tooltip)
self.setItem(idx, 2, col_release)
if app['model']['version'] and app['model']['latest_version'] and app['model']['version'] < app['model']['latest_version']:
col_release.setForeground(QColor('orange'))
col_branch = QTableWidgetItem()
col_branch.setText(app['model']['branch'])
col_branch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_branch.setToolTip(tooltip)
self.setItem(idx, 3, col_branch)
col_arch = QTableWidgetItem()
col_arch.setText(app['model']['arch'])
col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_arch.setToolTip(tooltip)
self.setItem(idx, 4, col_arch)
col_id = QTableWidgetItem()
col_id.setText(app['model']['id'])
col_id.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_id.setToolTip(tooltip)
self.setItem(idx, 5, col_id)
col_origin = QTableWidgetItem()
col_origin.setText(app['model']['origin'])
col_origin.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_origin.setToolTip(tooltip)
self.setItem(idx, 6, col_origin)
col_installed = QLabel()
col_installed.setPixmap((QPixmap(resource.get_path('img/{}.svg'.format('checked' if app['model']['installed'] else 'red_cross')))))
col_installed.setToolTip(tooltip)
col_installed.setAlignment(Qt.AlignCenter)
self.setCellWidget(idx, 7, col_installed)
col_update = UpdateToggleButton(app, self.window, self.window.locale_keys, app['model']['update']) if app['model']['update'] else None
self.setCellWidget(idx, 8, col_update)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
header_horizontal = self.horizontalHeader()
for i in range(self.columnCount()):
header_horizontal.setSectionResizeMode(i, policy)