From fb779cd650c3bb92e29a3654e95c8c6635de465f Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 11 Sep 2019 17:50:53 -0300 Subject: [PATCH] gem selector panel working --- bauh/app.py | 6 ++-- bauh/core/gems.py | 9 +++--- bauh/gems/arch/disk.py | 7 ++++- bauh/gems/arch/model.py | 13 +++++---- bauh/resources/locale/en | 11 ++++++-- bauh/resources/locale/es | 13 +++++++-- bauh/resources/locale/pt | 13 +++++++-- bauh/util/disk.py | 15 +++++----- bauh/view/qt/apps_table.py | 22 +-------------- bauh/view/qt/components.py | 26 ++++++++++++++++-- bauh/view/qt/gem_selector.py | 38 ++++++++++++++++++-------- bauh/view/qt/window.py | 53 +++++++++++++++++++++++++++--------- 12 files changed, 149 insertions(+), 77 deletions(-) diff --git a/bauh/app.py b/bauh/app.py index ea910e0c..a91049c2 100755 --- a/bauh/app.py +++ b/bauh/app.py @@ -49,7 +49,7 @@ else: enabled_managers = [m for m in managers if m.is_enabled()] if not user_config.gems and enabled_managers: - gem_panel = GemSelectorPanel(enabled_managers, i18n, boot=True) + gem_panel = GemSelectorPanel(enabled_managers, i18n, managers_set=None) gem_panel.show() else: manager = GenericSoftwareManager(enabled_managers, context=context, app_args=args) @@ -62,7 +62,9 @@ else: download_icons=bool(args.download_icons), screen_size=app.primaryScreen().size(), suggestions=args.sugs, - display_limit=args.max_displayed) + display_limit=args.max_displayed, + config=user_config, + context=context) if args.tray: trayIcon = TrayIcon(locale_keys=i18n, diff --git a/bauh/core/gems.py b/bauh/core/gems.py index 4e5a1f85..d5fa4599 100644 --- a/bauh/core/gems.py +++ b/bauh/core/gems.py @@ -1,7 +1,7 @@ import inspect import os import pkgutil -from typing import List +from typing import List, Type from bauh import ROOT_DIR from bauh.api.abstract.controller import SoftwareManager, ApplicationContext @@ -29,10 +29,11 @@ def load_managers(locale: str, context: ApplicationContext, names: List[str] = N manager_class = find_manager(module) if manager_class: - locale_path = '{}/resources/locale'.format(f.path) + if locale: + locale_path = '{}/resources/locale'.format(f.path) - if os.path.exists(locale_path): - context.i18n.update(util.get_locale_keys(locale, locale_path)) + if os.path.exists(locale_path): + context.i18n.update(util.get_locale_keys(locale, locale_path)) managers.append(manager_class(context=context)) diff --git a/bauh/gems/arch/disk.py b/bauh/gems/arch/disk.py index 6febf33a..aa36e467 100644 --- a/bauh/gems/arch/disk.py +++ b/bauh/gems/arch/disk.py @@ -113,6 +113,11 @@ def save_several(pkgnames: Set[str], mirror: str, overwrite: bool = True) -> int if entries: desktop_matches[pkg] = entries[0] + if len(entries) > 1: + for e in entries: + if e.startswith('/usr/share/applications'): + desktop_matches[pkg] = e + if not desktop_matches: no_desktop_files = to_cache else: @@ -130,7 +135,7 @@ def save_several(pkgnames: Set[str], mirror: str, overwrite: bool = True) -> int for field in RE_DESKTOP_ENTRY.findall(desktop_entry): if field[0] == 'Exec': - p.command = field[1].strip() + p.command = field[1].strip().replace('"', '') elif field[0] == 'Icon': p.icon_path = field[1].strip() diff --git a/bauh/gems/arch/model.py b/bauh/gems/arch/model.py index f7ded25c..b75e7d63 100644 --- a/bauh/gems/arch/model.py +++ b/bauh/gems/arch/model.py @@ -90,13 +90,14 @@ class ArchPackage(SoftwarePackage): return cache def fill_cached_data(self, data: dict): - for a in {'command', 'icon_path', 'mirror', 'desktop_entry'}: - val = data.get(a) - if val: - setattr(self, a, val) + if data: + for a in {'command', 'icon_path', 'mirror', 'desktop_entry'}: + val = data.get(a) + if val: + setattr(self, a, val) - if a == 'icon_path': - self.icon_url = val + if a == 'icon_path': + self.icon_url = val def can_be_run(self) -> bool: # only returns if there is a desktop entry set for the application to avoid running command-line applications diff --git a/bauh/resources/locale/en b/bauh/resources/locale/en index 6d402422..45f73182 100644 --- a/bauh/resources/locale/en +++ b/bauh/resources/locale/en @@ -44,7 +44,7 @@ popup.button.yes=Yes popup.button.no=No popup.button.cancel=Cancel tray.action.manage=Manage applications -tray.action.exit=Exit +tray.action.exit=Quit tray.action.about=About tray.action.refreshing=Refreshing notification.new_updates=Updates @@ -85,7 +85,7 @@ bt.app_upgrade=Upgrade bt.app_not_upgrade=Don't upgrade manage_window.upgrade_all.popup.title=Upgrade manage_window.upgrade_all.popup.body=Upgrade all selected applications ? -manage_window.bt_about.tooltip=About +manage_window.settings.about=About warning.no_managers=There are no installed extensions. It will not be possible to intall and manage applications. confirmation=confirmation and=and @@ -103,3 +103,10 @@ manage_window.name_filter.tooltip=Type here to filter apps by name manage_window.name_filter.placeholder=Filter by name publisher=publisher publisher.unknown=unknown publisher +welcome=welcome +gem_selector.title=Application types +gem_selector.question=What types of applications do you want to find here ? +proceed=proceed +change=change +exit=exit +manage_window.settings.gems=Application types \ No newline at end of file diff --git a/bauh/resources/locale/es b/bauh/resources/locale/es index 24398a7b..1cf352f4 100644 --- a/bauh/resources/locale/es +++ b/bauh/resources/locale/es @@ -46,7 +46,7 @@ popup.button.yes=Sí popup.button.no=No popup.button.cancel=Cancelar tray.action.manage=Administrar aplicativos -tray.action.exit=Salir +tray.action.exit=Cerrar tray.action.about=Sobre tray.action.refreshing=Recargando notification.new_updates=Actualizaciones @@ -87,7 +87,7 @@ bt.app_upgrade=Actualizar bt.app_not_upgrade=No actualizar manage_window.upgrade_all.popup.title=Actualizar manage_window.upgrade_all.popup.body=¿Actualizar todos los aplicativos seleccionados? -manage_window.bt_about.tooltip=Sobre +manage_window.settings.about=Sobre warning.no_managers=No hay extensiones instaladas. No será posible instalar y administrar aplicaciones. confirmation=confirmación and=y @@ -104,4 +104,11 @@ any=cualquier manage_window.name_filter.tooltip=Escriba aquí para filtrar aplicaciones por nombre manage_window.name_filter.placeholder=Filtrar por nombre publisher=publicador -publisher.unknown=publicador desconocido \ No newline at end of file +publisher.unknown=publicador desconocido +welcome=bienvenido +gem_selector.title=Tipos de aplicativos +gem_selector.question=¿Qué tipos de aplicativos quieres encontrar aquí? +proceed=continuar +change=cambiar +exit=salir +manage_window.settings.gems=Tipos de aplicativos \ No newline at end of file diff --git a/bauh/resources/locale/pt b/bauh/resources/locale/pt index e786a077..1473ffd9 100644 --- a/bauh/resources/locale/pt +++ b/bauh/resources/locale/pt @@ -46,7 +46,7 @@ popup.button.yes=Sim popup.button.no=Não popup.button.cancel=Cancelar tray.action.manage=Gerenciar aplicativos -tray.action.exit=Sair +tray.action.exit=Fechar tray.action.about=Sobre tray.action.refreshing=Recarregando notification.new_updates=Atualizações @@ -87,7 +87,7 @@ bt.app_upgrade=Atualizar bt.app_not_upgrade=Não atualizar manage_window.upgrade_all.popup.title=Atualizar manage_window.upgrade_all.popup.body=Atualizar todos os aplicativos selecionados ? -manage_window.bt_about.tooltip=Sobre +manage_window.settings.about=Sobre warning.no_managers=Não há nenhuma extensão instalada. Não será possível instalar e gerenciar aplicações. confirmation=confirmação and=e @@ -104,4 +104,11 @@ any=qualquer manage_window.name_filter.tooltip=Digite aqui para filtrar aplicativos pelo nome manage_window.name_filter.placeholder=Filtrar por nome publisher=publicador -publisher.unknown=publicador desconhecido \ No newline at end of file +publisher.unknown=publicador desconhecido +welcome=bem vindo +gem_selector.title=Tipos de aplicativos +gem_selector.question=Quais tipos de aplicativos você quer encontrar por aqui ? +proceed=continuar +change=alterar +exit=sair +manage_window.settings.gems=Tipos de aplicativos \ No newline at end of file diff --git a/bauh/util/disk.py b/bauh/util/disk.py index a25d386a..accdd5d2 100644 --- a/bauh/util/disk.py +++ b/bauh/util/disk.py @@ -47,11 +47,12 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader): if os.path.exists(pkg.get_disk_data_path()): with open(pkg.get_disk_data_path()) as f: cached_data = json.loads(f.read()) - pkg.fill_cached_data(cached_data) - cache = self.cache_map.get(pkg.__class__)\ + if cached_data: + pkg.fill_cached_data(cached_data) + cache = self.cache_map.get(pkg.__class__)\ - if cache: - cache.add_non_existing(pkg.id, cached_data) + if cache: + cache.add_non_existing(pkg.id, cached_data) class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory): @@ -63,10 +64,8 @@ class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory): def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache): if pkg_type: - if pkg_type in self.cache_map: - raise Exception('{} is already mapped') - - self.cache_map[pkg_type] = cache + if pkg_type not in self.cache_map: + self.cache_map[pkg_type] = cache def new(self) -> AsyncDiskCacheLoader: return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map) diff --git a/bauh/view/qt/apps_table.py b/bauh/view/qt/apps_table.py index b5831762..64b5d129 100644 --- a/bauh/view/qt/apps_table.py +++ b/bauh/view/qt/apps_table.py @@ -13,32 +13,12 @@ from bauh.api.abstract.model import PackageStatus from bauh.util import resource from bauh.util.html import strip_html from bauh.view.qt import dialog +from bauh.view.qt.components import IconButton from bauh.view.qt.view_model import PackageView, PackageViewStatus INSTALL_BT_STYLE = 'background: {back}; color: white; font-size: 10px; font-weight: bold' -class IconButton(QWidget): - - def __init__(self, icon_path: str, action, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None): - super(IconButton, self).__init__() - self.bt = QToolButton() - self.bt.setIcon(QIcon(icon_path)) - self.bt.clicked.connect(action) - - if background: - self.bt.setStyleSheet('QToolButton { color: white; background: ' + background + '}') - - if tooltip: - self.bt.setToolTip(tooltip) - - layout = QHBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.setAlignment(align) - layout.addWidget(self.bt) - self.setLayout(layout) - - class UpdateToggleButton(QWidget): def __init__(self, app_view: PackageView, root: QWidget, i18n: dict, checked: bool = True, clickable: bool = True): diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 143ce1d2..ea048b8c 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -1,10 +1,9 @@ +from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \ - QLabel, QSizePolicy, QLineEdit + QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout -from bauh import ROOT_DIR from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType -from bauh.commons import resource class RadioButtonQt(QRadioButton): @@ -162,6 +161,27 @@ class InputFilter(QLineEdit): self.last_text = p_str +class IconButton(QWidget): + + def __init__(self, icon_path: str, action, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None): + super(IconButton, self).__init__() + self.bt = QToolButton() + self.bt.setIcon(QIcon(icon_path)) + self.bt.clicked.connect(action) + + if background: + self.bt.setStyleSheet('QToolButton { color: white; background: ' + background + '}') + + if tooltip: + self.bt.setToolTip(tooltip) + + layout = QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.setAlignment(align) + layout.addWidget(self.bt) + self.setLayout(layout) + + def new_single_select(model: SingleSelectComponent): if model.type == SelectViewType.RADIO: return RadioSelectQt(model) diff --git a/bauh/view/qt/gem_selector.py b/bauh/view/qt/gem_selector.py index d457856a..a78a3172 100644 --- a/bauh/view/qt/gem_selector.py +++ b/bauh/view/qt/gem_selector.py @@ -16,26 +16,28 @@ from bauh.view.qt.components import MultipleSelectQt, CheckboxQt, new_spacer class GemSelectorPanel(QWidget): - def __init__(self, managers: List[SoftwareManager], i18n: dict, boot: bool): + def __init__(self, managers: List[SoftwareManager], i18n: dict, managers_set: List[str], exit_on_close: bool = True): super(GemSelectorPanel, self).__init__() self.managers = managers self.setLayout(QGridLayout()) self.setWindowIcon(QIcon(resource.get_path('img/logo.svg', ROOT_DIR))) - self.setWindowTitle('Welcome' if boot else 'Supported types') + self.setWindowTitle(i18n['welcome'].capitalize() if not managers_set else i18n['gem_selector.title']) self.resize(400, 400) + self.exit_on_close = exit_on_close - self.label_question = QLabel('What types of applications do you want to find here ?') + 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('Proceed') - self.bt_proceed.setStyleSheet('QPushButton { background: green; color: white; font-weight: bold}') - self.bt_proceed.setEnabled(True) + self.bt_proceed = QPushButton(i18n['proceed' if not managers_set else 'change'].capitalize()) + self.bt_proceed.setStyleSheet("""QPushButton { background: green; color: white; font-weight: bold} + QPushButton:disabled { background-color: gray; } + """) self.bt_proceed.clicked.connect(self.save) - self.bt_exit = QPushButton('Exit') + self.bt_exit = QPushButton(i18n['exit'].capitalize()) self.bt_exit.setStyleSheet('QPushButton { background: red; color: white; font-weight: bold}') - self.bt_exit.clicked.connect(lambda: QCoreApplication.exit()) + self.bt_exit.clicked.connect(self.exit) gem_options = [] @@ -45,7 +47,14 @@ class GemSelectorPanel(QWidget): value=modname, icon_path='{r}/gems/{n}/resources/img/{n}.png'.format(r=ROOT_DIR, n=modname))) - self.gem_select_model = MultipleSelectComponent(label='', options=gem_options, default_options=set(gem_options), max_per_line=3) + if managers_set: + default_ops = {o for o in gem_options if o.value in managers_set} + else: + default_ops = set(gem_options) + + self.bt_proceed.setEnabled(bool(default_ops)) + + self.gem_select_model = MultipleSelectComponent(label='', options=gem_options, default_options=default_ops, max_per_line=3) self.gem_select = MultipleSelectQt(self.gem_select_model, self.check_state) self.layout().addWidget(self.gem_select, 1, 1) @@ -59,11 +68,18 @@ class GemSelectorPanel(QWidget): self.setFixedSize(self.size()) def check_state(self, model: CheckboxQt, checked: bool): - self.bt_proceed.setEnabled(bool(self.gem_select_model.values)) + if self.isVisible(): + self.bt_proceed.setEnabled(bool(self.gem_select_model.values)) def save(self): config = Configuration(gems=[o.value for o in self.gem_select_model.values]) save(config) subprocess.Popen([sys.executable, *sys.argv]) - self.close() + QCoreApplication.exit() + + def exit(self): + if self.exit_on_close: + QCoreApplication.exit() + else: + self.close() diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index d9f8b8fc..25053f7a 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -3,20 +3,24 @@ from functools import reduce from typing import List, Type, Set from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal -from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap +from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \ - QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox + QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction + from bauh.api.abstract.cache import MemoryCache +from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.controller import SoftwareManager from bauh.api.abstract.model import SoftwarePackage, PackageAction from bauh.api.abstract.view import MessageType - +from bauh.core import gems +from bauh.core.config import Configuration from bauh.util import util, resource from bauh.view.qt import dialog, commons from bauh.view.qt.about import AboutDialog from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton -from bauh.view.qt.components import new_spacer, InputFilter +from bauh.view.qt.components import new_spacer, InputFilter, IconButton from bauh.view.qt.confirmation import ConfirmationDialog +from bauh.view.qt.gem_selector import GemSelectorPanel from bauh.view.qt.history import HistoryDialog from bauh.view.qt.info import InfoDialog from bauh.view.qt.root import is_root, ask_root_password @@ -39,7 +43,8 @@ class ManageWindow(QWidget): signal_user_res = pyqtSignal(bool) signal_table_update = pyqtSignal() - def __init__(self, i18n: dict, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool, download_icons: bool, screen_size, suggestions: bool, display_limit: int, tray_icon=None): + def __init__(self, i18n: dict, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool, download_icons: bool, + screen_size, suggestions: bool, display_limit: int, config: Configuration, context: ApplicationContext, tray_icon=None): super(ManageWindow, self).__init__() self.i18n = i18n self.manager = manager @@ -53,6 +58,8 @@ class ManageWindow(QWidget): self.disk_cache = disk_cache self.download_icons = download_icons self.screen_size = screen_size + self.config = config + self.context = context self.icon_app = QIcon(resource.get_path('img/logo.svg')) self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__) @@ -230,12 +237,8 @@ class ManageWindow(QWidget): self.toolbar_bottom.addWidget(new_spacer()) - bt_about = QToolButton() - bt_about.setStyleSheet('QToolButton { border: 0px; }') - bt_about.setIcon(QIcon(resource.get_path('img/about.svg'))) - bt_about.clicked.connect(self._show_about) - bt_about.setToolTip(self.i18n['manage_window.bt_about.tooltip']) - self.ref_bt_about = self.toolbar_bottom.addWidget(bt_about) + bt_settings = IconButton(icon_path=resource.get_path('img/app_settings.svg'), action=self._show_settings_menu, background='#12ABAB') + self.ref_bt_settings = self.toolbar_bottom.addWidget(bt_settings) self.layout.addWidget(self.toolbar_bottom) @@ -712,7 +715,7 @@ class ManageWindow(QWidget): def _begin_action(self, action_label: str, keep_search: bool = False, keep_bt_installed: bool = True, clear_filters: bool = False): self.ref_input_name_filter.setVisible(False) self.ref_combo_filter_type.setVisible(False) - self.ref_bt_about.setVisible(False) + self.ref_bt_settings.setVisible(False) self.ref_label_updates.setVisible(False) self.thread_animate_progress.stop = False self.thread_animate_progress.start() @@ -748,7 +751,7 @@ class ManageWindow(QWidget): self.progress_bar.setTextVisible(False) self._change_label_substatus('') - self.ref_bt_about.setVisible(True) + self.ref_bt_settings.setVisible(True) self.ref_label_updates.setVisible(True) self.ref_bt_refresh.setVisible(True) @@ -897,3 +900,27 @@ class ManageWindow(QWidget): self.refresh_apps(pkg_types={res['pkg'].model.__class__}) else: self.checkbox_console.setChecked(True) + + def _show_gems_selector(self): + managers = gems.load_managers(context=self.context, locale=None) + enabled_managers = [m for m in managers if m.is_enabled()] + gem_panel = GemSelectorPanel(managers=enabled_managers, i18n=self.i18n, managers_set=self.config.gems, exit_on_close=False) + gem_panel.show() + + def _show_settings_menu(self): + menu_row = QMenu() + + action_gems = QAction(self.i18n['manage_window.settings.gems']) + action_gems.setIcon(self.icon_app) + + action_gems.triggered.connect(self._show_gems_selector) + menu_row.addAction(action_gems) + + action_about = QAction(self.i18n['manage_window.settings.about']) + action_about.setIcon(QIcon(resource.get_path('img/about.svg'))) + action_about.triggered.connect(self._show_about) + menu_row.addAction(action_about) + + menu_row.adjustSize() + menu_row.popup(QCursor.pos()) + menu_row.exec_()