Files
bearhub/view/qt/window.py
Vinicius Moreira fe46fad5c2 Init
2019-06-12 16:52:53 -03:00

317 lines
11 KiB
Python
Executable File

import operator
from functools import reduce
from PyQt5 import QtCore
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from core import resource, __version__
from typing import List
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QUrl
from PyQt5.QtGui import QIcon, QColor, QPixmap
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QTableWidget, \
QTableWidgetItem, QTableView, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QMessageBox
from core.controller import FlatpakController
class UpdateToggleButton(QToolButton):
def __init__(self, model: dict, root: QWidget, checked: bool = True):
super(UpdateToggleButton, self).__init__()
self.model = 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;')
if not checked:
self.click()
def change_state(self, not_checked: bool):
self.model['update_checked'] = not not_checked
self.setIcon(self.icon_on if not not_checked else self.icon_off)
self.root.change_update_button_state()
class MainWindow(QWidget):
__COLUMNS__ = ['Package', 'Branch', 'Arch', 'Ref', 'Latest Release', 'Update ?']
__BASE_HEIGHT__ = 400
def __init__(self, controller: FlatpakController):
super(MainWindow, self).__init__()
self.controller = controller
self.icon_cache = {}
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon)
self.icon_flathub = QIcon(resource.get_path('img/flathub_logo.svg'))
self._check_flatpak_installed()
self.resize(MainWindow.__BASE_HEIGHT__, MainWindow.__BASE_HEIGHT__)
self.setWindowTitle('flatman ({})'.format(__version__))
self.setWindowIcon(self.icon_flathub)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.checkbox_only_apps = QCheckBox()
self.checkbox_only_apps.setText('Only apps')
self.checkbox_only_apps.setChecked(True)
self.checkbox_only_apps.stateChanged.connect(self.filter_only_apps)
toolbar = QToolBar()
toolbar.addWidget(self.checkbox_only_apps)
spacer_1 = QWidget()
spacer_1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
toolbar.addWidget(spacer_1)
self.label_status = QLabel()
self.label_status.setText('')
toolbar.addWidget(self.label_status)
spacer_2 = QWidget()
spacer_2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
toolbar.addWidget(spacer_2)
self.bt_refresh = QToolButton()
self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh_orange.svg')))
self.bt_refresh.clicked.connect(lambda: self.refresh(threaded=True))
toolbar.addWidget(self.bt_refresh)
self.bt_update = QToolButton()
self.bt_update.setIcon(QIcon(resource.get_path('img/update_green.svg')))
self.bt_update.setEnabled(False)
self.bt_update.clicked.connect(self.update_selected)
toolbar.addWidget(self.bt_update)
self.layout.addWidget(toolbar)
self.table_apps = QTableWidget()
self.table_apps.setColumnCount(len(MainWindow.__COLUMNS__))
self.table_apps.setFocusPolicy(Qt.NoFocus)
self.table_apps.setShowGrid(False)
self.table_apps.verticalHeader().setVisible(False)
self.table_apps.setSelectionBehavior(QTableView.SelectRows)
self.table_apps.setHorizontalHeaderLabels(MainWindow.__COLUMNS__)
self.table_apps.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
header_horizontal = self.table_apps.horizontalHeader()
for i in range(0, len(MainWindow.__COLUMNS__)):
header_horizontal.setSectionResizeMode(i, QHeaderView.ResizeToContents)
self.layout.addWidget(self.table_apps)
self.apps = []
self.centralize()
self.update_thread = UpdateSelectedPackages(self.controller)
self.update_thread.signal.connect(self._finish_update_selected)
self.refresh_thread = RefreshPackages(self.controller)
self.refresh_thread.signal.connect(self._finish_refresh)
self.layout.addWidget(QLabel('flatpak: ' + self.controller.get_version()), alignment=Qt.AlignRight)
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.apps):
if app['model']['icon'] == icon_url:
self.table_apps.item(idx, 0).setIcon(icon)
self.resize_and_center()
break
def _check_flatpak_installed(self):
if not self.controller.check_installed():
error_msg = QMessageBox()
error_msg.setIcon(QMessageBox.Critical)
error_msg.setText('flatpak seems not to be installed. Exiting...')
error_msg.setWindowTitle('Error')
error_msg.setWindowIcon(self.icon_flathub)
error_msg.exec_()
exit(1)
def refresh(self, threaded: bool = False):
self._check_flatpak_installed()
self._begin_action('Refreshing...')
if threaded:
self.refresh_thread.start()
else:
apps = self.controller.refresh()
self.update_packages(apps)
self.finish_action()
def _finish_refresh(self):
self.update_packages(self.refresh_thread.apps)
self.finish_action()
def filter_only_apps(self, only_apps: int):
if self.apps:
show_only_apps = True if only_apps == 2 else False
for idx, app in enumerate(self.apps):
hidden = show_only_apps and app['model']['runtime']
self.table_apps.setRowHidden(idx, hidden)
app['visible'] = not hidden
def change_update_button_state(self):
enable_bt_update = False
for app in self.apps:
if app['visible'] and app['update_checked']:
enable_bt_update = True
break
self.bt_update.setEnabled(enable_bt_update)
def centralize(self):
geo = self.frameGeometry()
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
center_point = QApplication.desktop().screenGeometry(screen).center()
geo.moveCenter(center_point)
self.move(geo.topLeft())
def update_packages(self, apps: List[dict]):
self._check_flatpak_installed()
self.table_apps.setEnabled(True)
self.apps = []
self.table_apps.setRowCount(len(apps) if apps else 0)
if apps:
for idx, app in enumerate(apps):
col_name = QTableWidgetItem()
col_name.setText(app['name'])
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if not app['icon']:
col_name.setIcon(self.icon_flathub)
else:
cached_icon = self.icon_cache.get(app['icon'])
if cached_icon:
col_name.setIcon(cached_icon)
else:
col_name.setIcon(self.icon_flathub)
self.network_man.get(QNetworkRequest(QUrl(app['icon'])))
self.table_apps.setItem(idx, 0, col_name)
col_version = QTableWidgetItem()
col_version.setText(app['branch'])
col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.table_apps.setItem(idx, 1, col_version)
col_arch = QTableWidgetItem()
col_arch.setText(app['arch'])
col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.table_apps.setItem(idx, 2, col_arch)
col_package = QTableWidgetItem()
col_package.setText(app['ref'])
col_package.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.table_apps.setItem(idx, 3, col_package)
col_release = QTableWidgetItem()
col_release.setText(app['latest_release'])
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.table_apps.setItem(idx, 4, col_release)
if app['update']:
col_release.setForeground(QColor('yellow'))
app_model = {'model': app, 'update_checked': app['update'], 'visible': True}
if app['update']:
col_update = UpdateToggleButton(app_model, self, app['update'])
self.table_apps.setCellWidget(idx, 5, col_update)
else:
self.table_apps.setCellWidget(idx, 5, None)
self.apps.append(app_model)
self.change_update_button_state()
self.filter_only_apps(2 if self.checkbox_only_apps.isChecked() else 0)
self.resize_and_center()
def resize_and_center(self):
new_width = reduce(operator.add, [self.table_apps.columnWidth(i) for i in range(len(MainWindow.__COLUMNS__))]) * 1.03
self.resize(new_width, self.height())
self.centralize()
def update_selected(self):
if self.apps:
to_update = [pak['model']['ref'] for pak in self.apps if pak['visible'] and pak['update_checked']]
if to_update:
self._begin_action('Updating...')
self.update_thread.refs_to_update = to_update
self.update_thread.start()
def _finish_update_selected(self):
self.update_packages(self.update_thread.updated_apps)
self.finish_action()
def _begin_action(self, action_label: str):
self.label_status.setText(action_label)
self.bt_update.setEnabled(False)
self.bt_refresh.setEnabled(False)
self.checkbox_only_apps.setEnabled(False)
self.table_apps.setEnabled(False)
def finish_action(self):
self.bt_refresh.setEnabled(True)
self.checkbox_only_apps.setEnabled(True)
self.table_apps.setEnabled(True)
self.label_status.setText('')
# Threaded actions
class UpdateSelectedPackages(QThread):
signal = pyqtSignal()
def __init__(self, controller: FlatpakController):
super(UpdateSelectedPackages, self).__init__()
self.controller = controller
self.refs_to_update = []
self.updated_apps = None
def run(self):
self.updated_apps = self.controller.update(self.refs_to_update)
self.signal.emit()
class RefreshPackages(QThread):
signal = pyqtSignal()
def __init__(self, controller: FlatpakController):
super(RefreshPackages, self).__init__()
self.controller = controller
self.apps = None
def run(self):
self.apps = self.controller.refresh()
self.signal.emit()