From f6434f9e3090a7b7228ef7c0a577899c007bd401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Moreira?= Date: Mon, 16 Dec 2019 11:40:05 -0300 Subject: [PATCH] [wgem] custom icon selector --- bauh/api/abstract/view.py | 9 +++++++ bauh/gems/web/controller.py | 15 ++++++++--- bauh/gems/web/model.py | 23 ++++++++++++++--- bauh/gems/web/resources/locale/en | 3 +++ bauh/gems/web/resources/locale/pt | 5 +++- bauh/view/qt/components.py | 41 ++++++++++++++++++++++++++++--- bauh/view/qt/confirmation.py | 2 +- bauh/view/resources/locale/ca | 6 ++++- bauh/view/resources/locale/de | 6 ++++- bauh/view/resources/locale/en | 6 ++++- bauh/view/resources/locale/es | 6 ++++- bauh/view/resources/locale/it | 6 ++++- bauh/view/resources/locale/pt | 6 ++++- 13 files changed, 116 insertions(+), 18 deletions(-) diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index a2830567..8242fa9d 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -123,3 +123,12 @@ class FormComponent(ViewComponent): super(FormComponent, self).__init__(id_=id_) self.label = label self.components = components + + +class FileChooserComponent(ViewComponent): + + def __init__(self, allowed_extensions: Set[str] = None, label: str = None, id_: str = None): + super(FileChooserComponent, self).__init__(id_=id_) + self.label = label + self.allowed_extensions = allowed_extensions + self.file_path = None diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 77bea64a..62ba2e54 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -18,7 +18,7 @@ from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOption, SingleSelectComponent, \ - SelectViewType, TextInputComponent, FormComponent + SelectViewType, TextInputComponent, FormComponent, FileChooserComponent from bauh.api.constants import DESKTOP_ENTRIES_DIR from bauh.commons.html import bold from bauh.commons.system import ProcessHandler, get_dir_size, get_human_size_str @@ -100,6 +100,9 @@ class WebApplicationManager(SoftwareManager): def _strip_url_protocol(self, url: str) -> str: return RE_PROTOCOL_STRIP.split(url)[1].strip().lower() + def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool): + super(WebApplicationManager, self).serialize_to_disk(pkg=pkg, icon_bytes=None, only_icon=False) + def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult: res = SearchResult([], [], 0) @@ -243,7 +246,9 @@ class WebApplicationManager(SoftwareManager): options=[tray_op_off, tray_op_default, tray_op_min], label=self.i18n['web.install.option.tray.label']) - form_1 = FormComponent(components=[inp_url, inp_name, inp_desc, inp_cat, inp_tray], label="Basic") + icon_chooser = FileChooserComponent(allowed_extensions={'png'}, label=self.i18n['web.install.option.icon.label']) + + form_1 = FormComponent(components=[inp_url, inp_name, inp_desc, inp_cat, icon_chooser, inp_tray], label=self.i18n['web.install.options.basic'].capitalize()) op_single = InputOption(id_='single', label=self.i18n['web.install.option.single.label'], value="--single-instance", tooltip=self.i18n['web.install.option.single.tip']) op_max = InputOption(id_='max', label=self.i18n['web.install.option.max.label'], value="--maximize", tooltip=self.i18n['web.install.option.max.tip']) @@ -254,7 +259,7 @@ class WebApplicationManager(SoftwareManager): op_insecure = InputOption(id_='insecure', label=self.i18n['web.install.option.insecure.label'], value="--insecure", tooltip=self.i18n['web.install.option.insecure.tip']) op_igcert = InputOption(id_='ignore_certs', label=self.i18n['web.install.option.ignore_certificate.label'], value="--ignore-certificate", tooltip=self.i18n['web.install.option.ignore_certificate.tip']) - check_options = MultipleSelectComponent(options=[op_single, op_allow_urls, op_max, op_fs, op_nframe, op_ncache, op_insecure, op_igcert], default_options={op_single}, label='Advanced') + check_options = MultipleSelectComponent(options=[op_single, op_allow_urls, op_max, op_fs, op_nframe, op_ncache, op_insecure, op_igcert], default_options={op_single}, label=self.i18n['web.install.options.advanced'].capitalize()) res = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'], body=None, @@ -287,6 +292,10 @@ class WebApplicationManager(SoftwareManager): if cat != 0: app.categories = [cat] + if icon_chooser.file_path: + app.set_custom_icon(icon_chooser.file_path) + selected.append('--icon={}'.format(icon_chooser.file_path)) + return res, selected return False, [] diff --git a/bauh/gems/web/model.py b/bauh/gems/web/model.py index 4374f74f..42dd7dc6 100644 --- a/bauh/gems/web/model.py +++ b/bauh/gems/web/model.py @@ -1,4 +1,3 @@ -import re from pathlib import Path from typing import List @@ -11,13 +10,14 @@ class WebApplication(SoftwarePackage): def __init__(self, id: str = None, url: str = None, name: str = None, description: str = None, icon_url: str = None, installation_dir: str = None, desktop_entry: str = None, installed: bool = False, version: str = None, - categories: List[str] = None): + categories: List[str] = None, custom_icon: str = None): super(WebApplication, self).__init__(id=id if id else url, name=name, description=description, icon_url=icon_url, installed=installed, version=version, categories=categories) self.url = url self.installation_dir = installation_dir self.desktop_entry = desktop_entry + self.set_custom_icon(custom_icon) def has_history(self): return False @@ -27,7 +27,7 @@ class WebApplication(SoftwarePackage): @staticmethod def _get_cached_attrs() -> tuple: - return 'id', 'name', 'version', 'url', 'description', 'icon_url', 'installation_dir', 'desktop_entry', 'categories' + return 'id', 'name', 'version', 'url', 'description', 'icon_url', 'installation_dir', 'desktop_entry', 'categories', 'custom_icon' def can_be_downgraded(self): return False @@ -45,9 +45,15 @@ class WebApplication(SoftwarePackage): def get_default_icon_path(self) -> str: return resource.get_path('img/web.png', ROOT_DIR) - def get_disk_data_path(self): + def get_disk_data_path(self) -> str: return '{}/data.yml'.format(self.get_disk_cache_path()) + def get_disk_icon_path(self) -> str: + if self.custom_icon: + return self.custom_icon + else: + super(WebApplication, self).get_disk_icon_path() + def is_application(self): return True @@ -76,6 +82,8 @@ class WebApplication(SoftwarePackage): if val and hasattr(self, attr): setattr(self, attr, val) + self.set_custom_icon(self.custom_icon) + def can_be_run(self) -> bool: return self.installed and self.installation_dir @@ -91,3 +99,10 @@ class WebApplication(SoftwarePackage): def get_autostart_path(self) -> str: if self.desktop_entry: return '{}/.config/autostart/{}'.format(Path.home(), self.desktop_entry.split('/')[-1]) + + def set_custom_icon(self, custom_icon: str): + self.custom_icon = custom_icon + + if custom_icon: + self.icon_url = custom_icon + diff --git a/bauh/gems/web/resources/locale/en b/bauh/gems/web/resources/locale/en index 69c08c7a..99a86289 100644 --- a/bauh/gems/web/resources/locale/en +++ b/bauh/gems/web/resources/locale/en @@ -3,6 +3,8 @@ web.environment.install=Installing {} web.waiting.env_updater=Updating environment web.env.checking=Checking the Web installation environment web.env.error=It seems there are issues with the Web installation environment. It wil not be possible to install {}. +web.install.options.basic=basic +web.install.options.advanced=advanced web.install.options_dialog.title=Installation options web.install.error=An error has happened during the {} installation web.install.nativefier.error.unknown=Have a look at the {} to identify the reason @@ -35,6 +37,7 @@ web.install.option.tray.default.tip=The app icon will be attached to the system web.install.option.tray.min.label=Start minimized web.install.option.tray.min.tip=The app will be started minimized as an icon in the system tray web.install.option.category.none=none +web.install.option.icon.label=Custom icon web.uninstall.error.install_dir.not_found=The installation directory {} was not found web.uninstall.error.remove_dir=It was not possible to remove {} web.info.1_url=URL diff --git a/bauh/gems/web/resources/locale/pt b/bauh/gems/web/resources/locale/pt index 0fa9df17..67a43c09 100644 --- a/bauh/gems/web/resources/locale/pt +++ b/bauh/gems/web/resources/locale/pt @@ -3,6 +3,8 @@ web.environment.nativefier=Instalando {} web.waiting.env_updater=Atualizando ambiente web.env.checking=Verificando o ambiente de instalação Web web.env.error=Parce que existem problemas com o ambiente de instalação Web. Não será possível instalar {}. +web.install.options.basic=básicas +web.install.options.advanced=avançadas web.install.options_dialog.title=Opções de instalação wen.install.error=Ocorreu um erro durante a instalação de {} web.install.nativefier.error.unknown=Dê uma olhada em {} para identificar o motivo @@ -35,6 +37,7 @@ web.install.option.tray.default.tip=O ícone do aplicativo será anexado a bande web.install.option.tray.min.label=Iniciar minimizado web.install.option.tray.min.tip=O aplicativo será iniciado minimizado como um ícone da bandeja do sistema web.install.option.category.none=nenhuma +web.install.option.icon.label=Ícone alternativo web.uninstall.error.install_dir.not_found=O diretório de instalação {} não foi encontrado web.uninstall.error.remove=Não foi possível remover {} web.info.1_url=URL @@ -45,4 +48,4 @@ web.info.5_installation_dir=diretório de instalação web.info.6_desktop_entry=atalho web.info.7_exec_file=executável web.info.8_icon_path=ícone -web.info.9_size=tamanho +web.info.9_size=tamanho \ No newline at end of file diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 438c2b4f..8ef92881 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -1,14 +1,16 @@ +from pathlib import Path from typing import Tuple from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \ - QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout + QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout, QFileDialog from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \ - TextInputComponent, FormComponent + TextInputComponent, FormComponent, FileChooserComponent from bauh.view.qt import css from bauh.view.util import resource +from bauh.view.util.translation import I18n class RadioButtonQt(QRadioButton): @@ -257,9 +259,10 @@ class IconButton(QWidget): class FormQt(QGroupBox): - def __init__(self, model: FormComponent): + def __init__(self, model: FormComponent, i18n: I18n): super(FormQt, self).__init__(model.label if model.label else '') self.model = model + self.i18n = i18n self.setLayout(QFormLayout()) self.setStyleSheet(css.GROUP_BOX) @@ -273,6 +276,9 @@ class FormQt(QGroupBox): label = QLabel(c.label.capitalize() if c.label else '') field = ComboBoxQt(c) self.layout().addRow(label, field) + elif isinstance(c, FileChooserComponent): + label, field = self._new_file_chooser(c) + self.layout().addRow(label, field) else: raise Exception('Unsupported component type {}'.format(c.__class__.__name__)) @@ -300,6 +306,35 @@ class FormQt(QGroupBox): line_edit.textChanged.connect(update_model) return QLabel(c.label.capitalize() if c.label else ''), line_edit + def _new_file_chooser(self, c: FileChooserComponent) -> Tuple[QLabel, QLineEdit]: + chooser = QLineEdit() + chooser.setReadOnly(True) + + chooser.setPlaceholderText(self.i18n['view.components.file_chooser.placeholder']) + + def open_chooser(e): + options = QFileDialog.Options() + + if c.allowed_extensions: + exts = ';;'.join({'{} {} (*.{})'.format(self.i18n['files'].capitalize(), e.upper(), e) for e in c.allowed_extensions}) + else: + exts = '{}} (*);;'.format(self.i18n['all_files'].capitalize()) + + file_path, _ = QFileDialog.getOpenFileName(self, self.i18n['file_chooser.title'], str(Path.home()), exts, options=options) + + if file_path: + c.file_path = file_path + chooser.setText(file_path) + else: + c.file_path = None + chooser.setText('') + + chooser.setCursorPosition(0) + + chooser.mousePressEvent = open_chooser + + return QLabel(c.label if c.label else ''), chooser + def new_single_select(model: SingleSelectComponent): if model.type == SelectViewType.RADIO: diff --git a/bauh/view/qt/confirmation.py b/bauh/view/qt/confirmation.py index 33488855..d6f8ea57 100644 --- a/bauh/view/qt/confirmation.py +++ b/bauh/view/qt/confirmation.py @@ -48,7 +48,7 @@ class ConfirmationDialog(QMessageBox): elif isinstance(comp, TextInputComponent): inst = TextInputQt(comp) elif isinstance(comp, FormComponent): - inst = FormQt(comp) + inst = FormQt(comp, i18n) else: raise Exception("Cannot render instances of " + comp.__class__.__name__) diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index 43b0f737..a449a470 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -208,4 +208,8 @@ details=detalls communication=comunicació administration=administració settings=configuració -address=adreça \ No newline at end of file +address=adreça +view.components.file_chooser.placeholder=Feu clic aquí per seleccionar +files=fitxers +all_files=tots els fitxers +file_chooser.title=Selector de fitxers \ No newline at end of file diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index 59ccd6e6..93065d93 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -162,4 +162,8 @@ details=details communication=Kommunikation administration=Verwaltung settings=Einstellungen -address=Adresse \ No newline at end of file +address=Adresse +view.components.file_chooser.placeholder=Klicken Sie hier, um auszuwählen +files=Dateien +all_files=Alle Dateien +file_chooser.title=Dateiauswahl \ No newline at end of file diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index 37737c22..a43a3c28 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -166,4 +166,8 @@ administration=administration audio=audio video=video settings=settings -address=address \ No newline at end of file +address=address +view.components.file_chooser.placeholder=Click here to select +files=files +all_files=all files +file_chooser.title=File selector \ No newline at end of file diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 6fdee244..140c041f 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -207,4 +207,8 @@ details=detalles communication=comunicación administration=administración settings=configuraciones -address=dirección \ No newline at end of file +address=dirección +view.components.file_chooser.placeholder=Pulse aquí para seleccionar +files=archivos +all_files=todos los archivos +file_chooser.title=Selector de archivos \ No newline at end of file diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index 38731b21..881d15a9 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -163,4 +163,8 @@ details=dettagli communication=comunicazione administration=amministrazione settings=impostazioni -address=indirizzo \ No newline at end of file +address=indirizzo +view.components.file_chooser.placeholder=Fai clic qui per selezionare +files=files +all_files=tutti i files +file_chooser.title=Selettore file \ No newline at end of file diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index e6a3608e..890d257a 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -210,4 +210,8 @@ details=detalhes communication=comunicação messaging=mensagem administration=administração -address=endereço \ No newline at end of file +address=endereço +view.components.file_chooser.placeholder=Clique aqui para selecionar +files=arquivos +all_files=todos os arquivos +file_chooser.title=Seletor arquivos \ No newline at end of file