mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-10 12:14:16 +02:00
[ui] feature: themes
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
from glob import glob
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel, QWidget, QHBoxLayout
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel, QWidget, QHBoxLayout, QSizePolicy, QApplication
|
||||
|
||||
from bauh import __version__, __app_name__, ROOT_DIR
|
||||
from bauh.context import generate_i18n
|
||||
@@ -21,28 +20,29 @@ class AboutDialog(QDialog):
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
logo_container = QWidget()
|
||||
logo_container.setObjectName('logo_container')
|
||||
logo_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
logo_container.setLayout(QHBoxLayout())
|
||||
|
||||
label_logo = QLabel()
|
||||
icon = QIcon(resource.get_path('img/logo.svg')).pixmap(64, 64)
|
||||
label_logo.setPixmap(icon)
|
||||
label_logo.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(label_logo)
|
||||
label_logo.setObjectName('logo')
|
||||
|
||||
logo_container.layout().addWidget(label_logo)
|
||||
layout.addWidget(logo_container)
|
||||
|
||||
label_name = QLabel(__app_name__)
|
||||
label_name.setStyleSheet('font-weight: bold; font-size: 14px')
|
||||
label_name.setAlignment(Qt.AlignCenter)
|
||||
label_name.setObjectName('app_name')
|
||||
layout.addWidget(label_name)
|
||||
|
||||
label_version = QLabel(i18n['about.version'].lower() + ' ' + __version__)
|
||||
label_version.setStyleSheet('QLabel { font-size: 11px; font-weight: bold }')
|
||||
label_version.setAlignment(Qt.AlignCenter)
|
||||
label_version.setObjectName('app_version')
|
||||
layout.addWidget(label_version)
|
||||
|
||||
layout.addWidget(QLabel(''))
|
||||
|
||||
line_desc = QLabel(i18n['about.info.desc'])
|
||||
line_desc.setStyleSheet('font-size: 12px; font-weight: bold;')
|
||||
line_desc.setAlignment(Qt.AlignCenter)
|
||||
line_desc.setMinimumWidth(400)
|
||||
line_desc.setObjectName('app_description')
|
||||
layout.addWidget(line_desc)
|
||||
|
||||
layout.addWidget(QLabel(''))
|
||||
@@ -54,54 +54,52 @@ class AboutDialog(QDialog):
|
||||
gems_widget.setLayout(QHBoxLayout())
|
||||
|
||||
gems_widget.layout().addWidget(QLabel())
|
||||
gem_logo_size = int(0.032552083 * QApplication.primaryScreen().size().height())
|
||||
|
||||
for gem_path in available_gems:
|
||||
icon = QLabel()
|
||||
icon.setObjectName('gem_logo')
|
||||
icon_path = gem_path + '/resources/img/{}.svg'.format(gem_path.split('/')[-1])
|
||||
icon.setPixmap(QIcon(icon_path).pixmap(QSize(25, 25)))
|
||||
icon.setPixmap(QIcon(icon_path).pixmap(gem_logo_size, gem_logo_size))
|
||||
gems_widget.layout().addWidget(icon)
|
||||
|
||||
gems_widget.layout().addWidget(QLabel())
|
||||
|
||||
layout.addWidget(gems_widget)
|
||||
layout.addWidget(QLabel(''))
|
||||
|
||||
label_more_info = QLabel()
|
||||
label_more_info.setStyleSheet('font-size: 11px;')
|
||||
label_more_info.setObjectName('app_more_information')
|
||||
label_more_info.setText(i18n['about.info.link'] + " <a href='{url}'>{url}</a>".format(url=PROJECT_URL))
|
||||
label_more_info.setOpenExternalLinks(True)
|
||||
label_more_info.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(label_more_info)
|
||||
|
||||
label_license = QLabel()
|
||||
label_license.setStyleSheet('font-size: 11px;')
|
||||
label_license.setObjectName('app_license')
|
||||
label_license.setText("<a href='{}'>{}</a>".format(LICENSE_URL, i18n['about.info.license']))
|
||||
label_license.setOpenExternalLinks(True)
|
||||
label_license.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(label_license)
|
||||
|
||||
layout.addWidget(QLabel(''))
|
||||
|
||||
label_trouble_question = QLabel(i18n['about.info.trouble.question'])
|
||||
label_trouble_question.setStyleSheet('font-size: 10px; font-weight: bold')
|
||||
label_trouble_question.setAlignment(Qt.AlignCenter)
|
||||
label_trouble_question.setObjectName('app_trouble_question')
|
||||
|
||||
layout.addWidget(label_trouble_question)
|
||||
|
||||
label_trouble_answer = QLabel(i18n['about.info.trouble.answer'])
|
||||
label_trouble_answer.setStyleSheet('font-size: 10px;')
|
||||
label_trouble_answer.setAlignment(Qt.AlignCenter)
|
||||
label_trouble_answer.setObjectName('app_trouble_answer')
|
||||
|
||||
layout.addWidget(label_trouble_answer)
|
||||
|
||||
layout.addWidget(QLabel(''))
|
||||
|
||||
label_rate_question = QLabel(i18n['about.info.rate.question'])
|
||||
label_rate_question.setStyleSheet('font-size: 10px; font-weight: bold;')
|
||||
label_rate_question.setAlignment(Qt.AlignCenter)
|
||||
label_rate_question.setObjectName('app_rate_question')
|
||||
layout.addWidget(label_rate_question)
|
||||
|
||||
label_rate_answer = QLabel(i18n['about.info.rate.answer'])
|
||||
label_rate_answer.setStyleSheet('font-size: 10px;')
|
||||
label_rate_answer.setAlignment(Qt.AlignCenter)
|
||||
label_rate_answer.setObjectName('app_rate_answer')
|
||||
layout.addWidget(label_rate_answer)
|
||||
|
||||
layout.addWidget(QLabel(''))
|
||||
|
||||
@@ -2,22 +2,21 @@ import operator
|
||||
import os
|
||||
from functools import reduce
|
||||
from threading import Lock
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl, QSize
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QCursor
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
|
||||
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QToolButton, QWidget, \
|
||||
QHeaderView, QLabel, QHBoxLayout, QToolBar, QSizePolicy
|
||||
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
from bauh.api.abstract.model import PackageStatus, CustomSoftwareAction
|
||||
from bauh.commons.html import strip_html, bold
|
||||
from bauh.view.qt import dialog
|
||||
from bauh.view.qt.colors import GREEN, BROWN
|
||||
from bauh.view.qt.components import IconButton
|
||||
from bauh.view.qt.components import IconButton, QCustomMenuAction, QCustomToolbar
|
||||
from bauh.view.qt.dialog import ConfirmationDialog
|
||||
from bauh.view.qt.qt_utils import measure_based_on_height
|
||||
from bauh.view.qt.view_model import PackageView
|
||||
from bauh.view.util import resource
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
NAME_MAX_SIZE = 30
|
||||
@@ -25,45 +24,32 @@ DESC_MAX_SIZE = 40
|
||||
PUBLISHER_MAX_SIZE = 25
|
||||
|
||||
|
||||
class UpdateToggleButton(QWidget):
|
||||
class UpgradeToggleButton(QToolButton):
|
||||
|
||||
STYLE_DEFAULT = 'QToolButton { background: ' + GREEN + ' } QToolButton:checked { background: gray } '
|
||||
STYLE_UNCHECKED = 'QToolButton:disabled { background: #d69003 }'
|
||||
|
||||
def __init__(self, pkg: PackageView, root: QWidget, i18n: I18n, checked: bool = True, clickable: bool = True):
|
||||
super(UpdateToggleButton, self).__init__()
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
def __init__(self, pkg: Optional[PackageView], root: QWidget, i18n: I18n, checked: bool = True,
|
||||
clickable: bool = True):
|
||||
super(UpgradeToggleButton, self).__init__()
|
||||
self.app_view = pkg
|
||||
self.root = root
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setAlignment(Qt.AlignCenter)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.bt = QToolButton()
|
||||
self.bt.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt.setCheckable(True)
|
||||
self.bt.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.setCheckable(True)
|
||||
|
||||
if clickable:
|
||||
self.bt.clicked.connect(self.change_state)
|
||||
self.clicked.connect(self.change_state)
|
||||
|
||||
self.bt.setStyleSheet(self.STYLE_DEFAULT + (self.STYLE_UNCHECKED if not clickable and not checked else ''))
|
||||
|
||||
layout.addWidget(self.bt)
|
||||
if not clickable and not checked:
|
||||
self.setProperty('enabled', 'false')
|
||||
|
||||
if not checked:
|
||||
self.bt.click()
|
||||
self.click()
|
||||
|
||||
if clickable:
|
||||
self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg')))
|
||||
self.setToolTip('{} {}'.format(i18n['manage_window.apps_table.upgrade_toggle.tooltip'],
|
||||
i18n['manage_window.apps_table.upgrade_toggle.enabled.tooltip']))
|
||||
else:
|
||||
if not checked:
|
||||
self.bt.setIcon(QIcon(resource.get_path('img/exclamation.svg')))
|
||||
self.bt.setEnabled(False)
|
||||
self.setEnabled(False)
|
||||
|
||||
tooltip = i18n['{}.update.disabled.tooltip'.format(pkg.model.gem_name)]
|
||||
|
||||
@@ -73,22 +59,22 @@ class UpdateToggleButton(QWidget):
|
||||
self.setToolTip('{} {}'.format(i18n['manage_window.apps_table.upgrade_toggle.tooltip'],
|
||||
i18n['manage_window.apps_table.upgrade_toggle.disabled.tooltip']))
|
||||
else:
|
||||
self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg')))
|
||||
self.bt.setCheckable(False)
|
||||
self.setCheckable(False)
|
||||
|
||||
def change_state(self, not_checked: bool):
|
||||
self.app_view.update_checked = not not_checked
|
||||
self.setProperty('toggled', str(self.app_view.update_checked).lower())
|
||||
self.root.update_bt_upgrade()
|
||||
self.style().unpolish(self)
|
||||
self.style().polish(self)
|
||||
|
||||
|
||||
class AppsTable(QTableWidget):
|
||||
|
||||
COL_NUMBER = 8
|
||||
STYLE_BT_INSTALL = 'background: {b}; color: white; font-size: 10px; font-weight: bold'.format(b=GREEN)
|
||||
STYLE_BT_UNINSTALL = 'color: {c}; font-size: 10px; font-weight: bold;'.format(c=BROWN)
|
||||
class PackagesTable(QTableWidget):
|
||||
COL_NUMBER = 9
|
||||
|
||||
def __init__(self, parent: QWidget, icon_cache: MemoryCache, download_icons: bool):
|
||||
super(AppsTable, self).__init__()
|
||||
super(PackagesTable, self).__init__()
|
||||
self.setObjectName('table_packages')
|
||||
self.setParent(parent)
|
||||
self.window = parent
|
||||
self.download_icons = download_icons
|
||||
@@ -102,8 +88,8 @@ class AppsTable(QTableWidget):
|
||||
self.setHorizontalHeaderLabels(['' for _ in range(self.columnCount())])
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.icon_logo = QIcon(resource.get_path('img/logo.svg'))
|
||||
self.pixmap_verified = QIcon(resource.get_path('img/verified.svg')).pixmap(QSize(10, 10))
|
||||
self.horizontalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.verticalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
self.network_man = QNetworkAccessManager()
|
||||
self.network_man.finished.connect(self._load_icon_and_cache)
|
||||
@@ -114,6 +100,10 @@ class AppsTable(QTableWidget):
|
||||
self.cache_type_icon = {}
|
||||
self.i18n = self.window.i18n
|
||||
|
||||
def icon_size(self) -> QSize:
|
||||
pixels = measure_based_on_height(0.02083)
|
||||
return QSize(pixels, pixels)
|
||||
|
||||
def has_any_settings(self, pkg: PackageView):
|
||||
return pkg.model.has_history() or \
|
||||
pkg.model.can_be_downgraded() or \
|
||||
@@ -122,77 +112,77 @@ class AppsTable(QTableWidget):
|
||||
|
||||
def show_pkg_actions(self, pkg: PackageView):
|
||||
menu_row = QMenu()
|
||||
menu_row.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
menu_row.setObjectName('app_actions')
|
||||
menu_row.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
if pkg.model.installed:
|
||||
|
||||
if pkg.model.has_history():
|
||||
action_history = QAction(self.i18n["manage_window.apps_table.row.actions.history"])
|
||||
action_history.setIcon(QIcon(resource.get_path('img/history.svg')))
|
||||
|
||||
def show_history():
|
||||
self.window.begin_show_history(pkg)
|
||||
|
||||
action_history.triggered.connect(show_history)
|
||||
menu_row.addAction(action_history)
|
||||
menu_row.addAction(QCustomMenuAction(parent=menu_row,
|
||||
label=self.i18n["manage_window.apps_table.row.actions.history"],
|
||||
action=show_history,
|
||||
button_name='app_history'))
|
||||
|
||||
if pkg.model.can_be_downgraded():
|
||||
action_downgrade = QAction(self.i18n["manage_window.apps_table.row.actions.downgrade"])
|
||||
|
||||
def downgrade():
|
||||
if dialog.ask_confirmation(
|
||||
title=self.i18n['manage_window.apps_table.row.actions.downgrade'],
|
||||
body=self._parag(self.i18n['manage_window.apps_table.row.actions.downgrade.popup.body'].format(self._bold(str(pkg)))),
|
||||
i18n=self.i18n):
|
||||
if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.downgrade'],
|
||||
body=self._parag(self.i18n[
|
||||
'manage_window.apps_table.row.actions.downgrade.popup.body'].format(
|
||||
self._bold(str(pkg)))),
|
||||
i18n=self.i18n).ask():
|
||||
self.window.begin_downgrade(pkg)
|
||||
|
||||
action_downgrade.triggered.connect(downgrade)
|
||||
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
|
||||
menu_row.addAction(action_downgrade)
|
||||
menu_row.addAction(QCustomMenuAction(parent=menu_row,
|
||||
label=self.i18n["manage_window.apps_table.row.actions.downgrade"],
|
||||
action=downgrade,
|
||||
button_name='app_downgrade'))
|
||||
|
||||
if pkg.model.supports_ignored_updates():
|
||||
if pkg.model.is_update_ignored():
|
||||
action_ignore_updates = QAction(
|
||||
self.i18n["manage_window.apps_table.row.actions.ignore_updates_reverse"])
|
||||
action_ignore_updates.setIcon(QIcon(resource.get_path('img/revert_update_ignored.svg')))
|
||||
action_label = self.i18n["manage_window.apps_table.row.actions.ignore_updates_reverse"]
|
||||
button_name = 'revert_ignore_updates'
|
||||
else:
|
||||
action_ignore_updates = QAction(self.i18n["manage_window.apps_table.row.actions.ignore_updates"])
|
||||
action_ignore_updates.setIcon(QIcon(resource.get_path('img/ignore_update.svg')))
|
||||
action_label = self.i18n["manage_window.apps_table.row.actions.ignore_updates"]
|
||||
button_name = 'ignore_updates'
|
||||
|
||||
def ignore_updates():
|
||||
self.window.begin_ignore_updates(pkg)
|
||||
|
||||
action_ignore_updates.triggered.connect(ignore_updates)
|
||||
menu_row.addAction(action_ignore_updates)
|
||||
menu_row.addAction(QCustomMenuAction(parent=menu_row,
|
||||
label=action_label,
|
||||
button_name=button_name,
|
||||
action=ignore_updates))
|
||||
|
||||
if bool(pkg.model.get_custom_supported_actions()):
|
||||
actions = [self._map_custom_action(pkg, a) for a in pkg.model.get_custom_supported_actions()]
|
||||
actions = [self._map_custom_action(pkg, a, menu_row) for a in pkg.model.get_custom_supported_actions()]
|
||||
menu_row.addActions(actions)
|
||||
|
||||
menu_row.adjustSize()
|
||||
menu_row.popup(QCursor.pos())
|
||||
menu_row.exec_()
|
||||
|
||||
def _map_custom_action(self, pkg: PackageView, action: CustomSoftwareAction) -> QAction:
|
||||
item = QAction(self.i18n[action.i18n_label_key])
|
||||
|
||||
if action.icon_path:
|
||||
item.setIcon(QIcon(action.icon_path))
|
||||
|
||||
def _map_custom_action(self, pkg: PackageView, action: CustomSoftwareAction, parent: QWidget) -> QCustomMenuAction:
|
||||
def custom_action():
|
||||
if action.i18n_confirm_key:
|
||||
body = self.i18n[action.i18n_confirm_key].format(bold(pkg.model.name))
|
||||
else:
|
||||
body = '{} ?'.format(self.i18n[action.i18n_label_key])
|
||||
|
||||
if dialog.ask_confirmation(
|
||||
title=self.i18n[action.i18n_label_key],
|
||||
body=self._parag(body),
|
||||
i18n=self.i18n):
|
||||
if ConfirmationDialog(icon=QIcon(pkg.model.get_type_icon_path()),
|
||||
title=self.i18n[action.i18n_label_key],
|
||||
body=self._parag(body),
|
||||
i18n=self.i18n).ask():
|
||||
self.window.begin_execute_custom_action(pkg, action)
|
||||
|
||||
item.triggered.connect(custom_action)
|
||||
return item
|
||||
return QCustomMenuAction(parent=parent,
|
||||
label=self.i18n[action.i18n_label_key],
|
||||
icon=QIcon(action.icon_path) if action.icon_path else None,
|
||||
action=custom_action)
|
||||
|
||||
def refresh(self, pkg: PackageView):
|
||||
self._update_row(pkg, update_check_enabled=False, change_update_col=False)
|
||||
@@ -206,9 +196,11 @@ class AppsTable(QTableWidget):
|
||||
self._update_row(pkg, change_update_col=change_update_col)
|
||||
|
||||
def _uninstall(self, pkg: PackageView):
|
||||
if dialog.ask_confirmation(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
||||
body=self._parag(self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(self._bold(str(pkg)))),
|
||||
i18n=self.i18n):
|
||||
if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
||||
body=self._parag(
|
||||
self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(
|
||||
self._bold(str(pkg)))),
|
||||
i18n=self.i18n).ask():
|
||||
self.window.begin_uninstall(pkg)
|
||||
|
||||
def _bold(self, text: str) -> str:
|
||||
@@ -224,13 +216,12 @@ class AppsTable(QTableWidget):
|
||||
warning = self.i18n.get('gem.{}.install.warning'.format(pkgv.model.get_type().lower()))
|
||||
|
||||
if warning:
|
||||
body += '<br/><br/> {}'.format('<br/>'.join(('{}.'.format(phrase) for phrase in warning.split('.') if phrase)))
|
||||
|
||||
if dialog.ask_confirmation(
|
||||
title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
|
||||
body=self._parag(body),
|
||||
i18n=self.i18n):
|
||||
body += '<br/><br/> {}'.format(
|
||||
'<br/>'.join(('{}.'.format(phrase) for phrase in warning.split('.') if phrase)))
|
||||
|
||||
if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
|
||||
body=self._parag(body),
|
||||
i18n=self.i18n).ask():
|
||||
self.window.install(pkgv)
|
||||
|
||||
def _load_icon_and_cache(self, http_response: QNetworkReply):
|
||||
@@ -257,18 +248,19 @@ class AppsTable(QTableWidget):
|
||||
if icon_data:
|
||||
for idx, app in enumerate(self.window.pkgs):
|
||||
if app.model.icon_url == icon_url:
|
||||
col_name = self.item(idx, 0)
|
||||
col_name.setIcon(icon_data['icon'])
|
||||
self._update_icon(self.cellWidget(idx, 0), icon_data['icon'])
|
||||
|
||||
if app.model.supports_disk_cache() and app.model.get_disk_icon_path() and icon_data['bytes']:
|
||||
if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()):
|
||||
self.window.manager.cache_to_disk(pkg=app.model, icon_bytes=icon_data['bytes'], only_icon=True)
|
||||
self.window.manager.cache_to_disk(pkg=app.model, icon_bytes=icon_data['bytes'],
|
||||
only_icon=True)
|
||||
|
||||
def update_packages(self, pkgs: List[PackageView], update_check_enabled: bool = True):
|
||||
self.setRowCount(0) # removes the overwrite effect when updates the table
|
||||
self.setEnabled(True)
|
||||
|
||||
if pkgs:
|
||||
self.setColumnCount(self.COL_NUMBER if update_check_enabled else self.COL_NUMBER - 1)
|
||||
self.setRowCount(len(pkgs))
|
||||
|
||||
for idx, pkg in enumerate(pkgs):
|
||||
@@ -284,59 +276,49 @@ class AppsTable(QTableWidget):
|
||||
self.scrollToTop()
|
||||
|
||||
def _update_row(self, pkg: PackageView, update_check_enabled: bool = True, change_update_col: bool = True):
|
||||
self._set_col_name(0, pkg)
|
||||
self._set_col_version(1, pkg)
|
||||
self._set_col_description(2, pkg)
|
||||
self._set_col_publisher(3, pkg)
|
||||
self._set_col_type(4, pkg)
|
||||
self._set_col_installed(5, pkg)
|
||||
self._set_col_actions(6, pkg)
|
||||
self._set_col_icon(0, pkg)
|
||||
self._set_col_name(1, pkg)
|
||||
self._set_col_version(2, pkg)
|
||||
self._set_col_description(3, pkg)
|
||||
self._set_col_publisher(4, pkg)
|
||||
self._set_col_type(5, pkg)
|
||||
self._set_col_installed(6, pkg)
|
||||
self._set_col_actions(7, pkg)
|
||||
|
||||
if change_update_col:
|
||||
col_update = None
|
||||
if change_update_col and update_check_enabled:
|
||||
if pkg.model.installed and not pkg.model.is_update_ignored() and pkg.model.update:
|
||||
col_update = QCustomToolbar()
|
||||
col_update.add_space()
|
||||
col_update.add_widget(UpgradeToggleButton(pkg=pkg,
|
||||
root=self.window,
|
||||
i18n=self.i18n,
|
||||
checked=pkg.update_checked if pkg.model.can_be_updated() else False,
|
||||
clickable=pkg.model.can_be_updated()))
|
||||
col_update.add_space()
|
||||
else:
|
||||
col_update = QLabel()
|
||||
|
||||
if update_check_enabled and pkg.model.installed and not pkg.model.is_update_ignored() and pkg.model.update:
|
||||
col_update = QToolBar()
|
||||
col_update.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
col_update.addWidget(UpdateToggleButton(pkg=pkg,
|
||||
root=self.window,
|
||||
i18n=self.i18n,
|
||||
checked=pkg.update_checked if pkg.model.can_be_updated() else False,
|
||||
clickable=pkg.model.can_be_updated()))
|
||||
|
||||
self.setCellWidget(pkg.table_index, 7, col_update)
|
||||
|
||||
def _gen_row_button(self, text: str, style: str, callback) -> QWidget:
|
||||
col = QWidget()
|
||||
col.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
self.setCellWidget(pkg.table_index, 8, col_update)
|
||||
|
||||
def _gen_row_button(self, text: str, name: str, callback) -> QToolButton:
|
||||
col_bt = QToolButton()
|
||||
col_bt.setProperty('text_only', 'true')
|
||||
col_bt.setObjectName(name)
|
||||
col_bt.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
col_bt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
col_bt.setText(text)
|
||||
col_bt.setStyleSheet('QToolButton { ' + style + '}')
|
||||
col_bt.setMinimumWidth(80)
|
||||
col_bt.clicked.connect(callback)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setAlignment(Qt.AlignCenter)
|
||||
|
||||
layout.addWidget(col_bt)
|
||||
|
||||
col.setLayout(layout)
|
||||
return col
|
||||
return col_bt
|
||||
|
||||
def _set_col_installed(self, col: int, pkg: PackageView):
|
||||
toolbar = QToolBar()
|
||||
toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
toolbar = QCustomToolbar()
|
||||
toolbar.add_space()
|
||||
|
||||
if pkg.model.installed:
|
||||
if pkg.model.can_be_uninstalled():
|
||||
def uninstall():
|
||||
self._uninstall(pkg)
|
||||
|
||||
item = self._gen_row_button(self.i18n['uninstall'].capitalize(), self.STYLE_BT_UNINSTALL, uninstall)
|
||||
item = self._gen_row_button(self.i18n['uninstall'].capitalize(), 'bt_uninstall', uninstall)
|
||||
else:
|
||||
item = None
|
||||
|
||||
@@ -344,34 +326,39 @@ class AppsTable(QTableWidget):
|
||||
def install():
|
||||
self._install_app(pkg)
|
||||
|
||||
item = self._gen_row_button(self.i18n['install'].capitalize(), self.STYLE_BT_INSTALL, install)
|
||||
item = self._gen_row_button(self.i18n['install'].capitalize(), 'bt_install', install)
|
||||
else:
|
||||
item = None
|
||||
|
||||
toolbar.addWidget(item)
|
||||
toolbar.add_widget(item)
|
||||
toolbar.add_space()
|
||||
self.setCellWidget(pkg.table_index, col, toolbar)
|
||||
|
||||
def _set_col_type(self, col: int, pkg: PackageView):
|
||||
icon_data = self.cache_type_icon.get(pkg.model.get_type())
|
||||
|
||||
if icon_data is None:
|
||||
pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(QSize(16, 16))
|
||||
pixmap = QIcon(pkg.model.get_type_icon_path()).pixmap(self.icon_size())
|
||||
icon_data = {'px': pixmap, 'tip': '{}: {}'.format(self.i18n['type'], pkg.get_type_label())}
|
||||
self.cache_type_icon[pkg.model.get_type()] = icon_data
|
||||
|
||||
item = QLabel()
|
||||
item.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
item.setPixmap(icon_data['px'])
|
||||
item.setAlignment(Qt.AlignCenter)
|
||||
|
||||
item.setToolTip(icon_data['tip'])
|
||||
self.setCellWidget(pkg.table_index, col, item)
|
||||
col_type_icon = QLabel()
|
||||
col_type_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
col_type_icon.setObjectName('app_type')
|
||||
col_type_icon.setProperty('icon', 'true')
|
||||
col_type_icon.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
col_type_icon.setPixmap(icon_data['px'])
|
||||
col_type_icon.setToolTip(icon_data['tip'])
|
||||
self.setCellWidget(pkg.table_index, col, col_type_icon)
|
||||
|
||||
def _set_col_version(self, col: int, pkg: PackageView):
|
||||
label_version = QLabel(str(pkg.model.version if pkg.model.version else '?'))
|
||||
label_version.setObjectName('app_version')
|
||||
label_version.setAlignment(Qt.AlignCenter)
|
||||
|
||||
item = QWidget()
|
||||
item.setProperty('container', 'true')
|
||||
item.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
item.setLayout(QHBoxLayout())
|
||||
item.layout().addWidget(label_version)
|
||||
|
||||
@@ -381,11 +368,11 @@ class AppsTable(QTableWidget):
|
||||
tooltip = self.i18n['version.unknown']
|
||||
|
||||
if pkg.model.update and not pkg.model.is_update_ignored():
|
||||
label_version.setStyleSheet("color: {}; font-weight: bold".format(GREEN))
|
||||
label_version.setProperty('update', 'true')
|
||||
tooltip = self.i18n['version.installed_outdated']
|
||||
|
||||
if pkg.model.is_update_ignored():
|
||||
label_version.setStyleSheet("color: {}; font-weight: bold".format(BROWN))
|
||||
label_version.setProperty('ignored', 'true')
|
||||
tooltip = self.i18n['version.updates_ignored']
|
||||
|
||||
if pkg.model.installed and pkg.model.update and not pkg.model.is_update_ignored() and pkg.model.version and pkg.model.latest_version and pkg.model.version != pkg.model.latest_version:
|
||||
@@ -395,25 +382,7 @@ class AppsTable(QTableWidget):
|
||||
item.setToolTip(tooltip)
|
||||
self.setCellWidget(pkg.table_index, col, item)
|
||||
|
||||
def _set_col_name(self, col: int, pkg: PackageView):
|
||||
item = QTableWidgetItem()
|
||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
||||
|
||||
name = pkg.model.get_display_name()
|
||||
if name:
|
||||
item.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), pkg.model.get_name_tooltip()))
|
||||
else:
|
||||
name = '...'
|
||||
item.setToolTip(self.i18n['app.name'].lower())
|
||||
|
||||
if len(name) > NAME_MAX_SIZE:
|
||||
name = name[0:NAME_MAX_SIZE - 3] + '...'
|
||||
|
||||
if len(name) < NAME_MAX_SIZE:
|
||||
name = name + ' ' * (NAME_MAX_SIZE-len(name))
|
||||
|
||||
item.setText(name)
|
||||
|
||||
def _set_col_icon(self, col: int, pkg: PackageView):
|
||||
icon_path = pkg.model.get_disk_icon_path()
|
||||
if pkg.model.installed and pkg.model.supports_disk_cache() and icon_path:
|
||||
if icon_path.startswith('/'):
|
||||
@@ -444,11 +413,42 @@ class AppsTable(QTableWidget):
|
||||
icon_data = self.icon_cache.get(pkg.model.icon_url)
|
||||
icon = icon_data['icon'] if icon_data else QIcon(pkg.model.get_default_icon_path())
|
||||
|
||||
item.setIcon(icon)
|
||||
self.setItem(pkg.table_index, col, item)
|
||||
col_icon = QLabel()
|
||||
col_icon.setObjectName('app_icon')
|
||||
col_icon.setProperty('icon', 'true')
|
||||
col_icon.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
self._update_icon(col_icon, icon)
|
||||
self.setCellWidget(pkg.table_index, col, col_icon)
|
||||
|
||||
def _set_col_name(self, col: int, pkg: PackageView):
|
||||
col_name = QLabel()
|
||||
col_name.setObjectName('app_name')
|
||||
col_name.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
col_name.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
|
||||
name = pkg.model.get_display_name()
|
||||
if name:
|
||||
col_name.setToolTip('{}: {}'.format(self.i18n['app.name'].lower(), pkg.model.get_name_tooltip()))
|
||||
else:
|
||||
name = '...'
|
||||
col_name.setToolTip(self.i18n['app.name'].lower())
|
||||
|
||||
if len(name) > NAME_MAX_SIZE:
|
||||
name = name[0:NAME_MAX_SIZE - 3] + '...'
|
||||
|
||||
if len(name) < NAME_MAX_SIZE:
|
||||
name = name + ' ' * (NAME_MAX_SIZE - len(name))
|
||||
|
||||
col_name.setText(name)
|
||||
self.setCellWidget(pkg.table_index, col, col_name)
|
||||
|
||||
def _update_icon(self, label: QLabel, icon: QIcon):
|
||||
label.setPixmap(icon.pixmap(QSize(self.icon_size())))
|
||||
|
||||
def _set_col_description(self, col: int, pkg: PackageView):
|
||||
item = QLabel()
|
||||
item.setObjectName('app_description')
|
||||
item.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
|
||||
if pkg.model.description is not None or not pkg.model.is_application() or pkg.model.status == PackageStatus.READY:
|
||||
desc = pkg.model.description.split('\n')[0] if pkg.model.description else pkg.model.description
|
||||
@@ -478,21 +478,27 @@ class AppsTable(QTableWidget):
|
||||
if len(publisher) > PUBLISHER_MAX_SIZE:
|
||||
publisher = full_publisher[0: PUBLISHER_MAX_SIZE - 3] + '...'
|
||||
|
||||
lb_name = QLabel()
|
||||
lb_name.setObjectName('app_publisher')
|
||||
lb_name.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
|
||||
if not publisher:
|
||||
if not pkg.model.installed:
|
||||
item.setStyleSheet('QLabel { color: red; }')
|
||||
lb_name.setProperty('publisher_known', 'false')
|
||||
|
||||
publisher = self.i18n['unknown']
|
||||
|
||||
lb_name = QLabel(' {}'.format(publisher))
|
||||
lb_name.setText(' {}'.format(publisher))
|
||||
item.addWidget(lb_name)
|
||||
|
||||
if publisher and full_publisher:
|
||||
lb_name.setToolTip(self.i18n['publisher'].capitalize() + ((': ' + full_publisher) if full_publisher else ''))
|
||||
lb_name.setToolTip(
|
||||
self.i18n['publisher'].capitalize() + ((': ' + full_publisher) if full_publisher else ''))
|
||||
|
||||
if pkg.model.is_trustable():
|
||||
lb_verified = QLabel()
|
||||
lb_verified.setPixmap(self.pixmap_verified)
|
||||
lb_verified.setObjectName('icon_publisher_verified')
|
||||
lb_verified.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
lb_verified.setToolTip(self.i18n['publisher.verified'].capitalize())
|
||||
item.addWidget(lb_verified)
|
||||
else:
|
||||
@@ -501,49 +507,63 @@ class AppsTable(QTableWidget):
|
||||
self.setCellWidget(pkg.table_index, col, item)
|
||||
|
||||
def _set_col_actions(self, col: int, pkg: PackageView):
|
||||
item = QToolBar()
|
||||
item.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
toolbar = QCustomToolbar()
|
||||
toolbar.setObjectName('app_actions')
|
||||
toolbar.add_space()
|
||||
|
||||
if pkg.model.installed:
|
||||
def run():
|
||||
self.window.begin_launch_package(pkg)
|
||||
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_play.svg')), i18n=self.i18n, action=run, tooltip=self.i18n['action.run.tooltip'])
|
||||
bt.setEnabled(pkg.model.can_be_run())
|
||||
item.addWidget(bt)
|
||||
bt = IconButton(i18n=self.i18n, action=run, tooltip=self.i18n['action.run.tooltip'])
|
||||
bt.setObjectName('app_run')
|
||||
|
||||
def handle_click():
|
||||
self.show_pkg_actions(pkg)
|
||||
if not pkg.model.can_be_run():
|
||||
bt.setEnabled(False)
|
||||
bt.setProperty('_enabled', 'false')
|
||||
|
||||
toolbar.layout().addWidget(bt)
|
||||
|
||||
settings = self.has_any_settings(pkg)
|
||||
|
||||
if pkg.model.installed:
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_actions.svg')), i18n=self.i18n, action=handle_click, tooltip=self.i18n['action.settings.tooltip'])
|
||||
def handle_custom_actions():
|
||||
self.show_pkg_actions(pkg)
|
||||
|
||||
bt = IconButton(i18n=self.i18n, action=handle_custom_actions, tooltip=self.i18n['action.settings.tooltip'])
|
||||
bt.setObjectName('app_actions')
|
||||
bt.setEnabled(bool(settings))
|
||||
item.addWidget(bt)
|
||||
toolbar.layout().addWidget(bt)
|
||||
|
||||
if not pkg.model.installed:
|
||||
def show_screenshots():
|
||||
self.window.begin_show_screenshots(pkg)
|
||||
|
||||
bt = IconButton(QIcon(resource.get_path('img/camera.svg')), i18n=self.i18n, action=show_screenshots,
|
||||
bt = IconButton(i18n=self.i18n, action=show_screenshots,
|
||||
tooltip=self.i18n['action.screenshots.tooltip'])
|
||||
bt.setEnabled(bool(pkg.model.has_screenshots()))
|
||||
item.addWidget(bt)
|
||||
bt.setObjectName('app_screenshots')
|
||||
|
||||
if not pkg.model.has_screenshots():
|
||||
bt.setEnabled(False)
|
||||
bt.setProperty('_enabled', 'false')
|
||||
|
||||
toolbar.layout().addWidget(bt)
|
||||
|
||||
def show_info():
|
||||
self.window.begin_show_info(pkg)
|
||||
|
||||
bt = IconButton(QIcon(resource.get_path('img/app_info.svg')), i18n=self.i18n, action=show_info, tooltip=self.i18n['action.info.tooltip'])
|
||||
bt = IconButton(i18n=self.i18n, action=show_info, tooltip=self.i18n['action.info.tooltip'])
|
||||
bt.setObjectName('app_info')
|
||||
bt.setEnabled(bool(pkg.model.has_info()))
|
||||
item.addWidget(bt)
|
||||
toolbar.layout().addWidget(bt)
|
||||
|
||||
self.setCellWidget(pkg.table_index, col, item)
|
||||
self.setCellWidget(pkg.table_index, col, toolbar)
|
||||
|
||||
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents, maximized: bool = False):
|
||||
header_horizontal = self.horizontalHeader()
|
||||
for i in range(self.columnCount()):
|
||||
if maximized:
|
||||
if i not in (3, 4, 7):
|
||||
if i not in (4, 5, 8):
|
||||
header_horizontal.setSectionResizeMode(i, QHeaderView.ResizeToContents)
|
||||
else:
|
||||
header_horizontal.setSectionResizeMode(i, QHeaderView.Stretch)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
GREEN = '#91a069'
|
||||
ORANGE = '#ECB03E'
|
||||
BROWN = '#B47C3E'
|
||||
RED = '#FF614E'
|
||||
|
||||
@@ -47,6 +47,16 @@ def apply_filters(pkg: PackageView, filters: dict, info: dict, limit: bool = Tru
|
||||
info['pkgs_displayed'].append(pkg)
|
||||
|
||||
|
||||
def sum_updates_displayed(info: dict) -> int:
|
||||
updates = 0
|
||||
if info['pkgs_displayed']:
|
||||
for p in info['pkgs_displayed']:
|
||||
if p.model.update and not p.model.is_update_ignored():
|
||||
updates += 1
|
||||
|
||||
return updates
|
||||
|
||||
|
||||
def is_package_hidden(pkg: PackageView, filters: dict) -> bool:
|
||||
hidden = filters['only_apps'] and pkg.model.installed and not pkg.model.is_application()
|
||||
|
||||
|
||||
@@ -3,18 +3,15 @@ import traceback
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Dict, Optional, Set
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize, QTimer
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QIntValidator, QCursor
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QIcon, QIntValidator, QCursor, QFocusEvent
|
||||
from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \
|
||||
QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout, QFileDialog, QTabWidget, QVBoxLayout, \
|
||||
QSlider, QScrollArea, QFrame, QAction, QSpinBox, QPlainTextEdit
|
||||
QSlider, QScrollArea, QFrame, QAction, QSpinBox, QPlainTextEdit, QWidgetAction, QPushButton, QMenu
|
||||
|
||||
from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \
|
||||
TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, TabGroupComponent, PanelComponent, \
|
||||
TwoStateButtonComponent, TextComponent, SpacerComponent, RangeInputComponent, ViewObserver, TextInputType
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.colors import RED
|
||||
from bauh.view.util import resource
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
|
||||
@@ -26,7 +23,7 @@ class QtComponentsManager:
|
||||
self.group_of_groups = {}
|
||||
self._saved_states = {}
|
||||
|
||||
def register_component(self, component_id: int, instance: QWidget, action: QAction = None):
|
||||
def register_component(self, component_id: int, instance: QWidget, action: Optional[QAction] = None):
|
||||
comp = (instance, action, {'v': True, 'e': True, 'r': False})
|
||||
self.components[component_id] = comp
|
||||
self._save_state(comp)
|
||||
@@ -339,6 +336,8 @@ class FormComboBoxQt(QComboBox):
|
||||
def __init__(self, model: SingleSelectComponent):
|
||||
super(FormComboBoxQt, self).__init__()
|
||||
self.model = model
|
||||
self.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.view().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
if model.max_width > 0:
|
||||
self.setMaximumWidth(model.max_width)
|
||||
@@ -401,8 +400,10 @@ class RadioSelectQt(QGroupBox):
|
||||
|
||||
def __init__(self, model: SingleSelectComponent):
|
||||
super(RadioSelectQt, self).__init__(model.label + ' :' if model.label else None)
|
||||
if not model.label:
|
||||
self.setObjectName('radio_select_notitle')
|
||||
|
||||
self.model = model
|
||||
self.setStyleSheet("QGroupBox { font-weight: bold }")
|
||||
|
||||
grid = QGridLayout()
|
||||
self.setLayout(grid)
|
||||
@@ -431,10 +432,10 @@ class ComboSelectQt(QGroupBox):
|
||||
def __init__(self, model: SingleSelectComponent):
|
||||
super(ComboSelectQt, self).__init__()
|
||||
self.model = model
|
||||
self.setLayout(QGridLayout())
|
||||
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
||||
self.layout().addWidget(QLabel(model.label + ' :' if model.label else ''), 0, 0)
|
||||
self.layout().addWidget(FormComboBoxQt(model), 0, 1)
|
||||
self._layout = QGridLayout()
|
||||
self.setLayout(self._layout)
|
||||
self._layout.addWidget(QLabel(model.label + ' :' if model.label else ''), 0, 0)
|
||||
self._layout.addWidget(FormComboBoxQt(model), 0, 1)
|
||||
|
||||
|
||||
class QLineEditObserver(QLineEdit, ViewObserver):
|
||||
@@ -469,9 +470,6 @@ class TextInputQt(QGroupBox):
|
||||
super(TextInputQt, self).__init__()
|
||||
self.model = model
|
||||
self.setLayout(QGridLayout())
|
||||
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
||||
|
||||
self.layout().addWidget(QLabel(model.get_label()), 0, 0)
|
||||
|
||||
if self.model.max_width > 0:
|
||||
self.setMaximumWidth(self.model.max_width)
|
||||
@@ -511,7 +509,6 @@ class MultipleSelectQt(QGroupBox):
|
||||
|
||||
def __init__(self, model: MultipleSelectComponent, callback):
|
||||
super(MultipleSelectQt, self).__init__(model.label if model.label else None)
|
||||
self.setStyleSheet(css.GROUP_BOX)
|
||||
self.model = model
|
||||
self._layout = QGridLayout()
|
||||
self.setLayout(self._layout)
|
||||
@@ -531,17 +528,6 @@ class MultipleSelectQt(QGroupBox):
|
||||
|
||||
col = 0
|
||||
|
||||
pixmap_help = QPixmap()
|
||||
|
||||
for op in model.options: # loads the help icon if at least one option has a tooltip
|
||||
if op.tooltip:
|
||||
try:
|
||||
pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
break
|
||||
|
||||
for op in model.options:
|
||||
comp = CheckboxQt(op, model, callback)
|
||||
|
||||
@@ -555,7 +541,8 @@ class MultipleSelectQt(QGroupBox):
|
||||
|
||||
if op.tooltip:
|
||||
help_icon = QLabel()
|
||||
help_icon.setPixmap(pixmap_help)
|
||||
help_icon.setProperty('help_icon', 'true')
|
||||
help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
help_icon.setToolTip(op.tooltip)
|
||||
widget.layout().addWidget(help_icon)
|
||||
|
||||
@@ -590,23 +577,12 @@ class FormMultipleSelectQt(QWidget):
|
||||
|
||||
if model.label:
|
||||
line = 1
|
||||
self.layout().addWidget(QLabel(), 0, 1)
|
||||
self._layout.addWidget(QLabel(), 0, 1)
|
||||
else:
|
||||
line = 0
|
||||
|
||||
col = 0
|
||||
|
||||
pixmap_help = QPixmap()
|
||||
|
||||
for op in model.options: # loads the help icon if at least one option has a tooltip
|
||||
if op.tooltip:
|
||||
try:
|
||||
pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
break
|
||||
|
||||
for op in model.options:
|
||||
comp = CheckboxQt(op, model, None)
|
||||
|
||||
@@ -620,9 +596,9 @@ class FormMultipleSelectQt(QWidget):
|
||||
|
||||
if op.tooltip:
|
||||
help_icon = QLabel()
|
||||
help_icon.setPixmap(pixmap_help)
|
||||
help_icon.setProperty('help_icon', 'true')
|
||||
help_icon.setToolTip(op.tooltip)
|
||||
help_icon.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
widget.layout().addWidget(help_icon)
|
||||
|
||||
self._layout.addWidget(widget, line, col)
|
||||
@@ -669,40 +645,26 @@ class InputFilter(QLineEdit):
|
||||
self.last_text = p_str
|
||||
|
||||
|
||||
class IconButton(QWidget):
|
||||
class IconButton(QToolButton):
|
||||
|
||||
def __init__(self, icon: QIcon, action, i18n: I18n, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None, expanding: bool = False):
|
||||
def __init__(self, action, i18n: I18n, align: int = Qt.AlignCenter, tooltip: str = None, expanding: bool = False):
|
||||
super(IconButton, self).__init__()
|
||||
self.bt = QToolButton()
|
||||
self.bt.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt.setIcon(icon)
|
||||
self.bt.clicked.connect(action)
|
||||
self.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.clicked.connect(action)
|
||||
self.i18n = i18n
|
||||
self.default_tootip = tooltip
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt.setSizePolicy(QSizePolicy.Expanding if expanding else QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
|
||||
if background:
|
||||
style = 'QToolButton { color: white; background: ' + background + '} '
|
||||
style += 'QToolButton:disabled { color: white; background: blue }'
|
||||
self.bt.setStyleSheet(style)
|
||||
self.setSizePolicy(QSizePolicy.Expanding if expanding else QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
|
||||
if tooltip:
|
||||
self.bt.setToolTip(tooltip)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setAlignment(align)
|
||||
layout.addWidget(self.bt)
|
||||
self.setLayout(layout)
|
||||
self.setToolTip(tooltip)
|
||||
|
||||
def setEnabled(self, enabled):
|
||||
super(IconButton, self).setEnabled(enabled)
|
||||
|
||||
if not enabled:
|
||||
self.bt.setToolTip(self.i18n['icon_button.tooltip.disabled'])
|
||||
self.setToolTip(self.i18n['icon_button.tooltip.disabled'])
|
||||
else:
|
||||
self.bt.setToolTip(self.default_tootip)
|
||||
self.setToolTip(self.default_tootip)
|
||||
|
||||
|
||||
class PanelQt(QWidget):
|
||||
@@ -726,7 +688,6 @@ class FormQt(QGroupBox):
|
||||
self.model = model
|
||||
self.i18n = i18n
|
||||
self.setLayout(QFormLayout())
|
||||
self.setStyleSheet(css.GROUP_BOX)
|
||||
|
||||
if model.spaces:
|
||||
self.layout().addRow(QLabel(), QLabel())
|
||||
@@ -787,7 +748,7 @@ class FormQt(QGroupBox):
|
||||
if hasattr(comp, 'get_label'):
|
||||
text = comp.get_label()
|
||||
else:
|
||||
attr = 'label' if hasattr(comp,'label') else 'value'
|
||||
attr = 'label' if hasattr(comp, 'label') else 'value'
|
||||
text = getattr(comp, attr)
|
||||
|
||||
if text:
|
||||
@@ -803,13 +764,8 @@ class FormQt(QGroupBox):
|
||||
|
||||
def gen_tip_icon(self, tip: str) -> QLabel:
|
||||
tip_icon = QLabel()
|
||||
tip_icon.setProperty('tip_icon', 'true')
|
||||
tip_icon.setToolTip(tip.strip())
|
||||
|
||||
try:
|
||||
tip_icon.setPixmap(QIcon(resource.get_path('img/about.svg')).pixmap(QSize(12, 12)))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
return tip_icon
|
||||
|
||||
def _new_text_input(self, c: TextInputComponent) -> Tuple[QLabel, QLineEdit]:
|
||||
@@ -860,6 +816,7 @@ class FormQt(QGroupBox):
|
||||
|
||||
def _new_range_input(self, model: RangeInputComponent) -> QSpinBox:
|
||||
spinner = QSpinBox()
|
||||
spinner.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
spinner.setMinimum(model.min)
|
||||
spinner.setMaximum(model.max)
|
||||
spinner.setSingleStep(model.step)
|
||||
@@ -929,13 +886,8 @@ class FormQt(QGroupBox):
|
||||
label = self._new_label(c)
|
||||
wrapped = self._wrap(chooser, c)
|
||||
|
||||
try:
|
||||
icon = QIcon(resource.get_path('img/clean.svg'))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
icon = QIcon()
|
||||
|
||||
bt = IconButton(icon, i18n=self.i18n['clean'].capitalize(), action=clean_path, background=RED, tooltip=self.i18n['action.run.tooltip'])
|
||||
bt = IconButton(i18n=self.i18n['clean'].capitalize(), action=clean_path, tooltip=self.i18n['clean'].capitalize())
|
||||
bt.setObjectName('clean_field')
|
||||
|
||||
wrapped.layout().addWidget(bt)
|
||||
return label, wrapped
|
||||
@@ -962,6 +914,8 @@ class TabGroupQt(QTabWidget):
|
||||
scroll.setWidget(to_widget(c.content, i18n))
|
||||
self.addTab(scroll, icon, c.label)
|
||||
|
||||
self.tabBar().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
|
||||
def new_single_select(model: SingleSelectComponent) -> QWidget:
|
||||
if model.type == SelectViewType.RADIO:
|
||||
@@ -974,6 +928,7 @@ def new_single_select(model: SingleSelectComponent) -> QWidget:
|
||||
|
||||
def new_spacer(min_width: int = None) -> QWidget:
|
||||
spacer = QWidget()
|
||||
spacer.setProperty('spacer', 'true')
|
||||
|
||||
if min_width:
|
||||
spacer.setMinimumWidth(min_width)
|
||||
@@ -1019,13 +974,13 @@ class RangeInputQt(QGroupBox):
|
||||
super(RangeInputQt, self).__init__()
|
||||
self.model = model
|
||||
self.setLayout(QGridLayout())
|
||||
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
||||
self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
|
||||
|
||||
if self.model.max_width > 0:
|
||||
self.setMaximumWidth(self.model.max_width)
|
||||
|
||||
self.spinner = QSpinBox()
|
||||
self.spinner.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.spinner.setMinimum(model.min)
|
||||
self.spinner.setMaximum(model.max)
|
||||
self.spinner.setSingleStep(model.step)
|
||||
@@ -1040,3 +995,155 @@ class RangeInputQt(QGroupBox):
|
||||
|
||||
def _update_value(self):
|
||||
self.model.value = self.spinner.value()
|
||||
|
||||
|
||||
class QCustomLineEdit(QLineEdit):
|
||||
|
||||
def __init__(self, focus_in_callback, focus_out_callback, **kwargs):
|
||||
super(QCustomLineEdit, self).__init__(**kwargs)
|
||||
self.focus_in_callback = focus_in_callback
|
||||
self.focus_out_callback = focus_out_callback
|
||||
|
||||
def focusInEvent(self, ev: QFocusEvent):
|
||||
super(QCustomLineEdit, self).focusInEvent(ev)
|
||||
if self.focus_in_callback:
|
||||
self.focus_in_callback()
|
||||
|
||||
def focusOutEvent(self, ev: QFocusEvent):
|
||||
super(QCustomLineEdit, self).focusOutEvent(ev)
|
||||
if self.focus_out_callback:
|
||||
self.focus_out_callback()
|
||||
|
||||
self.clearFocus()
|
||||
|
||||
|
||||
class QSearchBar(QWidget):
|
||||
|
||||
def __init__(self, search_callback, parent: Optional[QWidget] = None):
|
||||
super(QSearchBar, self).__init__(parent=parent)
|
||||
self.setLayout(QHBoxLayout())
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout().setSpacing(0)
|
||||
self.callback = search_callback
|
||||
|
||||
self.inp_search = QCustomLineEdit(focus_in_callback=self._set_focus_in,
|
||||
focus_out_callback=self._set_focus_out)
|
||||
self.inp_search.setObjectName('inp_search')
|
||||
self.inp_search.setFrame(False)
|
||||
self.inp_search.returnPressed.connect(search_callback)
|
||||
search_background_color = self.inp_search.palette().color(self.inp_search.backgroundRole()).name()
|
||||
|
||||
self.search_left_corner = QLabel()
|
||||
self.search_left_corner.setObjectName('lb_left_corner')
|
||||
|
||||
self.layout().addWidget(self.search_left_corner)
|
||||
|
||||
self.layout().addWidget(self.inp_search)
|
||||
|
||||
self.search_button = QPushButton()
|
||||
self.search_button.setObjectName('search_button')
|
||||
self.search_button.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.search_button.clicked.connect(search_callback)
|
||||
|
||||
self.layout().addWidget(self.search_button)
|
||||
|
||||
def clear(self):
|
||||
self.inp_search.clear()
|
||||
|
||||
def text(self) -> str:
|
||||
return self.inp_search.text()
|
||||
|
||||
def set_text(self, text: str):
|
||||
self.inp_search.setText(text)
|
||||
|
||||
def setFocus(self):
|
||||
self.inp_search.setFocus()
|
||||
|
||||
def set_tooltip(self, tip: str):
|
||||
self.inp_search.setToolTip(tip)
|
||||
|
||||
def set_button_tooltip(self, tip: str):
|
||||
self.search_button.setToolTip(tip)
|
||||
|
||||
def set_placeholder(self, placeholder: str):
|
||||
self.inp_search.setPlaceholderText(placeholder)
|
||||
|
||||
def _set_focus_in(self):
|
||||
self.search_button.setProperty('focused', 'true')
|
||||
self.search_left_corner.setProperty('focused', 'true')
|
||||
|
||||
for c in (self.search_button, self.search_left_corner):
|
||||
c.style().unpolish(c)
|
||||
c.style().polish(c)
|
||||
|
||||
def _set_focus_out(self):
|
||||
self.search_button.setProperty('focused', 'false')
|
||||
self.search_left_corner.setProperty('focused', 'false')
|
||||
|
||||
for c in (self.search_button, self.search_left_corner):
|
||||
c.style().unpolish(c)
|
||||
c.style().polish(c)
|
||||
|
||||
|
||||
class QCustomMenuAction(QWidgetAction):
|
||||
|
||||
def __init__(self, parent: QWidget, label: Optional[str] = None, action=None, button_name: Optional[str] = None,
|
||||
icon: Optional[QIcon] = None, tooltip: Optional[str] = None):
|
||||
super(QCustomMenuAction, self).__init__(parent)
|
||||
self.button = QPushButton()
|
||||
self.set_label(label)
|
||||
self._action = None
|
||||
self.set_action(action)
|
||||
self.set_button_name(button_name)
|
||||
self.set_icon(icon)
|
||||
self.setDefaultWidget(self.button)
|
||||
|
||||
if tooltip:
|
||||
self.button.setToolTip(tooltip)
|
||||
|
||||
def set_label(self, label: str):
|
||||
self.button.setText(label)
|
||||
|
||||
def set_action(self, action):
|
||||
self._action = action
|
||||
self.button.clicked.connect(self._handle_action)
|
||||
|
||||
def _handle_action(self):
|
||||
if self._action:
|
||||
self._action()
|
||||
|
||||
if self.parent() and isinstance(self.parent(), QMenu):
|
||||
self.parent().close()
|
||||
|
||||
def set_button_name(self, name: str):
|
||||
if name:
|
||||
self.button.setObjectName(name)
|
||||
|
||||
def set_icon(self, icon: QIcon):
|
||||
if icon:
|
||||
self.button.setIcon(icon)
|
||||
|
||||
def get_label(self) -> str:
|
||||
return self.button.text()
|
||||
|
||||
|
||||
class QCustomToolbar(QWidget):
|
||||
|
||||
def __init__(self, spacing: int = 2, parent: Optional[QWidget] = None, alignment: int = Qt.AlignRight):
|
||||
super(QCustomToolbar, self).__init__(parent=parent)
|
||||
self.setProperty('container', 'true')
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.setLayout(QHBoxLayout())
|
||||
self.layout().setContentsMargins(0, 0, 0, 0)
|
||||
self.layout().setSpacing(spacing)
|
||||
self.layout().setAlignment(alignment)
|
||||
|
||||
def add_widget(self, widget: QWidget):
|
||||
if widget:
|
||||
self.layout().addWidget(widget)
|
||||
|
||||
def add_stretch(self, value: int = 0):
|
||||
self.layout().addStretch(value)
|
||||
|
||||
def add_space(self, min_width: int = 0):
|
||||
self.layout().addWidget(new_spacer(min_width))
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import QSize, Qt
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
|
||||
|
||||
from bauh.api.abstract.view import ViewComponent
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.components import to_widget
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
|
||||
class ConfirmationDialog(QMessageBox):
|
||||
|
||||
def __init__(self, title: str, body: str, i18n: I18n, screen_size: QSize, components: List[ViewComponent] = None,
|
||||
confirmation_label: str = None, deny_label: str = None, deny_button: bool = True, window_cancel: bool = True,
|
||||
confirmation_button: bool = True):
|
||||
super(ConfirmationDialog, self).__init__()
|
||||
|
||||
if not window_cancel:
|
||||
self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
|
||||
self.setWindowTitle(title)
|
||||
self.setStyleSheet('QLabel { margin-right: 25px; }')
|
||||
|
||||
self.bt_yes = None
|
||||
if confirmation_button:
|
||||
self.bt_yes = self.addButton(i18n['popup.button.yes'] if not confirmation_label else confirmation_label.capitalize(), QMessageBox.YesRole)
|
||||
self.bt_yes.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_yes.setStyleSheet(css.OK_BUTTON)
|
||||
self.setDefaultButton(self.bt_yes)
|
||||
|
||||
if deny_button:
|
||||
self.bt_no = self.addButton(i18n['popup.button.no'] if not deny_label else deny_label.capitalize(), QMessageBox.NoRole)
|
||||
self.bt_no.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
if not confirmation_button:
|
||||
self.bt_no.setStyleSheet(css.OK_BUTTON)
|
||||
self.setDefaultButton(self.bt_no)
|
||||
|
||||
label = None
|
||||
if body:
|
||||
if not components:
|
||||
self.setIcon(QMessageBox.Question)
|
||||
label = QLabel(body)
|
||||
self.layout().addWidget(label, 0, 1)
|
||||
|
||||
width = 0
|
||||
if components:
|
||||
scroll = QScrollArea(self)
|
||||
scroll.setFrameShape(QFrame.NoFrame)
|
||||
scroll.setWidgetResizable(True)
|
||||
|
||||
comps_container = QWidget()
|
||||
comps_container.setLayout(QVBoxLayout())
|
||||
scroll.setWidget(comps_container)
|
||||
|
||||
height = 0
|
||||
|
||||
for idx, comp in enumerate(components):
|
||||
inst = to_widget(comp, i18n)
|
||||
height += inst.sizeHint().height()
|
||||
|
||||
if inst.sizeHint().width() > width:
|
||||
width = inst.sizeHint().width()
|
||||
|
||||
comps_container.layout().addWidget(inst)
|
||||
|
||||
height = height if height < int(screen_size.height() / 2.5) else int(screen_size.height() / 2.5)
|
||||
|
||||
scroll.setFixedHeight(height)
|
||||
|
||||
self.layout().addWidget(scroll, 1 if body else 0, 1)
|
||||
|
||||
if label and comps_container.sizeHint().width() > label.sizeHint().width():
|
||||
label.setText(label.text() + (' ' * int(comps_container.sizeHint().width() - label.sizeHint().width())))
|
||||
if not body and width > 0:
|
||||
self.layout().addWidget(QLabel(' ' * int(width / 2)), 1, 1)
|
||||
|
||||
self.exec_()
|
||||
|
||||
def is_confirmed(self) -> bool:
|
||||
return bool(self.bt_yes and self.clickedButton() == self.bt_yes)
|
||||
@@ -1,19 +0,0 @@
|
||||
from bauh.view.qt.colors import GREEN
|
||||
|
||||
OK_BUTTON = """QPushButton { background: %s; color: white; font-weight: bold}
|
||||
QPushButton:disabled { background-color: gray; }""" % GREEN
|
||||
|
||||
GROUP_BOX = """
|
||||
QGroupBox {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
border: 1px solid silver;
|
||||
border-radius: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 7px;
|
||||
padding: 0px 5px 0px 5px;
|
||||
}
|
||||
"""
|
||||
@@ -1,11 +1,12 @@
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, QSize, QMargins
|
||||
from PyQt5.QtGui import QIcon, QCursor
|
||||
from PyQt5.QtWidgets import QMessageBox, QLabel, QWidget, QHBoxLayout
|
||||
from PyQt5.QtWidgets import QMessageBox, QLabel, QWidget, QHBoxLayout, QDialog, QVBoxLayout, QSizePolicy, QApplication, \
|
||||
QStyle, QPushButton, QScrollArea, QFrame
|
||||
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.components import new_spacer
|
||||
from bauh.view.util import resource
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
@@ -28,34 +29,96 @@ def show_message(title: str, body: str, type_: MessageType, icon: QIcon = QIcon(
|
||||
popup.exec_()
|
||||
|
||||
|
||||
def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: List[QWidget] = None):
|
||||
diag = QMessageBox()
|
||||
diag.setIcon(QMessageBox.Question)
|
||||
diag.setWindowTitle(title)
|
||||
diag.setStyleSheet('QLabel { margin-right: 25px; }')
|
||||
diag.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
class ConfirmationDialog(QDialog):
|
||||
|
||||
wbody = QWidget()
|
||||
wbody.setLayout(QHBoxLayout())
|
||||
wbody.layout().addWidget(QLabel(body))
|
||||
def __init__(self, title: str, body: Optional[str], i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')),
|
||||
widgets: Optional[List[QWidget]] = None, confirmation_button: bool = True, deny_button: bool = True,
|
||||
window_cancel: bool = False, confirmation_label: Optional[str] = None, deny_label: Optional[str] = None):
|
||||
super(ConfirmationDialog, self).__init__()
|
||||
|
||||
if widgets:
|
||||
for w in widgets:
|
||||
wbody.layout().addWidget(w)
|
||||
if not window_cancel:
|
||||
self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
|
||||
diag.layout().addWidget(wbody, 0, 1)
|
||||
self.setLayout(QVBoxLayout())
|
||||
self.setWindowTitle(title)
|
||||
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
self.setMinimumWidth(250)
|
||||
self.confirmed = False
|
||||
|
||||
bt_yes = diag.addButton(i18n['popup.button.yes'], QMessageBox.YesRole)
|
||||
bt_yes.setStyleSheet(css.OK_BUTTON)
|
||||
bt_yes.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
diag.setDefaultButton(bt_yes)
|
||||
if icon:
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
bt_no = diag.addButton(i18n['popup.button.no'], QMessageBox.NoRole)
|
||||
bt_no.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
container_body = QWidget()
|
||||
container_body.setObjectName('confirm_container_body')
|
||||
container_body.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
|
||||
if icon:
|
||||
diag.setWindowIcon(icon)
|
||||
if widgets:
|
||||
container_body.setLayout(QVBoxLayout())
|
||||
scroll = QScrollArea(self)
|
||||
scroll.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
scroll.setFrameShape(QFrame.NoFrame)
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setWidget(container_body)
|
||||
self.layout().addWidget(scroll)
|
||||
else:
|
||||
container_body.setLayout(QHBoxLayout())
|
||||
self.layout().addWidget(container_body)
|
||||
|
||||
diag.exec_()
|
||||
lb_icon = QLabel()
|
||||
lb_icon.setObjectName('confirm_icon')
|
||||
lb_icon.setPixmap(QApplication.style().standardIcon(QStyle.SP_MessageBoxQuestion).pixmap(QSize(48, 48)))
|
||||
container_body.layout().addWidget(lb_icon)
|
||||
|
||||
return diag.clickedButton() == bt_yes
|
||||
if body:
|
||||
lb_msg = QLabel(body)
|
||||
lb_msg.setObjectName('confirm_msg')
|
||||
container_body.layout().addWidget(lb_msg)
|
||||
|
||||
if widgets:
|
||||
for w in widgets:
|
||||
container_body.layout().addWidget(w)
|
||||
else:
|
||||
container_body.layout().addWidget(new_spacer())
|
||||
|
||||
container_bottom = QWidget()
|
||||
container_bottom.setObjectName('confirm_container_bottom')
|
||||
container_bottom.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
container_bottom.setLayout(QHBoxLayout())
|
||||
self.layout().addWidget(container_bottom)
|
||||
|
||||
container_bottom.layout().addWidget(new_spacer())
|
||||
|
||||
if confirmation_button:
|
||||
bt_confirm = QPushButton(confirmation_label.capitalize() if confirmation_label else i18n['popup.button.yes'])
|
||||
bt_confirm.setObjectName('ok')
|
||||
bt_confirm.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_confirm.setDefault(True)
|
||||
bt_confirm.setAutoDefault(True)
|
||||
bt_confirm.clicked.connect(self.confirm)
|
||||
container_bottom.layout().addWidget(bt_confirm)
|
||||
|
||||
if deny_button:
|
||||
bt_cancel = QPushButton(deny_label.capitalize() if deny_label else i18n['popup.button.no'])
|
||||
bt_cancel.setObjectName('bt_cancel')
|
||||
bt_cancel.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_cancel.clicked.connect(self.close)
|
||||
container_bottom.layout().addWidget(bt_cancel)
|
||||
|
||||
if not confirmation_button:
|
||||
bt_cancel.setDefault(True)
|
||||
bt_cancel.setAutoDefault(True)
|
||||
|
||||
def confirm(self):
|
||||
self.confirmed = True
|
||||
self.close()
|
||||
|
||||
def ask(self) -> bool:
|
||||
self.exec_()
|
||||
return self.confirmed
|
||||
|
||||
|
||||
def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')),
|
||||
widgets: List[QWidget] = None) -> bool:
|
||||
popup = ConfirmationDialog(title=title, body=body, i18n=i18n, icon=icon, widgets=widgets)
|
||||
popup.exec_()
|
||||
return popup.confirmed
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QPushButton
|
||||
|
||||
from bauh import ROOT_DIR
|
||||
from bauh.api.abstract.view import MultipleSelectComponent, InputOption
|
||||
from bauh.view.core.config import save
|
||||
from bauh.view.core.controller import GenericSoftwareManager
|
||||
from bauh.view.util import resource
|
||||
from bauh.view.qt import qt_utils, css
|
||||
from bauh.view.qt.components import MultipleSelectQt, CheckboxQt, new_spacer
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
|
||||
class GemSelectorPanel(QWidget):
|
||||
|
||||
def __init__(self, window: QWidget, manager: GenericSoftwareManager, i18n: I18n, config: dict, show_panel_after_restart: bool = False):
|
||||
super(GemSelectorPanel, self).__init__()
|
||||
self.window = window
|
||||
self.manager = manager
|
||||
self.config = config
|
||||
self.setLayout(QGridLayout())
|
||||
self.setWindowIcon(QIcon(resource.get_path('img/logo.svg')))
|
||||
self.setWindowTitle(i18n['gem_selector.title'])
|
||||
self.resize(400, 400)
|
||||
self.show_panel_after_restart = show_panel_after_restart
|
||||
|
||||
self.label_question = QLabel(i18n['gem_selector.question'])
|
||||
self.label_question.setStyleSheet('QLabel { font-weight: bold}')
|
||||
self.layout().addWidget(self.label_question, 0, 1, Qt.AlignHCenter)
|
||||
|
||||
self.bt_proceed = QPushButton(i18n['change'].capitalize())
|
||||
self.bt_proceed.setStyleSheet(css.OK_BUTTON)
|
||||
self.bt_proceed.clicked.connect(self.save)
|
||||
|
||||
self.bt_exit = QPushButton(i18n['close'].capitalize())
|
||||
self.bt_exit.clicked.connect(self.exit)
|
||||
|
||||
self.gem_map = {}
|
||||
gem_options = []
|
||||
default = set()
|
||||
|
||||
managers = [*manager.managers]
|
||||
managers.sort(key=lambda c: c.__class__.__name__)
|
||||
|
||||
for m in managers:
|
||||
if m.can_work():
|
||||
modname = m.__module__.split('.')[-2]
|
||||
op = InputOption(label=i18n.get('gem.{}.label'.format(modname), modname.capitalize()),
|
||||
tooltip=i18n.get('gem.{}.info'.format(modname)),
|
||||
value=modname,
|
||||
icon_path='{r}/gems/{n}/resources/img/{n}.svg'.format(r=ROOT_DIR, n=modname))
|
||||
|
||||
gem_options.append(op)
|
||||
self.gem_map[modname] = m
|
||||
|
||||
if m.is_enabled() and m in manager.working_managers:
|
||||
default.add(op)
|
||||
|
||||
if self.config['gems']:
|
||||
default_ops = {o for o in gem_options if o.value in self.config['gems']}
|
||||
else:
|
||||
default_ops = default
|
||||
|
||||
self.bt_proceed.setEnabled(bool(default_ops))
|
||||
|
||||
self.gem_select_model = MultipleSelectComponent(label='', options=gem_options, default_options=default_ops, max_per_line=1)
|
||||
|
||||
self.gem_select = MultipleSelectQt(self.gem_select_model, self.check_state)
|
||||
self.layout().addWidget(self.gem_select, 1, 1)
|
||||
|
||||
self.layout().addWidget(new_spacer(), 2, 1)
|
||||
|
||||
self.layout().addWidget(self.bt_proceed, 3, 1, Qt.AlignRight)
|
||||
self.layout().addWidget(self.bt_exit, 3, 1, Qt.AlignLeft)
|
||||
|
||||
self.adjustSize()
|
||||
self.setFixedSize(self.size())
|
||||
qt_utils.centralize(self)
|
||||
|
||||
def check_state(self, model: CheckboxQt, checked: bool):
|
||||
if self.isVisible():
|
||||
self.bt_proceed.setEnabled(bool(self.gem_select_model.values))
|
||||
|
||||
def save(self):
|
||||
enabled_gems = [op.value for op in self.gem_select_model.values]
|
||||
|
||||
for module, man in self.gem_map.items():
|
||||
enabled = module in enabled_gems
|
||||
man.set_enabled(enabled)
|
||||
|
||||
self.config['gems'] = enabled_gems
|
||||
save(self.config)
|
||||
|
||||
self.manager.reset_cache()
|
||||
self.manager.prepare()
|
||||
self.window.verify_warnings()
|
||||
self.window.types_changed = True
|
||||
self.window.begin_refresh_packages()
|
||||
self.close()
|
||||
|
||||
def exit(self):
|
||||
self.close()
|
||||
@@ -2,8 +2,8 @@ import operator
|
||||
from functools import reduce
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
|
||||
from PyQt5.QtGui import QColor, QIcon, QCursor
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView, QLabel
|
||||
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
from bauh.api.abstract.model import PackageHistory
|
||||
@@ -40,16 +40,19 @@ class HistoryDialog(QDialog):
|
||||
current_status = history.pkg_status_idx == row
|
||||
|
||||
for col, key in enumerate(sorted(data.keys())):
|
||||
item = QTableWidgetItem()
|
||||
item = QLabel()
|
||||
item.setProperty('even', row % 2 == 0)
|
||||
item.setText(str(data[key]))
|
||||
|
||||
if current_status:
|
||||
item.setBackground(QColor(ORANGE if row != 0 else GREEN))
|
||||
item.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
item.setProperty('outdated', str(row != 0).lower())
|
||||
|
||||
tip = '{}. {}.'.format(i18n['popup.history.selected.tooltip'], i18n['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
|
||||
|
||||
item.setToolTip(tip)
|
||||
|
||||
table_history.setItem(row, col, item)
|
||||
table_history.setCellWidget(row, col, item)
|
||||
|
||||
layout.addWidget(table_history)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from PyQt5.QtCore import QSize, Qt
|
||||
from PyQt5.QtGui import QIcon, QCursor
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGroupBox, \
|
||||
QLineEdit, QLabel, QGridLayout, QPushButton, QPlainTextEdit, QToolBar, QScrollArea, QFrame, QWidget
|
||||
QLineEdit, QLabel, QGridLayout, QPushButton, QPlainTextEdit, QScrollArea, QFrame, QWidget, QSizePolicy, \
|
||||
QHBoxLayout
|
||||
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
from bauh.view.qt.components import new_spacer
|
||||
@@ -13,7 +14,7 @@ IGNORED_ATTRS = {'name', '__app__'}
|
||||
class InfoDialog(QDialog):
|
||||
|
||||
def __init__(self, pkg_info: dict, icon_cache: MemoryCache, i18n: I18n, screen_size: QSize):
|
||||
super(InfoDialog, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
super(InfoDialog, self).__init__()
|
||||
self.setWindowTitle(str(pkg_info['__app__']))
|
||||
self.screen_size = screen_size
|
||||
self.i18n = i18n
|
||||
@@ -24,16 +25,19 @@ class InfoDialog(QDialog):
|
||||
scroll.setFrameShape(QFrame.NoFrame)
|
||||
scroll.setWidgetResizable(True)
|
||||
comps_container = QWidget()
|
||||
comps_container.setObjectName('root_container')
|
||||
comps_container.setLayout(QVBoxLayout())
|
||||
scroll.setWidget(comps_container)
|
||||
|
||||
# shows complete field string
|
||||
self.text_field = QPlainTextEdit()
|
||||
self.text_field.setObjectName('full_field')
|
||||
self.text_field.setReadOnly(True)
|
||||
comps_container.layout().addWidget(self.text_field)
|
||||
self.text_field.hide()
|
||||
|
||||
self.gbox_info = QGroupBox()
|
||||
self.gbox_info.setObjectName('fields')
|
||||
self.gbox_info.setLayout(QGridLayout())
|
||||
|
||||
comps_container.layout().addWidget(self.gbox_info)
|
||||
@@ -64,14 +68,14 @@ class InfoDialog(QDialog):
|
||||
show_val = val
|
||||
|
||||
text = QLineEdit()
|
||||
text.setObjectName('field_value')
|
||||
text.setToolTip(show_val)
|
||||
text.setText(val)
|
||||
text.setCursorPosition(0)
|
||||
text.setStyleSheet("width: 400px")
|
||||
text.setReadOnly(True)
|
||||
|
||||
label = QLabel(i18n.get(i18n_key, i18n.get(attr.lower(), attr)).capitalize())
|
||||
label.setStyleSheet("font-weight: bold")
|
||||
label.setObjectName('field_name')
|
||||
|
||||
self.gbox_info.layout().addWidget(label, idx, 0)
|
||||
self.gbox_info.layout().addWidget(text, idx, 1)
|
||||
@@ -79,21 +83,27 @@ class InfoDialog(QDialog):
|
||||
|
||||
layout.addWidget(scroll)
|
||||
|
||||
lower_bar = QToolBar()
|
||||
bt_back = QPushButton(self.i18n['back'].capitalize())
|
||||
bt_back.setVisible(False)
|
||||
bt_back.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_back.clicked.connect(self.back_to_info)
|
||||
lower_container = QWidget()
|
||||
lower_container.setObjectName('lower_container')
|
||||
lower_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
lower_container.setLayout(QHBoxLayout())
|
||||
|
||||
self.ref_bt_back = lower_bar.addWidget(bt_back)
|
||||
lower_bar.addWidget(new_spacer())
|
||||
self.bt_back = QPushButton('< {}'.format(self.i18n['back'].capitalize()))
|
||||
self.bt_back.setObjectName('back')
|
||||
self.bt_back.setVisible(False)
|
||||
self.bt_back.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_back.clicked.connect(self.back_to_info)
|
||||
|
||||
bt_close = QPushButton(self.i18n['close'].capitalize())
|
||||
bt_close.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_close.clicked.connect(self.close)
|
||||
lower_container.layout().addWidget(self.bt_back)
|
||||
lower_container.layout().addWidget(new_spacer())
|
||||
|
||||
lower_bar.addWidget(bt_close)
|
||||
layout.addWidget(lower_bar)
|
||||
self.bt_close = QPushButton(self.i18n['close'].capitalize())
|
||||
self.bt_close.setObjectName('close')
|
||||
self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_close.clicked.connect(self.close)
|
||||
|
||||
lower_container.layout().addWidget(self.bt_close)
|
||||
layout.addWidget(lower_container)
|
||||
self.setMinimumWidth(self.gbox_info.sizeHint().width() * 1.2)
|
||||
self.setMaximumHeight(screen_size.height() * 0.8)
|
||||
self.adjustSize()
|
||||
@@ -102,11 +112,12 @@ class InfoDialog(QDialog):
|
||||
|
||||
def show_full_field():
|
||||
self.gbox_info.hide()
|
||||
self.ref_bt_back.setVisible(True)
|
||||
self.bt_back.setVisible(True)
|
||||
self.text_field.show()
|
||||
self.text_field.setPlainText(val)
|
||||
|
||||
bt_full_field = QPushButton(self.i18n['show'].capitalize())
|
||||
bt_full_field.setObjectName('show')
|
||||
bt_full_field.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_full_field.clicked.connect(show_full_field)
|
||||
self.gbox_info.layout().addWidget(bt_full_field, idx, 2)
|
||||
@@ -115,4 +126,4 @@ class InfoDialog(QDialog):
|
||||
self.text_field.setPlainText("")
|
||||
self.text_field.hide()
|
||||
self.gbox_info.show()
|
||||
self.ref_bt_back.setVisible(False)
|
||||
self.bt_back.setVisible(False)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import datetime
|
||||
import operator
|
||||
from functools import reduce
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from PyQt5.QtCore import QSize, Qt, QThread, pyqtSignal, QCoreApplication
|
||||
from PyQt5.QtGui import QIcon, QCursor
|
||||
from PyQt5.QtGui import QIcon, QCursor, QCloseEvent
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy, QTableWidget, QHeaderView, QPushButton, QToolBar, \
|
||||
QProgressBar, QApplication, QPlainTextEdit, QToolButton, QHBoxLayout
|
||||
|
||||
@@ -12,10 +12,9 @@ from bauh import __app_name__
|
||||
from bauh.api.abstract.context import ApplicationContext
|
||||
from bauh.api.abstract.controller import SoftwareManager
|
||||
from bauh.api.abstract.handler import TaskManager
|
||||
from bauh.view.qt import root, styles
|
||||
from bauh.view.qt.colors import GREEN
|
||||
from bauh.view.qt.components import new_spacer
|
||||
from bauh.view.qt.components import new_spacer, QCustomToolbar
|
||||
from bauh.view.qt.qt_utils import centralize
|
||||
from bauh.view.qt.root import RootDialog
|
||||
from bauh.view.qt.thread import AnimateProgress
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
@@ -37,7 +36,7 @@ class Prepare(QThread, TaskManager):
|
||||
self.password_response = None
|
||||
self._registered = 0
|
||||
|
||||
def ask_password(self) -> Tuple[str, bool]:
|
||||
def ask_password(self) -> Tuple[bool, Optional[str]]:
|
||||
self.waiting_password = True
|
||||
self.signal_ask_password.emit()
|
||||
|
||||
@@ -46,14 +45,14 @@ class Prepare(QThread, TaskManager):
|
||||
|
||||
return self.password_response
|
||||
|
||||
def set_password_reply(self, password: str, valid: bool):
|
||||
self.password_response = password, valid
|
||||
def set_password_reply(self, valid: bool, password: str):
|
||||
self.password_response = valid, password
|
||||
self.waiting_password = False
|
||||
|
||||
def run(self):
|
||||
root_pwd = None
|
||||
if self.manager.requires_root('prepare', None):
|
||||
root_pwd, ok = self.ask_password()
|
||||
ok, root_pwd = self.ask_password()
|
||||
|
||||
if not ok:
|
||||
QCoreApplication.exit(1)
|
||||
@@ -115,7 +114,7 @@ class EnableSkip(QThread):
|
||||
class PreparePanel(QWidget, TaskManager):
|
||||
|
||||
signal_status = pyqtSignal(int)
|
||||
signal_password_response = pyqtSignal(str, bool)
|
||||
signal_password_response = pyqtSignal(bool, str)
|
||||
|
||||
def __init__(self, context: ApplicationContext, manager: SoftwareManager, screen_size: QSize, i18n: I18n, manage_window: QWidget):
|
||||
super(PreparePanel, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
@@ -157,14 +156,14 @@ class PreparePanel(QWidget, TaskManager):
|
||||
self.label_top = QLabel()
|
||||
self.label_top.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.label_top.setText("{}...".format(self.i18n['prepare_panel.title.start'].capitalize()))
|
||||
self.label_top.setObjectName('prepare_status')
|
||||
self.label_top.setAlignment(Qt.AlignHCenter)
|
||||
self.label_top.setStyleSheet("QLabel { font-size: 14px; font-weight: bold; }")
|
||||
self.layout().addWidget(self.label_top)
|
||||
self.layout().addWidget(QLabel())
|
||||
|
||||
self.table = QTableWidget()
|
||||
self.table.setObjectName('tasks')
|
||||
self.table.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.table.setStyleSheet("QTableWidget { background-color: transparent; }")
|
||||
self.table.setFocusPolicy(Qt.NoFocus)
|
||||
self.table.setShowGrid(False)
|
||||
self.table.verticalHeader().setVisible(False)
|
||||
@@ -172,58 +171,63 @@ class PreparePanel(QWidget, TaskManager):
|
||||
self.table.horizontalHeader().setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
self.table.setColumnCount(4)
|
||||
self.table.setHorizontalHeaderLabels(['' for _ in range(4)])
|
||||
self.table.horizontalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.table.verticalScrollBar().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.layout().addWidget(self.table)
|
||||
|
||||
self.textarea_output = QPlainTextEdit(self)
|
||||
self.textarea_output.resize(self.table.size())
|
||||
self.textarea_output.setStyleSheet("background: black; color: white;")
|
||||
self.layout().addWidget(self.textarea_output)
|
||||
self.textarea_output.setVisible(False)
|
||||
self.textarea_output.setReadOnly(True)
|
||||
self.textarea_output.setMaximumHeight(100)
|
||||
self.textarea_details = QPlainTextEdit(self)
|
||||
self.textarea_details.setObjectName('task_details')
|
||||
self.textarea_details.setProperty('console', 'true')
|
||||
self.textarea_details.resize(self.table.size())
|
||||
self.layout().addWidget(self.textarea_details)
|
||||
self.textarea_details.setVisible(False)
|
||||
self.textarea_details.setReadOnly(True)
|
||||
self.textarea_details.setMaximumHeight(100)
|
||||
self.current_output_task = None
|
||||
|
||||
self.bottom_widget = QWidget()
|
||||
self.bottom_widget.setLayout(QHBoxLayout())
|
||||
self.bottom_widget.layout().addStretch()
|
||||
bt_hide_output = QPushButton(self.i18n['prepare.bt_hide_details'])
|
||||
bt_hide_output.setStyleSheet('QPushButton { text-decoration: underline; border: 0px; background: none } ')
|
||||
bt_hide_output.clicked.connect(self.hide_output)
|
||||
bt_hide_output.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bottom_widget.layout().addWidget(bt_hide_output)
|
||||
|
||||
bt_hide_details = QPushButton(self.i18n['prepare.bt_hide_details'])
|
||||
bt_hide_details.setObjectName('bt_hide_details')
|
||||
bt_hide_details.clicked.connect(self.hide_output)
|
||||
bt_hide_details.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bottom_widget.layout().addWidget(bt_hide_details)
|
||||
self.bottom_widget.layout().addStretch()
|
||||
self.layout().addWidget(self.bottom_widget)
|
||||
self.bottom_widget.setVisible(False)
|
||||
|
||||
self.bt_bar = QToolBar()
|
||||
self.bt_bar = QCustomToolbar()
|
||||
self.bt_close = QPushButton(self.i18n['close'].capitalize())
|
||||
self.bt_close.setObjectName('bt_cancel')
|
||||
self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_close.clicked.connect(self.close)
|
||||
self.bt_close.setVisible(False)
|
||||
self.ref_bt_close = self.bt_bar.addWidget(self.bt_close)
|
||||
self.bt_bar.add_widget(self.bt_close)
|
||||
self.bt_bar.add_widget(new_spacer())
|
||||
|
||||
self.bt_bar.addWidget(new_spacer())
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
|
||||
self.progress_bar.setObjectName('prepare_progress')
|
||||
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
|
||||
self.progress_bar.setTextVisible(False)
|
||||
self.progress_bar.setVisible(False)
|
||||
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.ref_progress_bar = self.bt_bar.addWidget(self.progress_bar)
|
||||
self.bt_bar.addWidget(new_spacer())
|
||||
self.bt_bar.add_widget(self.progress_bar)
|
||||
self.bt_bar.add_widget(new_spacer())
|
||||
|
||||
self.bt_skip = QPushButton(self.i18n['prepare_panel.bt_skip.label'].capitalize())
|
||||
self.bt_skip.clicked.connect(self.finish)
|
||||
self.bt_skip.setEnabled(False)
|
||||
self.bt_skip.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.bt_bar.addWidget(self.bt_skip)
|
||||
self.bt_bar.add_widget(self.bt_skip)
|
||||
|
||||
self.layout().addWidget(self.bt_bar)
|
||||
|
||||
def hide_output(self):
|
||||
self.current_output_task = None
|
||||
self.textarea_output.setVisible(False)
|
||||
self.textarea_output.clear()
|
||||
self.textarea_details.setVisible(False)
|
||||
self.textarea_details.clear()
|
||||
self.bottom_widget.setVisible(False)
|
||||
self._resize_columns()
|
||||
self.setFocus(Qt.NoFocusReason)
|
||||
@@ -232,8 +236,8 @@ class PreparePanel(QWidget, TaskManager):
|
||||
self.bt_bar.setVisible(True)
|
||||
|
||||
def ask_root_password(self):
|
||||
root_pwd, ok = root.ask_root_password(self.context, self.i18n)
|
||||
self.signal_password_response.emit(root_pwd, ok)
|
||||
valid, root_pwd = RootDialog.ask_password(self.context, self.i18n)
|
||||
self.signal_password_response.emit(valid, root_pwd)
|
||||
|
||||
def _enable_skip_button(self):
|
||||
self.bt_skip.setEnabled(True)
|
||||
@@ -250,7 +254,7 @@ class PreparePanel(QWidget, TaskManager):
|
||||
for i in range(self.table.columnCount()):
|
||||
header_horizontal.setSectionResizeMode(i, QHeaderView.ResizeToContents)
|
||||
|
||||
self.resize(self.get_table_width() * 1.05, self.sizeHint().height())
|
||||
self.resize(int(self.get_table_width() * 1.05), self.sizeHint().height())
|
||||
|
||||
def show(self):
|
||||
super(PreparePanel, self).show()
|
||||
@@ -264,10 +268,10 @@ class PreparePanel(QWidget, TaskManager):
|
||||
|
||||
self.progress_thread.start()
|
||||
|
||||
self.ref_bt_close.setVisible(True)
|
||||
self.ref_progress_bar.setVisible(True)
|
||||
self.bt_close.setVisible(True)
|
||||
self.progress_bar.setVisible(True)
|
||||
|
||||
def closeEvent(self, QCloseEvent):
|
||||
def closeEvent(self, ev: QCloseEvent):
|
||||
if not self.self_close:
|
||||
QCoreApplication.exit()
|
||||
|
||||
@@ -277,14 +281,15 @@ class PreparePanel(QWidget, TaskManager):
|
||||
task_row = self.added_tasks - 1
|
||||
|
||||
icon_widget = QWidget()
|
||||
icon_widget.setProperty('container', 'true')
|
||||
icon_widget.setLayout(QHBoxLayout())
|
||||
icon_widget.layout().setContentsMargins(10, 0, 10, 0)
|
||||
|
||||
bt_icon = QToolButton()
|
||||
bt_icon.setObjectName('bt_task')
|
||||
bt_icon.setCursor(QCursor(Qt.WaitCursor))
|
||||
bt_icon.setEnabled(False)
|
||||
bt_icon.setToolTip(self.i18n['prepare.bt_icon.no_output'])
|
||||
bt_icon.setFixedSize(QSize(24, 24))
|
||||
bt_icon.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
|
||||
if icon_path:
|
||||
bt_icon.setIcon(QIcon(icon_path))
|
||||
@@ -294,11 +299,11 @@ class PreparePanel(QWidget, TaskManager):
|
||||
|
||||
if lines:
|
||||
self.current_output_task = id_
|
||||
self.textarea_output.clear()
|
||||
self.textarea_output.setVisible(True)
|
||||
self.textarea_details.clear()
|
||||
self.textarea_details.setVisible(True)
|
||||
|
||||
for l in lines:
|
||||
self.textarea_output.appendPlainText(l)
|
||||
self.textarea_details.appendPlainText(l)
|
||||
|
||||
self.bottom_widget.setVisible(True)
|
||||
|
||||
@@ -313,22 +318,27 @@ class PreparePanel(QWidget, TaskManager):
|
||||
self.table.setCellWidget(task_row, 0, icon_widget)
|
||||
|
||||
lb_status = QLabel(label)
|
||||
lb_status.setObjectName('task_status')
|
||||
lb_status.setProperty('status', 'running')
|
||||
lb_status.setCursor(Qt.WaitCursor)
|
||||
lb_status.setMinimumWidth(50)
|
||||
lb_status.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
lb_status.setStyleSheet("QLabel { font-weight: bold; }")
|
||||
self.table.setCellWidget(task_row, 1, lb_status)
|
||||
lb_status_col = 1
|
||||
self.table.setCellWidget(task_row, lb_status_col, lb_status)
|
||||
|
||||
lb_progress = QLabel('{0:.2f}'.format(0) + '%')
|
||||
lb_progress.setObjectName('task_progress')
|
||||
lb_progress.setProperty('status', 'running')
|
||||
lb_progress.setCursor(Qt.WaitCursor)
|
||||
lb_progress.setContentsMargins(10, 0, 10, 0)
|
||||
lb_progress.setStyleSheet("QLabel { font-weight: bold; }")
|
||||
lb_progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
lb_progress_col = 2
|
||||
|
||||
self.table.setCellWidget(task_row, 2, lb_progress)
|
||||
self.table.setCellWidget(task_row, lb_progress_col, lb_progress)
|
||||
|
||||
lb_sub = QLabel()
|
||||
lb_status.setCursor(Qt.WaitCursor)
|
||||
lb_sub.setObjectName('task_substatus')
|
||||
lb_sub.setCursor(Qt.WaitCursor)
|
||||
lb_sub.setContentsMargins(10, 0, 10, 0)
|
||||
lb_sub.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
lb_sub.setMinimumWidth(50)
|
||||
@@ -336,10 +346,13 @@ class PreparePanel(QWidget, TaskManager):
|
||||
|
||||
self.tasks[id_] = {'bt_icon': bt_icon,
|
||||
'lb_status': lb_status,
|
||||
'lb_status_col': lb_status_col,
|
||||
'lb_prog': lb_progress,
|
||||
'lb_prog_col': lb_progress_col,
|
||||
'progress': 0,
|
||||
'lb_sub': lb_sub,
|
||||
'finished': False}
|
||||
'finished': False,
|
||||
'row': task_row}
|
||||
|
||||
def update_progress(self, task_id: str, progress: float, substatus: str):
|
||||
task = self.tasks[task_id]
|
||||
@@ -369,14 +382,18 @@ class PreparePanel(QWidget, TaskManager):
|
||||
full_output.append(output)
|
||||
|
||||
if self.current_output_task == task_id:
|
||||
self.textarea_output.appendPlainText(output)
|
||||
self.textarea_details.appendPlainText(output)
|
||||
|
||||
def finish_task(self, task_id: str):
|
||||
task = self.tasks[task_id]
|
||||
task['lb_sub'].setText('')
|
||||
|
||||
for key in ('lb_prog', 'lb_status'):
|
||||
task[key].setStyleSheet('QLabel { color: %s; text-decoration: line-through; }' % GREEN)
|
||||
label = task[key]
|
||||
label.setProperty('status', 'done')
|
||||
label.style().unpolish(label)
|
||||
label.style().polish(label)
|
||||
label.update()
|
||||
|
||||
task['finished'] = True
|
||||
self._resize_columns()
|
||||
@@ -389,7 +406,7 @@ class PreparePanel(QWidget, TaskManager):
|
||||
|
||||
def finish(self):
|
||||
if self.isVisible():
|
||||
self.manage_window.begin_refresh_packages()
|
||||
self.manage_window.show()
|
||||
self.manage_window.begin_refresh_packages()
|
||||
self.self_close = True
|
||||
self.close()
|
||||
|
||||
@@ -19,3 +19,11 @@ def load_icon(path: str, width: int, height: int = None) -> QIcon:
|
||||
|
||||
def load_resource_icon(path: str, width: int, height: int = None) -> QIcon:
|
||||
return load_icon(resource.get_path(path), width, height)
|
||||
|
||||
|
||||
def measure_based_on_width(percent: float) -> int:
|
||||
return round(percent * QApplication.primaryScreen().size().width())
|
||||
|
||||
|
||||
def measure_based_on_height(percent: float) -> int:
|
||||
return round(percent * QApplication.primaryScreen().size().height())
|
||||
|
||||
@@ -1,100 +1,164 @@
|
||||
import os
|
||||
from typing import Tuple
|
||||
import traceback
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCursor, QIcon
|
||||
from PyQt5.QtWidgets import QInputDialog, QLineEdit, QDialogButtonBox, QApplication
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import QLineEdit, QApplication, QDialog, QPushButton, QVBoxLayout, \
|
||||
QSizePolicy, QToolBar, QLabel
|
||||
|
||||
from bauh.api.abstract.context import ApplicationContext
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.commons.system import new_subprocess
|
||||
from bauh.view.core.config import read_config
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.components import QtComponentsManager
|
||||
from bauh.view.qt.dialog import show_message
|
||||
from bauh.view.qt.components import QtComponentsManager, new_spacer
|
||||
from bauh.view.util import util
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
ACTION_ASK_ROOT = 99
|
||||
|
||||
|
||||
class ValidatePassword(QThread):
|
||||
|
||||
signal_valid = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, password: Optional[str] = None):
|
||||
super(ValidatePassword, self).__init__()
|
||||
self.password = password
|
||||
|
||||
def run(self):
|
||||
if self.password is not None:
|
||||
try:
|
||||
valid = validate_password(self.password)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
valid = False
|
||||
|
||||
self.signal_valid.emit(valid)
|
||||
|
||||
|
||||
class RootDialog(QDialog):
|
||||
|
||||
def __init__(self, i18n: I18n, max_tries: int = 3):
|
||||
super(RootDialog, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
self.i18n = i18n
|
||||
self.max_tries = max_tries
|
||||
self.tries = 0
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.setWindowIcon(util.get_default_icon()[1])
|
||||
self.setWindowTitle(i18n['popup.root.title'])
|
||||
self.setLayout(QVBoxLayout())
|
||||
self.setMinimumWidth(300)
|
||||
|
||||
self.label_msg = QLabel(i18n['popup.root.msg'])
|
||||
self.label_msg.setObjectName('message')
|
||||
self.layout().addWidget(self.label_msg)
|
||||
|
||||
self.input_password = QLineEdit()
|
||||
self.input_password.setObjectName('password')
|
||||
self.layout().addWidget(self.input_password)
|
||||
|
||||
self.label_error = QLabel()
|
||||
self.label_error.setProperty('error', 'true')
|
||||
self.layout().addWidget(self.label_error)
|
||||
self.label_error.hide()
|
||||
|
||||
self.lower_bar = QToolBar()
|
||||
self.layout().addWidget(self.lower_bar)
|
||||
|
||||
self.lower_bar.addWidget(new_spacer())
|
||||
self.bt_ok = QPushButton(i18n['popup.root.continue'])
|
||||
self.bt_ok.setDefault(True)
|
||||
self.bt_ok.setAutoDefault(True)
|
||||
self.bt_ok.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_ok.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt_ok.setObjectName('ok')
|
||||
self.bt_ok.clicked.connect(self._validate_password)
|
||||
self.lower_bar.addWidget(self.bt_ok)
|
||||
|
||||
self.bt_cancel = QPushButton()
|
||||
self.bt_cancel.setText(i18n['popup.button.cancel'])
|
||||
|
||||
self.bt_cancel.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_cancel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt_cancel.setObjectName('bt_cancel')
|
||||
self.bt_cancel.clicked.connect(self.close)
|
||||
self.lower_bar.addWidget(self.bt_cancel)
|
||||
self.lower_bar.addWidget(new_spacer())
|
||||
|
||||
self.valid = False
|
||||
self.password = None
|
||||
self.validate_password = ValidatePassword()
|
||||
self.validate_password.signal_valid.connect(self._handle_password_validated)
|
||||
|
||||
def _validate_password(self):
|
||||
password = self.input_password.text().strip()
|
||||
|
||||
if password:
|
||||
self.password = password
|
||||
self.tries += 1
|
||||
self.bt_ok.setEnabled(False)
|
||||
self.bt_cancel.setEnabled(False)
|
||||
self.input_password.setEnabled(False)
|
||||
self.label_error.setText('')
|
||||
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
self.validate_password.password = password
|
||||
self.validate_password.start()
|
||||
|
||||
def _handle_password_validated(self, valid: bool):
|
||||
self.valid = valid
|
||||
|
||||
QApplication.restoreOverrideCursor()
|
||||
tries_ended = self.tries == self.max_tries
|
||||
|
||||
if not self.valid:
|
||||
self.label_error.show()
|
||||
self.bt_cancel.setEnabled(True)
|
||||
|
||||
if tries_ended:
|
||||
self.bt_cancel.setText(self.i18n['close'].capitalize())
|
||||
self.label_error.setText(self.i18n['popup.root.bad_password.last_try'])
|
||||
self.bt_cancel.setFocus()
|
||||
else:
|
||||
self.label_error.setText(self.i18n['popup.root.bad_password.body'])
|
||||
self.bt_ok.setEnabled(True)
|
||||
self.input_password.setEnabled(True)
|
||||
self.input_password.setFocus()
|
||||
else:
|
||||
self.close()
|
||||
|
||||
@staticmethod
|
||||
def ask_password(context: ApplicationContext, i18n: I18n, app_config: Optional[dict] = None,
|
||||
comp_manager: Optional[QtComponentsManager] = None, tries: int = 3) -> Tuple[bool, Optional[str]]:
|
||||
|
||||
current_config = read_config() if not app_config else app_config
|
||||
|
||||
store_password = bool(current_config['store_root_password'])
|
||||
|
||||
if store_password and context.root_password and validate_password(context.root_password):
|
||||
return True, context.root_password
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
|
||||
comp_manager.disable_visible()
|
||||
|
||||
diag = RootDialog(i18n=i18n, max_tries=tries)
|
||||
diag.exec()
|
||||
password = diag.password
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
if password is not None and store_password:
|
||||
context.root_password = password
|
||||
|
||||
return (True, password) if diag.valid else (False, None)
|
||||
|
||||
|
||||
def is_root():
|
||||
return os.getuid() == 0
|
||||
|
||||
|
||||
def ask_root_password(context: ApplicationContext, i18n: I18n,
|
||||
app_config: dict = None,
|
||||
comp_manager: QtComponentsManager = None) -> Tuple[str, bool]:
|
||||
|
||||
cur_config = read_config() if not app_config else app_config
|
||||
store_password = bool(cur_config['store_root_password'])
|
||||
|
||||
if store_password and context.root_password and validate_password(context.root_password):
|
||||
return context.root_password, True
|
||||
|
||||
diag = QInputDialog(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
diag.setStyleSheet("""QLineEdit { border-radius: 5px; font-size: 16px; border: 1px solid lightblue }""")
|
||||
diag.setInputMode(QInputDialog.TextInput)
|
||||
diag.setTextEchoMode(QLineEdit.Password)
|
||||
diag.setWindowIcon(util.get_default_icon()[1])
|
||||
diag.setWindowTitle(i18n['popup.root.title'])
|
||||
diag.setLabelText('')
|
||||
diag.setOkButtonText(i18n['popup.root.continue'].capitalize())
|
||||
diag.setCancelButtonText(i18n['popup.button.cancel'].capitalize())
|
||||
|
||||
bt_box = [c for c in diag.children() if isinstance(c, QDialogButtonBox)][0]
|
||||
|
||||
for bt in bt_box.buttons():
|
||||
if bt_box.buttonRole(bt) == QDialogButtonBox.AcceptRole:
|
||||
bt.setStyleSheet(css.OK_BUTTON)
|
||||
|
||||
bt.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt.setIcon(QIcon())
|
||||
|
||||
diag.resize(400, 200)
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
|
||||
comp_manager.disable_visible()
|
||||
|
||||
for attempt in range(3):
|
||||
|
||||
ok = diag.exec_()
|
||||
|
||||
if ok:
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
valid = validate_password(diag.textValue())
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
if not valid:
|
||||
body = i18n['popup.root.bad_password.body']
|
||||
|
||||
if attempt == 2:
|
||||
body += '. ' + i18n['popup.root.bad_password.last_try']
|
||||
|
||||
show_message(title=i18n['popup.root.bad_password.title'],
|
||||
body=body,
|
||||
type_=MessageType.ERROR)
|
||||
ok = False
|
||||
diag.setTextValue('')
|
||||
|
||||
if ok:
|
||||
if store_password:
|
||||
context.root_password = diag.textValue()
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
return diag.textValue(), ok
|
||||
else:
|
||||
break
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
return '', False
|
||||
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
clean = new_subprocess(['sudo', '-k']).stdout
|
||||
echo = new_subprocess(['echo', password], stdin=clean).stdout
|
||||
|
||||
@@ -4,11 +4,12 @@ from typing import List
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QCursor
|
||||
from PyQt5.QtWidgets import QDialog, QLabel, QPushButton, QToolBar, QVBoxLayout, QProgressBar, QApplication
|
||||
from PyQt5.QtWidgets import QDialog, QLabel, QPushButton, QVBoxLayout, QProgressBar, QApplication, QWidget, \
|
||||
QSizePolicy, QHBoxLayout
|
||||
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
from bauh.api.http import HttpClient
|
||||
from bauh.view.qt import qt_utils, styles
|
||||
from bauh.view.qt import qt_utils
|
||||
from bauh.view.qt.components import new_spacer
|
||||
from bauh.view.qt.thread import AnimateProgress
|
||||
from bauh.view.qt.view_model import PackageView
|
||||
@@ -17,9 +18,6 @@ from bauh.view.util.translation import I18n
|
||||
|
||||
class ScreenshotsDialog(QDialog):
|
||||
|
||||
MAX_HEIGHT = 600
|
||||
MAX_WIDTH = 800
|
||||
|
||||
def __init__(self, pkg: PackageView, http_client: HttpClient, icon_cache: MemoryCache, i18n: I18n, screenshots: List[QPixmap], logger: logging.Logger):
|
||||
super(ScreenshotsDialog, self).__init__()
|
||||
self.setWindowTitle(str(pkg))
|
||||
@@ -27,12 +25,11 @@ class ScreenshotsDialog(QDialog):
|
||||
self.logger = logger
|
||||
self.loaded_imgs = []
|
||||
self.download_threads = []
|
||||
self.resize(1280, 720)
|
||||
self.i18n = i18n
|
||||
self.http_client = http_client
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setObjectName('progress_screenshots')
|
||||
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
|
||||
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 6)
|
||||
self.progress_bar.setTextVisible(False)
|
||||
self.thread_progress = AnimateProgress()
|
||||
@@ -50,39 +47,48 @@ class ScreenshotsDialog(QDialog):
|
||||
self.setWindowIcon(QIcon(pkg.model.get_type_icon_path()))
|
||||
self.setLayout(QVBoxLayout())
|
||||
|
||||
self.layout().addWidget(new_spacer())
|
||||
self.img = QLabel()
|
||||
self.img.setObjectName('image')
|
||||
self.layout().addWidget(self.img)
|
||||
self.layout().addWidget(new_spacer())
|
||||
|
||||
self.bottom_bar = QToolBar()
|
||||
self.container_buttons = QWidget()
|
||||
self.container_buttons.setObjectName('buttons_container')
|
||||
self.container_buttons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
self.container_buttons.setContentsMargins(0, 0, 0, 0)
|
||||
self.container_buttons.setLayout(QHBoxLayout())
|
||||
|
||||
self.bt_back = QPushButton(' < ' + self.i18n['screenshots.bt_back.label'].capitalize())
|
||||
self.bt_back.setObjectName('back')
|
||||
self.bt_back.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_back.clicked.connect(self.back)
|
||||
self.ref_bt_back = self.bottom_bar.addWidget(self.bt_back)
|
||||
self.bottom_bar.addWidget(new_spacer(50))
|
||||
self.container_buttons.layout().addWidget(self.bt_back)
|
||||
self.container_buttons.layout().addWidget(new_spacer())
|
||||
|
||||
self.img_label = QLabel()
|
||||
self.img_label.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.img_label.setStyleSheet('QLabel { font-weight: bold; text-align: center }')
|
||||
self.ref_img_label = self.bottom_bar.addWidget(self.img_label)
|
||||
self.ref_img_label.setVisible(False)
|
||||
self.ref_progress_bar = self.bottom_bar.addWidget(self.progress_bar)
|
||||
self.bottom_bar.addWidget(new_spacer(50))
|
||||
self.container_buttons.layout().addWidget(self.progress_bar)
|
||||
self.container_buttons.layout().addWidget(new_spacer())
|
||||
|
||||
self.bt_next = QPushButton(self.i18n['screenshots.bt_next.label'].capitalize() + ' > ')
|
||||
self.bt_next.setObjectName('next')
|
||||
self.bt_next.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_next.clicked.connect(self.next)
|
||||
self.ref_bt_next = self.bottom_bar.addWidget(self.bt_next)
|
||||
self.container_buttons.layout().addWidget(self.bt_next)
|
||||
|
||||
self.layout().addWidget(self.bottom_bar)
|
||||
self.layout().addWidget(self.container_buttons)
|
||||
|
||||
self.img_idx = 0
|
||||
screen_size = QApplication.primaryScreen().size()
|
||||
self.max_img_width = int(screen_size.width() * 0.585651537)
|
||||
self.max_img_height = int(screen_size.height() * 0.78125)
|
||||
|
||||
for idx, s in enumerate(self.screenshots):
|
||||
t = Thread(target=self._download_img, args=(idx, s), daemon=True)
|
||||
t.start()
|
||||
|
||||
self.resize(self.max_img_width, self.max_img_height)
|
||||
self._load_img()
|
||||
qt_utils.centralize(self)
|
||||
|
||||
def _update_progress(self, val: int):
|
||||
self.progress_bar.setValue(val)
|
||||
@@ -92,32 +98,28 @@ class ScreenshotsDialog(QDialog):
|
||||
img = self.loaded_imgs[self.img_idx]
|
||||
|
||||
if isinstance(img, QPixmap):
|
||||
self.img_label.setText('')
|
||||
self.img.setText('')
|
||||
self.img.setPixmap(img)
|
||||
else:
|
||||
self.img_label.setText(img)
|
||||
self.img.setText(img)
|
||||
self.img.setPixmap(QPixmap())
|
||||
|
||||
self.img.unsetCursor()
|
||||
self.thread_progress.stop = True
|
||||
self.ref_progress_bar.setVisible(False)
|
||||
self.ref_img_label.setVisible(True)
|
||||
self.progress_bar.setVisible(False)
|
||||
else:
|
||||
self.img.setPixmap(QPixmap())
|
||||
self.img.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.ref_img_label.setVisible(False)
|
||||
self.ref_progress_bar.setVisible(True)
|
||||
self.img.setText('{}...'.format(self.i18n['screenshots.image.loading']))
|
||||
self.progress_bar.setVisible(True)
|
||||
self.thread_progress.start()
|
||||
|
||||
if len(self.screenshots) == 1:
|
||||
self.ref_bt_back.setVisible(False)
|
||||
self.ref_bt_next.setVisible(False)
|
||||
self.bt_back.setVisible(False)
|
||||
self.bt_next.setVisible(False)
|
||||
else:
|
||||
self.ref_bt_back.setEnabled(self.img_idx != 0)
|
||||
self.ref_bt_next.setEnabled(self.img_idx != len(self.screenshots) - 1)
|
||||
|
||||
self.adjustSize()
|
||||
qt_utils.centralize(self)
|
||||
self.bt_back.setEnabled(self.img_idx != 0)
|
||||
self.bt_next.setEnabled(self.img_idx != len(self.screenshots) - 1)
|
||||
|
||||
def _download_img(self, idx: int, url: str):
|
||||
self.logger.info('Downloading image [{}] from {}'.format(idx, url))
|
||||
@@ -133,8 +135,8 @@ class ScreenshotsDialog(QDialog):
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(res.content)
|
||||
|
||||
if pixmap.size().height() > self.MAX_HEIGHT or pixmap.size().width() > self.MAX_WIDTH:
|
||||
pixmap = pixmap.scaled(self.MAX_WIDTH, self.MAX_HEIGHT, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
if pixmap.size().height() > self.max_img_height or pixmap.size().width() > self.max_img_width:
|
||||
pixmap = pixmap.scaled(self.max_img_width, self.max_img_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
|
||||
self.loaded_imgs.append(pixmap)
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import gc
|
||||
from io import StringIO
|
||||
from typing import Optional
|
||||
|
||||
from PyQt5.QtCore import QSize, Qt, QCoreApplication
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolBar, QSizePolicy, QPushButton
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QPushButton, QHBoxLayout
|
||||
|
||||
from bauh import __app_name__
|
||||
from bauh.api.abstract.controller import SoftwareManager
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.view.core.controller import GenericSoftwareManager
|
||||
from bauh.view.qt import dialog, css
|
||||
from bauh.view.qt import dialog
|
||||
from bauh.view.qt.components import to_widget, new_spacer
|
||||
from bauh.view.qt.dialog import ConfirmationDialog
|
||||
from bauh.view.qt.qt_utils import centralize
|
||||
from bauh.view.util import util
|
||||
from bauh.view.util.translation import I18n
|
||||
@@ -18,7 +20,7 @@ from bauh.view.util.translation import I18n
|
||||
|
||||
class SettingsWindow(QWidget):
|
||||
|
||||
def __init__(self, manager: SoftwareManager, i18n: I18n, screen_size: QSize, window: QWidget, parent: QWidget = None):
|
||||
def __init__(self, manager: SoftwareManager, i18n: I18n, screen_size: QSize, window: QWidget, parent: Optional[QWidget] = None):
|
||||
super(SettingsWindow, self).__init__(parent=parent, flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
self.setWindowTitle('{} ({})'.format(i18n['settings'].capitalize(), __app_name__))
|
||||
self.setLayout(QVBoxLayout())
|
||||
@@ -30,28 +32,34 @@ class SettingsWindow(QWidget):
|
||||
self.settings_model = self.manager.get_settings(screen_size.width(), screen_size.height())
|
||||
|
||||
tab_group = to_widget(self.settings_model, i18n)
|
||||
tab_group.setMinimumWidth(int(screen_size.width() / 3))
|
||||
tab_group.setObjectName('settings')
|
||||
self.layout().addWidget(tab_group)
|
||||
|
||||
action_bar = QToolBar()
|
||||
action_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
lower_container = QWidget()
|
||||
lower_container.setObjectName('lower_container')
|
||||
lower_container.setProperty('container', 'true')
|
||||
lower_container.setLayout(QHBoxLayout())
|
||||
lower_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
|
||||
bt_close = QPushButton()
|
||||
bt_close.setObjectName('cancel')
|
||||
bt_close.setAutoDefault(True)
|
||||
bt_close.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_close.setText(self.i18n['close'].capitalize())
|
||||
bt_close.clicked.connect(lambda: self.close())
|
||||
action_bar.addWidget(bt_close)
|
||||
lower_container.layout().addWidget(bt_close)
|
||||
|
||||
action_bar.addWidget(new_spacer())
|
||||
lower_container.layout().addWidget(new_spacer())
|
||||
|
||||
bt_change = QPushButton()
|
||||
bt_change.setAutoDefault(True)
|
||||
bt_change.setObjectName('ok')
|
||||
bt_change.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_change.setStyleSheet(css.OK_BUTTON)
|
||||
bt_change.setText(self.i18n['change'].capitalize())
|
||||
bt_change.clicked.connect(self._save_settings)
|
||||
action_bar.addWidget(bt_change)
|
||||
lower_container.layout().addWidget(bt_change)
|
||||
|
||||
self.layout().addWidget(action_bar)
|
||||
self.layout().addWidget(lower_container)
|
||||
|
||||
def show(self):
|
||||
super(SettingsWindow, self).show()
|
||||
@@ -83,10 +91,10 @@ class SettingsWindow(QWidget):
|
||||
body=self.i18n['settings.changed.success.warning'],
|
||||
type_=MessageType.INFO)
|
||||
QCoreApplication.exit()
|
||||
elif dialog.ask_confirmation(title=self.i18n['warning'].capitalize(),
|
||||
body="<p>{}</p><p>{}</p>".format(self.i18n['settings.changed.success.warning'],
|
||||
self.i18n['settings.changed.success.reboot']),
|
||||
i18n=self.i18n):
|
||||
elif ConfirmationDialog(title=self.i18n['warning'].capitalize(),
|
||||
body="<p>{}</p><p>{}</p>".format(self.i18n['settings.changed.success.warning'],
|
||||
self.i18n['settings.changed.success.reboot']),
|
||||
i18n=self.i18n).ask():
|
||||
self.close()
|
||||
util.restart_app()
|
||||
else:
|
||||
@@ -109,4 +117,4 @@ class SettingsWindow(QWidget):
|
||||
msg.write('<p style="font-weight: bold">* ' + w + '</p><br/>')
|
||||
|
||||
msg.seek(0)
|
||||
dialog.show_message(title="Warning", body=msg.read(), type_=MessageType.WARNING)
|
||||
dialog.show_message(title=self.i18n['warning'].capitalize(), body=msg.read(), type_=MessageType.WARNING)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from bauh.view.qt.colors import GREEN
|
||||
|
||||
PROGRESS_BAR = """QProgressBar::chunk { background-color: %s; }""" % GREEN
|
||||
@@ -5,11 +5,12 @@ import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import List, Type, Set, Tuple
|
||||
from typing import List, Type, Set, Tuple, Optional
|
||||
|
||||
import requests
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QWidget
|
||||
|
||||
from bauh import LOGS_PATH
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
@@ -22,7 +23,7 @@ from bauh.api.exception import NoInternetException
|
||||
from bauh.commons import user
|
||||
from bauh.commons.html import bold
|
||||
from bauh.commons.system import get_human_size_str, ProcessHandler, SimpleProcess
|
||||
from bauh.view.core import timeshift
|
||||
from bauh.view.core import timeshift, config
|
||||
from bauh.view.core.config import read_config
|
||||
from bauh.view.qt import commons
|
||||
from bauh.view.qt.view_model import PackageView, PackageViewStatus
|
||||
@@ -43,7 +44,7 @@ class AsyncAction(QThread, ProcessWatcher):
|
||||
signal_root_password = pyqtSignal()
|
||||
signal_progress_control = pyqtSignal(bool)
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, root_password: Optional[Tuple[bool, str]] = None):
|
||||
super(AsyncAction, self).__init__()
|
||||
self.wait_confirmation = False
|
||||
self.confirmation_res = None
|
||||
@@ -62,7 +63,7 @@ class AsyncAction(QThread, ProcessWatcher):
|
||||
self.wait_user()
|
||||
return self.confirmation_res
|
||||
|
||||
def request_root_password(self) -> Tuple[str, bool]:
|
||||
def request_root_password(self) -> Tuple[bool, Optional[str]]:
|
||||
self.wait_confirmation = True
|
||||
self.signal_root_password.emit()
|
||||
self.wait_user()
|
||||
@@ -74,8 +75,8 @@ class AsyncAction(QThread, ProcessWatcher):
|
||||
self.confirmation_res = res
|
||||
self.wait_confirmation = False
|
||||
|
||||
def set_root_password(self, password: str, valid: bool):
|
||||
self.root_password = (password, valid)
|
||||
def set_root_password(self, valid: bool, password: str):
|
||||
self.root_password = (valid, password)
|
||||
self.wait_confirmation = False
|
||||
|
||||
def wait_user(self):
|
||||
@@ -286,9 +287,9 @@ class UpgradeSelected(AsyncAction):
|
||||
|
||||
return FormComponent(label=lb, components=comps), (required_size, extra_size)
|
||||
|
||||
def _request_password(self) -> Tuple[bool, str]:
|
||||
def _request_password(self) -> Tuple[bool, Optional[str]]:
|
||||
if not user.is_root():
|
||||
pwd, success = self.request_root_password()
|
||||
success, pwd = self.request_root_password()
|
||||
|
||||
if not success:
|
||||
return False, None
|
||||
@@ -1006,3 +1007,20 @@ class IgnorePackageUpdates(AsyncAction):
|
||||
|
||||
finally:
|
||||
self.pkg = None
|
||||
|
||||
|
||||
class SaveTheme(QThread):
|
||||
|
||||
def __init__(self, theme_key: Optional[str], parent: Optional[QWidget] = None):
|
||||
super(SaveTheme, self).__init__(parent=parent)
|
||||
self.theme_key = theme_key
|
||||
|
||||
def run(self):
|
||||
if self.theme_key:
|
||||
core_config = read_config()
|
||||
core_config['ui']['theme'] = self.theme_key
|
||||
|
||||
try:
|
||||
config.save(core_config)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import logging
|
||||
import logging
|
||||
import operator
|
||||
import time
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import List, Type, Set, Tuple
|
||||
from typing import List, Type, Set, Tuple, Optional
|
||||
|
||||
from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
|
||||
from PyQt5.QtCore import QEvent, Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QCursor
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
|
||||
QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QApplication, QListView, QSizePolicy, \
|
||||
QMenu, QAction
|
||||
QLabel, QPlainTextEdit, QProgressBar, QPushButton, QComboBox, QApplication, QListView, QSizePolicy, \
|
||||
QMenu, QHBoxLayout
|
||||
|
||||
from bauh import LOGS_PATH
|
||||
from bauh.api.abstract.cache import MemoryCache
|
||||
@@ -21,16 +20,20 @@ from bauh.api.abstract.view import MessageType
|
||||
from bauh.api.http import HttpClient
|
||||
from bauh.commons import user
|
||||
from bauh.commons.html import bold
|
||||
from bauh.context import set_theme
|
||||
from bauh.stylesheet import read_all_themes_metadata, ThemeMetadata
|
||||
from bauh.view.core.config import read_config
|
||||
from bauh.view.core.tray_client import notify_tray
|
||||
from bauh.view.qt import dialog, commons, qt_utils, root, styles
|
||||
from bauh.view.qt import dialog, commons, qt_utils
|
||||
from bauh.view.qt.about import AboutDialog
|
||||
from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton
|
||||
from bauh.view.qt.colors import GREEN
|
||||
from bauh.view.qt.components import new_spacer, InputFilter, IconButton, QtComponentsManager
|
||||
from bauh.view.qt.confirmation import ConfirmationDialog
|
||||
from bauh.view.qt.apps_table import PackagesTable, UpgradeToggleButton
|
||||
from bauh.view.qt.commons import sum_updates_displayed
|
||||
from bauh.view.qt.components import new_spacer, IconButton, QtComponentsManager, to_widget, QSearchBar, \
|
||||
QCustomMenuAction
|
||||
from bauh.view.qt.dialog import ConfirmationDialog
|
||||
from bauh.view.qt.history import HistoryDialog
|
||||
from bauh.view.qt.info import InfoDialog
|
||||
from bauh.view.qt.root import ask_root_password
|
||||
from bauh.view.qt.root import RootDialog
|
||||
from bauh.view.qt.screenshots import ScreenshotsDialog
|
||||
from bauh.view.qt.settings import SettingsWindow
|
||||
from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallPackage, DowngradePackage, ShowPackageInfo, \
|
||||
@@ -38,7 +41,7 @@ from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallPackage,
|
||||
ListWarnings, \
|
||||
AsyncAction, LaunchPackage, ApplyFilters, CustomSoftwareAction, ShowScreenshots, CustomAction, \
|
||||
NotifyInstalledLoaded, \
|
||||
IgnorePackageUpdates
|
||||
IgnorePackageUpdates, SaveTheme
|
||||
from bauh.view.qt.view_model import PackageView, PackageViewStatus
|
||||
from bauh.view.util import util, resource
|
||||
from bauh.view.util.translation import I18n
|
||||
@@ -46,19 +49,6 @@ from bauh.view.util.translation import I18n
|
||||
DARK_ORANGE = '#FF4500'
|
||||
|
||||
|
||||
def toolbar_button_style(bg: str = None, color: str = None):
|
||||
style = 'QPushButton { font-weight: 500;'
|
||||
|
||||
if bg:
|
||||
style += 'background: {};'.format(bg)
|
||||
|
||||
if color:
|
||||
style += 'color: {};'.format(color)
|
||||
|
||||
style += ' }'
|
||||
return style
|
||||
|
||||
|
||||
# action ids
|
||||
ACTION_APPLY_FILTERS = 1
|
||||
ACTION_SEARCH = 2
|
||||
@@ -84,10 +74,11 @@ CHECK_APPS = 7
|
||||
COMBO_TYPES = 8
|
||||
COMBO_CATEGORIES = 9
|
||||
INP_NAME = 10
|
||||
CHECK_CONSOLE = 11
|
||||
CHECK_DETAILS = 11
|
||||
BT_SETTINGS = 12
|
||||
BT_CUSTOM_ACTIONS = 13
|
||||
BT_ABOUT = 14
|
||||
BT_THEMES = 15
|
||||
|
||||
# component groups ids
|
||||
GROUP_FILTERS = 1
|
||||
@@ -99,13 +90,14 @@ GROUP_LOWER_BTS = 5
|
||||
|
||||
class ManageWindow(QWidget):
|
||||
signal_user_res = pyqtSignal(bool)
|
||||
signal_root_password = pyqtSignal(str, bool)
|
||||
signal_root_password = pyqtSignal(bool, str)
|
||||
signal_table_update = pyqtSignal()
|
||||
signal_stop_notifying = pyqtSignal()
|
||||
|
||||
def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager, screen_size, config: dict,
|
||||
context: ApplicationContext, http_client: HttpClient, logger: logging.Logger, icon: QIcon):
|
||||
super(ManageWindow, self).__init__()
|
||||
self.setObjectName('manage_window')
|
||||
self.comp_manager = QtComponentsManager()
|
||||
self.i18n = i18n
|
||||
self.logger = logger
|
||||
@@ -127,95 +119,56 @@ class ManageWindow(QWidget):
|
||||
self.layout = QVBoxLayout()
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.toolbar_top = QToolBar()
|
||||
self.toolbar_top.addWidget(new_spacer())
|
||||
self.toolbar_status = QToolBar()
|
||||
self.toolbar_status.setObjectName('toolbar_status')
|
||||
self.toolbar_status.addWidget(new_spacer())
|
||||
|
||||
self.label_status = QLabel()
|
||||
self.label_status.setObjectName('label_status')
|
||||
self.label_status.setText('')
|
||||
self.label_status.setStyleSheet("font-weight: bold")
|
||||
self.toolbar_top.addWidget(self.label_status)
|
||||
self.toolbar_status.addWidget(self.label_status)
|
||||
|
||||
self.search_bar = QToolBar()
|
||||
self.search_bar.setStyleSheet("spacing: 0px;")
|
||||
self.search_bar.setContentsMargins(0, 0, 0, 0)
|
||||
self.search_bar = QSearchBar(search_callback=self.search)
|
||||
self.search_bar.set_placeholder(i18n['window_manage.search_bar.placeholder'] + "...")
|
||||
self.search_bar.set_tooltip(i18n['window_manage.search_bar.tooltip'])
|
||||
self.search_bar.set_button_tooltip(i18n['window_manage.search_bar.button_tooltip'])
|
||||
self.comp_manager.register_component(SEARCH_BAR, self.search_bar, self.toolbar_status.addWidget(self.search_bar))
|
||||
|
||||
self.inp_search = QLineEdit()
|
||||
self.inp_search.setFrame(False)
|
||||
self.inp_search.setPlaceholderText(self.i18n['window_manage.input_search.placeholder'] + "...")
|
||||
self.inp_search.setToolTip(self.i18n['window_manage.input_search.tooltip'])
|
||||
self.inp_search.setStyleSheet("""QLineEdit {
|
||||
color: grey;
|
||||
spacing: 0;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
width: 300px;
|
||||
border-bottom: 1px solid lightgrey;
|
||||
border-top: 1px solid lightgrey;
|
||||
}
|
||||
""")
|
||||
self.inp_search.returnPressed.connect(self.search)
|
||||
search_background_color = self.inp_search.palette().color(self.inp_search.backgroundRole()).name()
|
||||
self.toolbar_status.addWidget(new_spacer())
|
||||
self.layout.addWidget(self.toolbar_status)
|
||||
|
||||
label_pre_search = QLabel()
|
||||
label_pre_search.setStyleSheet("""
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-left: 1px solid lightgrey;
|
||||
border-top: 1px solid lightgrey;
|
||||
border-bottom: 1px solid lightgrey;
|
||||
background: %s;
|
||||
""" % search_background_color)
|
||||
|
||||
self.search_bar.addWidget(label_pre_search)
|
||||
|
||||
self.search_bar.addWidget(self.inp_search)
|
||||
|
||||
label_pos_search = QLabel()
|
||||
label_pos_search.setPixmap(QIcon(resource.get_path('img/search.svg')).pixmap(QSize(10, 10)))
|
||||
label_pos_search.setStyleSheet("""
|
||||
padding-right: 10px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
border-right: 1px solid lightgrey;
|
||||
border-top: 1px solid lightgrey;
|
||||
border-bottom: 1px solid lightgrey;
|
||||
background: %s;
|
||||
""" % search_background_color)
|
||||
|
||||
self.search_bar.addWidget(label_pos_search)
|
||||
|
||||
self.comp_manager.register_component(SEARCH_BAR, self.search_bar, self.toolbar_top.addWidget(self.search_bar))
|
||||
|
||||
self.toolbar_top.addWidget(new_spacer())
|
||||
self.layout.addWidget(self.toolbar_top)
|
||||
|
||||
self.toolbar = QToolBar()
|
||||
self.toolbar.setStyleSheet('QToolBar {spacing: 4px; margin-top: 15px;}')
|
||||
self.toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
self.toolbar.setContentsMargins(0, 0, 0, 0)
|
||||
self.toolbar_filters = QWidget()
|
||||
self.toolbar_filters.setObjectName('table_filters')
|
||||
self.toolbar_filters.setLayout(QHBoxLayout())
|
||||
self.toolbar_filters.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
self.toolbar_filters.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.check_updates = QCheckBox()
|
||||
self.check_updates.setObjectName('check_updates')
|
||||
self.check_updates.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.check_updates.setText(self.i18n['updates'].capitalize())
|
||||
self.check_updates.stateChanged.connect(self._handle_updates_filter)
|
||||
self.check_updates.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
self.comp_manager.register_component(CHECK_UPDATES, self.check_updates, self.toolbar.addWidget(self.check_updates))
|
||||
self.toolbar_filters.layout().addWidget(self.check_updates)
|
||||
self.comp_manager.register_component(CHECK_UPDATES, self.check_updates)
|
||||
|
||||
self.check_apps = QCheckBox()
|
||||
self.check_apps.setObjectName('check_apps')
|
||||
self.check_apps.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.check_apps.setText(self.i18n['manage_window.checkbox.only_apps'])
|
||||
self.check_apps.setChecked(True)
|
||||
self.check_apps.stateChanged.connect(self._handle_filter_only_apps)
|
||||
self.check_apps.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
self.comp_manager.register_component(CHECK_APPS, self.check_apps, self.toolbar.addWidget(self.check_apps))
|
||||
self.toolbar_filters.layout().addWidget(self.check_apps)
|
||||
self.comp_manager.register_component(CHECK_APPS, self.check_apps)
|
||||
|
||||
self.any_type_filter = 'any'
|
||||
self.cache_type_filter_icons = {}
|
||||
self.combo_filter_type = QComboBox()
|
||||
self.combo_filter_type.setObjectName('combo_types')
|
||||
self.combo_filter_type.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.combo_filter_type.setView(QListView())
|
||||
self.combo_filter_type.setStyleSheet('QLineEdit { height: 2px; }')
|
||||
self.combo_filter_type.setIconSize(QSize(14, 14))
|
||||
self.combo_filter_type.view().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.combo_filter_type.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||
self.combo_filter_type.setEditable(True)
|
||||
self.combo_filter_type.lineEdit().setReadOnly(True)
|
||||
@@ -223,78 +176,73 @@ class ManageWindow(QWidget):
|
||||
self.combo_filter_type.activated.connect(self._handle_type_filter)
|
||||
self.combo_filter_type.addItem('--- {} ---'.format(self.i18n['type'].capitalize()), self.any_type_filter)
|
||||
self.combo_filter_type.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
self.comp_manager.register_component(COMBO_TYPES, self.combo_filter_type, self.toolbar.addWidget(self.combo_filter_type))
|
||||
self.toolbar_filters.layout().addWidget(self.combo_filter_type)
|
||||
self.comp_manager.register_component(COMBO_TYPES, self.combo_filter_type)
|
||||
|
||||
self.any_category_filter = 'any'
|
||||
self.combo_categories = QComboBox()
|
||||
self.combo_categories.setObjectName('combo_categories')
|
||||
self.combo_categories.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.combo_categories.setStyleSheet('QLineEdit { height: 2px; }')
|
||||
self.combo_categories.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||
self.combo_categories.view().setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.combo_categories.setEditable(True)
|
||||
self.combo_categories.lineEdit().setReadOnly(True)
|
||||
self.combo_categories.lineEdit().setAlignment(Qt.AlignCenter)
|
||||
self.combo_categories.activated.connect(self._handle_category_filter)
|
||||
self.combo_categories.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
self.combo_categories.addItem('--- {} ---'.format(self.i18n['category'].capitalize()), self.any_category_filter)
|
||||
self.comp_manager.register_component(COMBO_CATEGORIES, self.combo_categories, self.toolbar.addWidget(self.combo_categories))
|
||||
self.toolbar_filters.layout().addWidget(self.combo_categories)
|
||||
self.comp_manager.register_component(COMBO_CATEGORIES, self.combo_categories)
|
||||
|
||||
self.input_name = InputFilter(self.begin_apply_filters)
|
||||
self.input_name.setPlaceholderText(self.i18n['manage_window.name_filter.placeholder'] + '...')
|
||||
self.input_name.setToolTip(self.i18n['manage_window.name_filter.tooltip'])
|
||||
self.input_name.setStyleSheet("QLineEdit { color: grey; }")
|
||||
self.input_name.setFixedWidth(130)
|
||||
self.input_name = QSearchBar(search_callback=self.begin_apply_filters)
|
||||
self.input_name.palette().swap(self.combo_categories.palette())
|
||||
self.input_name.setObjectName('name_filter')
|
||||
self.input_name.set_placeholder(self.i18n['manage_window.name_filter.placeholder'] + '...')
|
||||
self.input_name.set_tooltip(self.i18n['manage_window.name_filter.tooltip'])
|
||||
self.input_name.set_button_tooltip(self.i18n['manage_window.name_filter.button_tooltip'])
|
||||
self.input_name.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
self.comp_manager.register_component(INP_NAME, self.input_name, self.toolbar.addWidget(self.input_name))
|
||||
self.toolbar_filters.layout().addWidget(self.input_name)
|
||||
self.comp_manager.register_component(INP_NAME, self.input_name)
|
||||
|
||||
self.toolbar.addWidget(new_spacer())
|
||||
self.toolbar_filters.layout().addWidget(new_spacer())
|
||||
|
||||
toolbar_bts = []
|
||||
|
||||
if config['suggestions']['enabled']:
|
||||
bt_sugs = QPushButton()
|
||||
bt_sugs.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_sugs.setToolTip(self.i18n['manage_window.bt.suggestions.tooltip'])
|
||||
bt_sugs.setText(self.i18n['manage_window.bt.suggestions.text'].capitalize())
|
||||
bt_sugs.setIcon(QIcon(resource.get_path('img/suggestions.svg')))
|
||||
bt_sugs.setStyleSheet(toolbar_button_style())
|
||||
bt_sugs.clicked.connect(lambda: self._begin_load_suggestions(filter_installed=True))
|
||||
bt_sugs.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
ref_bt_sugs = self.toolbar.addWidget(bt_sugs)
|
||||
toolbar_bts.append(bt_sugs)
|
||||
self.comp_manager.register_component(BT_SUGGESTIONS, bt_sugs, ref_bt_sugs)
|
||||
|
||||
bt_inst = QPushButton()
|
||||
bt_inst.setObjectName('bt_installed')
|
||||
bt_inst.setProperty('root', 'true')
|
||||
bt_inst.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_inst.setToolTip(self.i18n['manage_window.bt.installed.tooltip'])
|
||||
bt_inst.setIcon(QIcon(resource.get_path('img/disk.svg')))
|
||||
bt_inst.setText(self.i18n['manage_window.bt.installed.text'].capitalize())
|
||||
bt_inst.clicked.connect(self._begin_loading_installed)
|
||||
bt_inst.setStyleSheet(toolbar_button_style())
|
||||
bt_inst.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
toolbar_bts.append(bt_inst)
|
||||
self.comp_manager.register_component(BT_INSTALLED, bt_inst, self.toolbar.addWidget(bt_inst))
|
||||
self.toolbar_filters.layout().addWidget(bt_inst)
|
||||
self.comp_manager.register_component(BT_INSTALLED, bt_inst)
|
||||
|
||||
bt_ref = QPushButton()
|
||||
bt_ref.setObjectName('bt_refresh')
|
||||
bt_ref.setProperty('root', 'true')
|
||||
bt_ref.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt_ref.setToolTip(i18n['manage_window.bt.refresh.tooltip'])
|
||||
bt_ref.setIcon(QIcon(resource.get_path('img/refresh.svg')))
|
||||
bt_ref.setText(self.i18n['manage_window.bt.refresh.text'])
|
||||
bt_ref.setStyleSheet(toolbar_button_style())
|
||||
bt_ref.clicked.connect(self.begin_refresh_packages)
|
||||
bt_ref.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
toolbar_bts.append(bt_ref)
|
||||
self.comp_manager.register_component(BT_REFRESH, bt_ref, self.toolbar.addWidget(bt_ref))
|
||||
self.toolbar_filters.layout().addWidget(bt_ref)
|
||||
self.comp_manager.register_component(BT_REFRESH, bt_ref)
|
||||
|
||||
self.bt_upgrade = QPushButton()
|
||||
self.bt_upgrade.setProperty('root', 'true')
|
||||
self.bt_upgrade.setObjectName('bt_upgrade')
|
||||
self.bt_upgrade.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_upgrade.setToolTip(i18n['manage_window.bt.upgrade.tooltip'])
|
||||
self.bt_upgrade.setIcon(QIcon(resource.get_path('img/app_update.svg')))
|
||||
self.bt_upgrade.setText(i18n['manage_window.bt.upgrade.text'])
|
||||
self.bt_upgrade.setStyleSheet(toolbar_button_style(GREEN, 'white'))
|
||||
self.bt_upgrade.clicked.connect(self.upgrade_selected)
|
||||
self.bt_upgrade.sizePolicy().setRetainSizeWhenHidden(True)
|
||||
toolbar_bts.append(self.bt_upgrade)
|
||||
self.comp_manager.register_component(BT_UPGRADE, self.bt_upgrade, self.toolbar.addWidget(self.bt_upgrade))
|
||||
self.toolbar_filters.layout().addWidget(self.bt_upgrade)
|
||||
self.comp_manager.register_component(BT_UPGRADE, self.bt_upgrade)
|
||||
|
||||
# setting all buttons to the same size:
|
||||
bt_biggest_size = 0
|
||||
@@ -308,44 +256,59 @@ class ManageWindow(QWidget):
|
||||
if bt_biggest_size > bt_width:
|
||||
bt.setFixedWidth(bt_biggest_size)
|
||||
|
||||
self.layout.addWidget(self.toolbar)
|
||||
self.layout.addWidget(self.toolbar_filters)
|
||||
|
||||
self.table_container = QWidget()
|
||||
self.table_container.setObjectName('table_container')
|
||||
self.table_container.setContentsMargins(0, 0, 0, 0)
|
||||
self.table_container.setLayout(QVBoxLayout())
|
||||
self.table_container.layout().setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.table_apps = AppsTable(self, self.icon_cache, download_icons=bool(self.config['download']['icons']))
|
||||
self.table_apps = PackagesTable(self, self.icon_cache, download_icons=bool(self.config['download']['icons']))
|
||||
self.table_apps.change_headers_policy()
|
||||
self.table_container.layout().addWidget(self.table_apps)
|
||||
|
||||
self.layout.addWidget(self.table_container)
|
||||
|
||||
toolbar_console = QToolBar()
|
||||
self.toolbar_console = QWidget()
|
||||
self.toolbar_console.setObjectName('console_toolbar')
|
||||
self.toolbar_console.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
self.toolbar_console.setLayout(QHBoxLayout())
|
||||
self.toolbar_console.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.check_console = QCheckBox()
|
||||
self.check_console.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.check_console.setText(self.i18n['manage_window.checkbox.show_details'])
|
||||
self.check_console.stateChanged.connect(self._handle_console)
|
||||
self.comp_manager.register_component(CHECK_CONSOLE, self.check_console, toolbar_console.addWidget(self.check_console))
|
||||
self.check_details = QCheckBox()
|
||||
self.check_details.setObjectName('check_details')
|
||||
self.check_details.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.check_details.setText(self.i18n['manage_window.checkbox.show_details'])
|
||||
self.check_details.stateChanged.connect(self._handle_console)
|
||||
self.toolbar_console.layout().addWidget(self.check_details)
|
||||
self.comp_manager.register_component(CHECK_DETAILS, self.check_details)
|
||||
|
||||
toolbar_console.addWidget(new_spacer())
|
||||
self.toolbar_console.layout().addWidget(new_spacer())
|
||||
|
||||
self.label_displayed = QLabel()
|
||||
toolbar_console.addWidget(self.label_displayed)
|
||||
self.label_displayed.setObjectName('apps_displayed')
|
||||
self.label_displayed.setCursor(QCursor(Qt.WhatsThisCursor))
|
||||
self.label_displayed.setToolTip(self.i18n['manage_window.label.apps_displayed.tip'])
|
||||
self.toolbar_console.layout().addWidget(self.label_displayed)
|
||||
self.label_displayed.hide()
|
||||
|
||||
self.layout.addWidget(toolbar_console)
|
||||
self.layout.addWidget(self.toolbar_console)
|
||||
|
||||
self.textarea_output = QPlainTextEdit(self)
|
||||
self.textarea_output.resize(self.table_apps.size())
|
||||
self.textarea_output.setStyleSheet("background: black; color: white;")
|
||||
self.layout.addWidget(self.textarea_output)
|
||||
self.textarea_output.setVisible(False)
|
||||
self.textarea_output.setReadOnly(True)
|
||||
self.textarea_details = QPlainTextEdit(self)
|
||||
self.textarea_details.setObjectName('textarea_details')
|
||||
self.textarea_details.setProperty('console', 'true')
|
||||
self.textarea_details.resize(self.table_apps.size())
|
||||
self.layout.addWidget(self.textarea_details)
|
||||
self.textarea_details.setVisible(False)
|
||||
self.textarea_details.setReadOnly(True)
|
||||
|
||||
self.toolbar_substatus = QToolBar()
|
||||
self.toolbar_substatus.setObjectName('toolbar_substatus')
|
||||
self.toolbar_substatus.addWidget(new_spacer())
|
||||
|
||||
self.label_substatus = QLabel()
|
||||
self.label_substatus.setObjectName('label_substatus')
|
||||
self.label_substatus.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.toolbar_substatus.addWidget(self.label_substatus)
|
||||
self.toolbar_substatus.addWidget(new_spacer())
|
||||
@@ -383,43 +346,65 @@ class ManageWindow(QWidget):
|
||||
self.thread_ignore_updates = IgnorePackageUpdates(manager=self.manager)
|
||||
self._bind_async_action(self.thread_ignore_updates, finished_call=self.finish_ignore_updates)
|
||||
|
||||
self.toolbar_bottom = QToolBar()
|
||||
self.toolbar_bottom.setIconSize(QSize(16, 16))
|
||||
self.toolbar_bottom.setStyleSheet('QToolBar { spacing: 3px }')
|
||||
self.container_bottom = QWidget()
|
||||
self.container_bottom.setObjectName('container_bottom')
|
||||
self.container_bottom.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
self.container_bottom.setLayout(QHBoxLayout())
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self.container_bottom.layout().setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.toolbar_bottom.addWidget(new_spacer())
|
||||
self.container_bottom.layout().addWidget(new_spacer())
|
||||
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setObjectName('progress_manage')
|
||||
self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
|
||||
self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
|
||||
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
|
||||
|
||||
self.progress_bar.setTextVisible(False)
|
||||
self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)
|
||||
self.container_bottom.layout().addWidget(self.progress_bar)
|
||||
|
||||
self.toolbar_bottom.addWidget(new_spacer())
|
||||
self.container_bottom.layout().addWidget(new_spacer())
|
||||
|
||||
if config['suggestions']['enabled']:
|
||||
bt_sugs = IconButton(action=lambda: self._begin_load_suggestions(filter_installed=True),
|
||||
i18n=i18n,
|
||||
tooltip=self.i18n['manage_window.bt.suggestions.tooltip'])
|
||||
bt_sugs.setObjectName('suggestions')
|
||||
self.container_bottom.layout().addWidget(bt_sugs)
|
||||
self.comp_manager.register_component(BT_SUGGESTIONS, bt_sugs)
|
||||
|
||||
bt_themes = IconButton(self.show_themes,
|
||||
i18n=self.i18n,
|
||||
tooltip=self.i18n['manage_window.bt_themes.tip'])
|
||||
bt_themes.setObjectName('themes')
|
||||
self.container_bottom.layout().addWidget(bt_themes)
|
||||
self.comp_manager.register_component(BT_THEMES, bt_themes)
|
||||
|
||||
self.custom_actions = manager.get_custom_actions()
|
||||
bt_custom_actions = IconButton(QIcon(resource.get_path('img/custom_actions.svg')),
|
||||
action=self.show_custom_actions,
|
||||
bt_custom_actions = IconButton(action=self.show_custom_actions,
|
||||
i18n=self.i18n,
|
||||
tooltip=self.i18n['manage_window.bt_custom_actions.tip'])
|
||||
bt_custom_actions.setVisible(bool(self.custom_actions))
|
||||
self.comp_manager.register_component(BT_CUSTOM_ACTIONS, bt_custom_actions, self.toolbar_bottom.addWidget(bt_custom_actions))
|
||||
bt_custom_actions.setObjectName('custom_actions')
|
||||
|
||||
bt_settings = IconButton(QIcon(resource.get_path('img/settings.svg')),
|
||||
action=self.show_settings,
|
||||
bt_custom_actions.setVisible(bool(self.custom_actions))
|
||||
self.container_bottom.layout().addWidget(bt_custom_actions)
|
||||
self.comp_manager.register_component(BT_CUSTOM_ACTIONS, bt_custom_actions)
|
||||
|
||||
bt_settings = IconButton(action=self.show_settings,
|
||||
i18n=self.i18n,
|
||||
tooltip=self.i18n['manage_window.bt_settings.tooltip'])
|
||||
self.comp_manager.register_component(BT_SETTINGS, bt_settings, self.toolbar_bottom.addWidget(bt_settings))
|
||||
bt_settings.setObjectName('settings')
|
||||
self.container_bottom.layout().addWidget(bt_settings)
|
||||
self.comp_manager.register_component(BT_SETTINGS, bt_settings)
|
||||
|
||||
bt_about = IconButton(QIcon(resource.get_path('img/info.svg')),
|
||||
action=self._show_about,
|
||||
bt_about = IconButton(action=self._show_about,
|
||||
i18n=self.i18n,
|
||||
tooltip=self.i18n['manage_window.settings.about'])
|
||||
self.comp_manager.register_component(BT_ABOUT, bt_about, self.toolbar_bottom.addWidget(bt_about))
|
||||
bt_about.setObjectName('about')
|
||||
self.container_bottom.layout().addWidget(bt_about)
|
||||
self.comp_manager.register_component(BT_ABOUT, bt_about)
|
||||
|
||||
self.layout.addWidget(self.toolbar_bottom)
|
||||
self.layout.addWidget(self.container_bottom)
|
||||
|
||||
qt_utils.centralize(self)
|
||||
|
||||
@@ -442,6 +427,8 @@ class ManageWindow(QWidget):
|
||||
self.settings_window = None
|
||||
self.search_performed = False
|
||||
|
||||
self.thread_save_theme = SaveTheme(theme_key='')
|
||||
|
||||
self.thread_load_installed = NotifyInstalledLoaded()
|
||||
self.thread_load_installed.signal_loaded.connect(self._finish_loading_installed)
|
||||
self.setMinimumHeight(int(screen_size.height() * 0.5))
|
||||
@@ -457,14 +444,14 @@ class ManageWindow(QWidget):
|
||||
BT_INSTALLED, BT_SUGGESTIONS) # buttons
|
||||
|
||||
self.comp_manager.register_group(GROUP_VIEW_INSTALLED, False,
|
||||
BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE, # buttons
|
||||
BT_REFRESH, BT_UPGRADE, # buttons
|
||||
*filters)
|
||||
|
||||
self.comp_manager.register_group(GROUP_UPPER_BAR, False,
|
||||
CHECK_APPS, CHECK_UPDATES, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME,
|
||||
BT_INSTALLED, BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE)
|
||||
|
||||
self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_ABOUT, BT_SETTINGS, BT_CUSTOM_ACTIONS)
|
||||
self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_SUGGESTIONS, BT_THEMES, BT_CUSTOM_ACTIONS, BT_SETTINGS, BT_ABOUT)
|
||||
|
||||
def update_custom_actions(self):
|
||||
self.custom_actions = self.manager.get_custom_actions()
|
||||
@@ -540,26 +527,27 @@ class ManageWindow(QWidget):
|
||||
|
||||
def _ask_confirmation(self, msg: dict):
|
||||
self.thread_animate_progress.pause()
|
||||
extra_widgets = [to_widget(comp=c, i18n=self.i18n) for c in msg['components']] if msg.get('components') else None
|
||||
diag = ConfirmationDialog(title=msg['title'],
|
||||
body=msg['body'],
|
||||
i18n=self.i18n,
|
||||
components=msg['components'],
|
||||
widgets=extra_widgets,
|
||||
confirmation_label=msg['confirmation_label'],
|
||||
deny_label=msg['deny_label'],
|
||||
deny_button=msg['deny_button'],
|
||||
window_cancel=msg['window_cancel'],
|
||||
confirmation_button=msg.get('confirmation_button', True),
|
||||
screen_size=self.screen_size)
|
||||
res = diag.is_confirmed()
|
||||
confirmation_button=msg.get('confirmation_button', True))
|
||||
diag.ask()
|
||||
res = diag.confirmed
|
||||
self.thread_animate_progress.animate()
|
||||
self.signal_user_res.emit(res)
|
||||
|
||||
def _pause_and_ask_root_password(self):
|
||||
self.thread_animate_progress.pause()
|
||||
password, valid = root.ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
valid, password = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
|
||||
self.thread_animate_progress.animate()
|
||||
self.signal_root_password.emit(password, valid)
|
||||
self.signal_root_password.emit(valid, password)
|
||||
|
||||
def _show_message(self, msg: dict):
|
||||
self.thread_animate_progress.pause()
|
||||
@@ -582,8 +570,8 @@ class ManageWindow(QWidget):
|
||||
self.thread_warnings.start()
|
||||
|
||||
def _begin_loading_installed(self):
|
||||
self.inp_search.setText('')
|
||||
self.input_name.setText('')
|
||||
self.search_bar.clear()
|
||||
self.input_name.set_text('')
|
||||
self._begin_action(self.i18n['manage_window.status.installed'])
|
||||
self._handle_console_option(False)
|
||||
self.comp_manager.set_components_visible(False)
|
||||
@@ -666,20 +654,20 @@ class ManageWindow(QWidget):
|
||||
|
||||
def _handle_console(self, checked: bool):
|
||||
if checked:
|
||||
self.textarea_output.show()
|
||||
self.textarea_details.show()
|
||||
else:
|
||||
self.textarea_output.hide()
|
||||
self.textarea_details.hide()
|
||||
|
||||
def _handle_console_option(self, enable: bool):
|
||||
if enable:
|
||||
self.textarea_output.clear()
|
||||
self.textarea_details.clear()
|
||||
|
||||
self.comp_manager.set_component_visible(CHECK_CONSOLE, enable)
|
||||
self.check_console.setChecked(False)
|
||||
self.textarea_output.hide()
|
||||
self.comp_manager.set_component_visible(CHECK_DETAILS, enable)
|
||||
self.check_details.setChecked(False)
|
||||
self.textarea_details.hide()
|
||||
|
||||
def begin_refresh_packages(self, pkg_types: Set[Type[SoftwarePackage]] = None):
|
||||
self.inp_search.clear()
|
||||
self.search_bar.clear()
|
||||
|
||||
self._begin_action(self.i18n['manage_window.status.refreshing'])
|
||||
self.comp_manager.set_components_visible(False)
|
||||
@@ -710,7 +698,7 @@ class ManageWindow(QWidget):
|
||||
self.types_changed = False
|
||||
|
||||
def _begin_load_suggestions(self, filter_installed: bool):
|
||||
self.inp_search.clear()
|
||||
self.search_bar.clear()
|
||||
self._begin_action(self.i18n['manage_window.status.suggestions'])
|
||||
self._handle_console_option(False)
|
||||
self.comp_manager.set_components_visible(False)
|
||||
@@ -825,15 +813,21 @@ class ManageWindow(QWidget):
|
||||
def _update_table(self, pkgs_info: dict, signal: bool = False):
|
||||
self.pkgs = pkgs_info['pkgs_displayed']
|
||||
|
||||
self.table_apps.update_packages(self.pkgs, update_check_enabled=pkgs_info['not_installed'] == 0)
|
||||
if pkgs_info['not_installed'] == 0:
|
||||
update_check = sum_updates_displayed(pkgs_info) > 0
|
||||
else:
|
||||
update_check = False
|
||||
|
||||
self.table_apps.update_packages(self.pkgs, update_check_enabled=update_check)
|
||||
|
||||
if not self._maximized:
|
||||
self.label_displayed.show()
|
||||
self.table_apps.change_headers_policy(QHeaderView.Stretch)
|
||||
self.table_apps.change_headers_policy()
|
||||
self._resize(accept_lower_width=len(self.pkgs) > 0)
|
||||
self.label_displayed.setText('{} / {}'.format(len(self.pkgs), len(self.pkgs_available)))
|
||||
else:
|
||||
self.label_displayed.setText('')
|
||||
self.label_displayed.hide()
|
||||
|
||||
if signal:
|
||||
self.signal_table_update.emit()
|
||||
@@ -887,12 +881,12 @@ class ManageWindow(QWidget):
|
||||
'type': self.type_filter,
|
||||
'category': self.category_filter,
|
||||
'updates': False if ignore_updates else self.filter_updates,
|
||||
'name': self.input_name.get_text().lower() if self.input_name.get_text() else None,
|
||||
'name': self.input_name.text().lower() if self.input_name.text() else None,
|
||||
'display_limit': None if self.filter_updates else self.display_limit
|
||||
}
|
||||
|
||||
def update_pkgs(self, new_pkgs: List[SoftwarePackage], as_installed: bool, types: Set[type] = None, ignore_updates: bool = False, keep_filters: bool = False) -> bool:
|
||||
self.input_name.setText('')
|
||||
self.input_name.set_text('')
|
||||
pkgs_info = commons.new_pkgs_info()
|
||||
filters = self._gen_filters(ignore_updates=ignore_updates)
|
||||
|
||||
@@ -1053,8 +1047,8 @@ class ManageWindow(QWidget):
|
||||
|
||||
def _resize(self, accept_lower_width: bool = True):
|
||||
table_width = self.table_apps.get_width()
|
||||
toolbar_width = self.toolbar.sizeHint().width()
|
||||
topbar_width = self.toolbar_top.sizeHint().width()
|
||||
toolbar_width = self.toolbar_filters.sizeHint().width()
|
||||
topbar_width = self.toolbar_status.sizeHint().width()
|
||||
|
||||
new_width = max(table_width, toolbar_width, topbar_width)
|
||||
new_width *= 1.05 # this extra size is not because of the toolbar button, but the table upgrade buttons
|
||||
@@ -1066,13 +1060,14 @@ class ManageWindow(QWidget):
|
||||
self.progress_controll_enabled = enabled
|
||||
|
||||
def upgrade_selected(self):
|
||||
if dialog.ask_confirmation(title=self.i18n['manage_window.upgrade_all.popup.title'],
|
||||
body=self.i18n['manage_window.upgrade_all.popup.body'],
|
||||
i18n=self.i18n,
|
||||
widgets=[UpdateToggleButton(pkg=None,
|
||||
root=self,
|
||||
i18n=self.i18n,
|
||||
clickable=False)]):
|
||||
body = QWidget()
|
||||
body.setLayout(QHBoxLayout())
|
||||
body.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
|
||||
body.layout().addWidget(QLabel(self.i18n['manage_window.upgrade_all.popup.body']))
|
||||
body.layout().addWidget(UpgradeToggleButton(pkg=None, root=self, i18n=self.i18n, clickable=False))
|
||||
if ConfirmationDialog(title=self.i18n['manage_window.upgrade_all.popup.title'],
|
||||
i18n=self.i18n, body=None,
|
||||
widgets=[body]).ask():
|
||||
|
||||
self._begin_action(action_label=self.i18n['manage_window.status.upgrading'],
|
||||
action_id=ACTION_UPGRADE)
|
||||
@@ -1085,7 +1080,7 @@ class ManageWindow(QWidget):
|
||||
self._finish_action()
|
||||
|
||||
if res.get('id'):
|
||||
output = self.textarea_output.toPlainText()
|
||||
output = self.textarea_details.toPlainText()
|
||||
|
||||
if output:
|
||||
try:
|
||||
@@ -1094,8 +1089,8 @@ class ManageWindow(QWidget):
|
||||
with open(logs_path, 'w+') as f:
|
||||
f.write(output)
|
||||
|
||||
self.textarea_output.appendPlainText('\n*Upgrade summary generated at: {}'.format(UpgradeSelected.SUMMARY_FILE.format(res['id'])))
|
||||
self.textarea_output.appendPlainText('*Upgrade logs generated at: {}'.format(logs_path))
|
||||
self.textarea_details.appendPlainText('\n*Upgrade summary generated at: {}'.format(UpgradeSelected.SUMMARY_FILE.format(res['id'])))
|
||||
self.textarea_details.appendPlainText('*Upgrade logs generated at: {}'.format(logs_path))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -1118,19 +1113,19 @@ class ManageWindow(QWidget):
|
||||
self.update_custom_actions()
|
||||
|
||||
def _show_console_errors(self):
|
||||
if self.textarea_output.toPlainText():
|
||||
self.check_console.setChecked(True)
|
||||
if self.textarea_details.toPlainText():
|
||||
self.check_details.setChecked(True)
|
||||
else:
|
||||
self._handle_console_option(False)
|
||||
self.comp_manager.set_component_visible(CHECK_CONSOLE, False)
|
||||
self.comp_manager.set_component_visible(CHECK_DETAILS, False)
|
||||
|
||||
def _update_action_output(self, output: str):
|
||||
self.textarea_output.appendPlainText(output)
|
||||
self.textarea_details.appendPlainText(output)
|
||||
|
||||
def _begin_action(self, action_label: str, action_id: int = None):
|
||||
self.thread_animate_progress.stop = False
|
||||
self.thread_animate_progress.start()
|
||||
self.ref_progress_bar.setVisible(True)
|
||||
self.progress_bar.setVisible(True)
|
||||
|
||||
if action_id is not None:
|
||||
self.comp_manager.save_states(action_id, only_visible=True)
|
||||
@@ -1149,7 +1144,7 @@ class ManageWindow(QWidget):
|
||||
self.thread_animate_progress.stop = True
|
||||
self.thread_animate_progress.wait(msecs=1000)
|
||||
|
||||
self.ref_progress_bar.setVisible(False)
|
||||
self.progress_bar.setVisible(False)
|
||||
self.progress_bar.setValue(0)
|
||||
self.progress_bar.setTextVisible(False)
|
||||
|
||||
@@ -1246,8 +1241,8 @@ class ManageWindow(QWidget):
|
||||
|
||||
if res.get('error'):
|
||||
self._handle_console_option(True)
|
||||
self.textarea_output.appendPlainText(res['error'])
|
||||
self.check_console.setChecked(True)
|
||||
self.textarea_details.appendPlainText(res['error'])
|
||||
self.check_details.setChecked(True)
|
||||
elif not res['history'].history:
|
||||
dialog.show_message(title=self.i18n['action.history.no_history.title'],
|
||||
body=self.i18n['action.history.no_history.body'].format(bold(res['history'].pkg.name)),
|
||||
@@ -1261,7 +1256,7 @@ class ManageWindow(QWidget):
|
||||
self._begin_action('{} {}'.format(self.i18n['manage_window.status.searching'], word if word else ''), action_id=action_id)
|
||||
|
||||
def search(self):
|
||||
word = self.inp_search.text().strip()
|
||||
word = self.search_bar.text().strip()
|
||||
if word:
|
||||
self._handle_console(False)
|
||||
self._begin_search(word, action_id=ACTION_SEARCH)
|
||||
@@ -1284,13 +1279,13 @@ class ManageWindow(QWidget):
|
||||
self.comp_manager.restore_state(ACTION_SEARCH)
|
||||
dialog.show_message(title=self.i18n['warning'].capitalize(), body=self.i18n[res['error']], type_=MessageType.WARNING)
|
||||
|
||||
def _ask_root_password(self, action: str, pkg: PackageView) -> Tuple[str, bool]:
|
||||
def _ask_root_password(self, action: str, pkg: PackageView) -> Tuple[Optional[str], bool]:
|
||||
pwd = None
|
||||
requires_root = self.manager.requires_root(action, pkg.model)
|
||||
|
||||
if not user.is_root() and requires_root:
|
||||
pwd, ok = ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
if not ok:
|
||||
valid, pwd = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
if not valid:
|
||||
return pwd, False
|
||||
|
||||
return pwd, True
|
||||
@@ -1312,7 +1307,7 @@ class ManageWindow(QWidget):
|
||||
def _finish_install(self, res: dict):
|
||||
self._finish_action(action_id=ACTION_INSTALL)
|
||||
|
||||
console_output = self.textarea_output.toPlainText()
|
||||
console_output = self.textarea_details.toPlainText()
|
||||
|
||||
if console_output:
|
||||
log_path = '{}/install/{}/{}'.format(LOGS_PATH, res['pkg'].model.get_type(), res['pkg'].model.name)
|
||||
@@ -1323,9 +1318,9 @@ class ManageWindow(QWidget):
|
||||
with open(log_file, 'w+') as f:
|
||||
f.write(console_output)
|
||||
|
||||
self.textarea_output.appendPlainText(self.i18n['console.install_logs.path'].format('"{}"'.format(log_file)))
|
||||
self.textarea_details.appendPlainText(self.i18n['console.install_logs.path'].format('"{}"'.format(log_file)))
|
||||
except:
|
||||
self.textarea_output.appendPlainText("[warning] Could not write install log file to '{}'".format(log_path))
|
||||
self.textarea_details.appendPlainText("[warning] Could not write install log file to '{}'".format(log_path))
|
||||
|
||||
if res['success']:
|
||||
if self._can_notify_user():
|
||||
@@ -1385,6 +1380,9 @@ class ManageWindow(QWidget):
|
||||
self.pkgs_installed.insert(idx, PackageView(model, self.i18n))
|
||||
|
||||
self.update_custom_actions()
|
||||
self.table_apps.change_headers_policy(policy=QHeaderView.Stretch, maximized=self._maximized)
|
||||
self.table_apps.change_headers_policy(policy=QHeaderView.ResizeToContents, maximized=self._maximized)
|
||||
self._resize(accept_lower_width=False)
|
||||
else:
|
||||
self._show_console_errors()
|
||||
if self._can_notify_user():
|
||||
@@ -1393,19 +1391,19 @@ class ManageWindow(QWidget):
|
||||
def _update_progress(self, value: int):
|
||||
self.progress_bar.setValue(value)
|
||||
|
||||
def begin_execute_custom_action(self, pkg: PackageView, action: CustomSoftwareAction):
|
||||
if pkg is None and not dialog.ask_confirmation(title=self.i18n['confirmation'].capitalize(),
|
||||
body=self.i18n['custom_action.proceed_with'].capitalize().format('"{}"'.format(self.i18n[action.i18n_label_key])),
|
||||
icon=QIcon(action.icon_path) if action.icon_path else QIcon(resource.get_path('img/logo.svg')),
|
||||
i18n=self.i18n):
|
||||
def begin_execute_custom_action(self, pkg: Optional[PackageView], action: CustomSoftwareAction):
|
||||
if pkg is None and not ConfirmationDialog(title=self.i18n['confirmation'].capitalize(),
|
||||
body='<p>{}</p>'.format(self.i18n['custom_action.proceed_with'].capitalize().format(bold(self.i18n[action.i18n_label_key]))),
|
||||
icon=QIcon(action.icon_path) if action.icon_path else QIcon(resource.get_path('img/logo.svg')),
|
||||
i18n=self.i18n).ask():
|
||||
return False
|
||||
|
||||
pwd = None
|
||||
|
||||
if not user.is_root() and action.requires_root:
|
||||
pwd, ok = ask_root_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
valid, pwd = RootDialog.ask_password(self.context, i18n=self.i18n, comp_manager=self.comp_manager)
|
||||
|
||||
if not ok:
|
||||
if not valid:
|
||||
return
|
||||
|
||||
self._begin_action(action_label='{}{}'.format(self.i18n[action.i18n_status_key], ' {}'.format(pkg.model.name) if pkg else ''),
|
||||
@@ -1434,10 +1432,10 @@ class ManageWindow(QWidget):
|
||||
self._show_console_errors()
|
||||
|
||||
def _show_console_checkbox_if_output(self):
|
||||
if self.textarea_output.toPlainText():
|
||||
self.comp_manager.set_component_visible(CHECK_CONSOLE, True)
|
||||
if self.textarea_details.toPlainText():
|
||||
self.comp_manager.set_component_visible(CHECK_DETAILS, True)
|
||||
else:
|
||||
self.comp_manager.set_component_visible(CHECK_CONSOLE, False)
|
||||
self.comp_manager.set_component_visible(CHECK_DETAILS, False)
|
||||
|
||||
def show_settings(self):
|
||||
if self.settings_window:
|
||||
@@ -1450,8 +1448,7 @@ class ManageWindow(QWidget):
|
||||
qt_utils.centralize(self.settings_window)
|
||||
self.settings_window.show()
|
||||
|
||||
def _map_custom_action(self, action: CustomSoftwareAction) -> QAction:
|
||||
custom_action = QAction(self.i18n[action.i18n_label_key])
|
||||
def _map_custom_action(self, action: CustomSoftwareAction, parent: QWidget) -> QCustomMenuAction:
|
||||
|
||||
if action.icon_path:
|
||||
try:
|
||||
@@ -1459,20 +1456,21 @@ class ManageWindow(QWidget):
|
||||
icon = QIcon(action.icon_path)
|
||||
else:
|
||||
icon = QIcon.fromTheme(action.icon_path)
|
||||
|
||||
custom_action.setIcon(icon)
|
||||
|
||||
except:
|
||||
pass
|
||||
icon = None
|
||||
else:
|
||||
icon = None
|
||||
|
||||
custom_action.triggered.connect(lambda: self.begin_execute_custom_action(None, action))
|
||||
return custom_action
|
||||
return QCustomMenuAction(parent=parent,
|
||||
label=self.i18n[action.i18n_label_key],
|
||||
action=lambda: self.begin_execute_custom_action(None, action),
|
||||
icon=icon)
|
||||
|
||||
def show_custom_actions(self):
|
||||
if self.custom_actions:
|
||||
menu_row = QMenu()
|
||||
menu_row.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
actions = [self._map_custom_action(a) for a in self.custom_actions]
|
||||
actions = [self._map_custom_action(a, menu_row) for a in self.custom_actions]
|
||||
menu_row.addActions(actions)
|
||||
menu_row.adjustSize()
|
||||
menu_row.popup(QCursor.pos())
|
||||
@@ -1547,3 +1545,48 @@ class ManageWindow(QWidget):
|
||||
self.__add_category(cat)
|
||||
else:
|
||||
self._update_categories(pkg_categories)
|
||||
|
||||
def _map_theme_action(self, theme: ThemeMetadata, menu: QMenu) -> QCustomMenuAction:
|
||||
def _change_theme():
|
||||
set_theme(theme_key=theme.key, app=QApplication.instance(), logger=self.context.logger)
|
||||
self.thread_save_theme.theme_key = theme.key
|
||||
self.thread_save_theme.start()
|
||||
|
||||
return QCustomMenuAction(label=theme.get_i18n_name(self.i18n),
|
||||
action=_change_theme,
|
||||
parent=menu,
|
||||
tooltip=theme.get_i18n_description(self.i18n))
|
||||
|
||||
def show_themes(self):
|
||||
menu_row = QMenu()
|
||||
menu_row.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
menu_row.addActions(self._map_theme_actions(menu_row))
|
||||
menu_row.adjustSize()
|
||||
menu_row.popup(QCursor.pos())
|
||||
menu_row.exec_()
|
||||
|
||||
def _map_theme_actions(self, menu: QMenu) -> List[QCustomMenuAction]:
|
||||
core_config = read_config()
|
||||
|
||||
current_theme_key, current_action = core_config['ui']['theme'], None
|
||||
|
||||
actions = []
|
||||
|
||||
for t in read_all_themes_metadata():
|
||||
if not t.abstract:
|
||||
action = self._map_theme_action(t, menu)
|
||||
|
||||
if current_action is None and current_theme_key is not None and current_theme_key == t.key:
|
||||
action.button.setProperty('current', 'true')
|
||||
current_action = action
|
||||
else:
|
||||
actions.append(action)
|
||||
|
||||
if not current_action:
|
||||
invalid_action = QCustomMenuAction(label=self.i18n['manage_window.bt_themes.option.invalid'], parent=menu)
|
||||
invalid_action.button.setProperty('current', 'true')
|
||||
current_action = invalid_action
|
||||
|
||||
actions.sort(key=lambda a: a.get_label())
|
||||
actions.insert(0, current_action)
|
||||
return actions
|
||||
|
||||
Reference in New Issue
Block a user