[wgem] custom icon selector

This commit is contained in:
Vinícius Moreira
2019-12-16 11:40:05 -03:00
parent 995dc23a72
commit f6434f9e30
13 changed files with 116 additions and 18 deletions

View File

@@ -123,3 +123,12 @@ class FormComponent(ViewComponent):
super(FormComponent, self).__init__(id_=id_) super(FormComponent, self).__init__(id_=id_)
self.label = label self.label = label
self.components = components 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

View File

@@ -18,7 +18,7 @@ from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory
from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOption, SingleSelectComponent, \ 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.api.constants import DESKTOP_ENTRIES_DIR
from bauh.commons.html import bold from bauh.commons.html import bold
from bauh.commons.system import ProcessHandler, get_dir_size, get_human_size_str 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: def _strip_url_protocol(self, url: str) -> str:
return RE_PROTOCOL_STRIP.split(url)[1].strip().lower() 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: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
res = SearchResult([], [], 0) res = SearchResult([], [], 0)
@@ -243,7 +246,9 @@ class WebApplicationManager(SoftwareManager):
options=[tray_op_off, tray_op_default, tray_op_min], options=[tray_op_off, tray_op_default, tray_op_min],
label=self.i18n['web.install.option.tray.label']) 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_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']) 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_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']) 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'], res = watcher.request_confirmation(title=self.i18n['web.install.options_dialog.title'],
body=None, body=None,
@@ -287,6 +292,10 @@ class WebApplicationManager(SoftwareManager):
if cat != 0: if cat != 0:
app.categories = [cat] 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 res, selected
return False, [] return False, []

View File

@@ -1,4 +1,3 @@
import re
from pathlib import Path from pathlib import Path
from typing import List 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, 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, 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, super(WebApplication, self).__init__(id=id if id else url, name=name, description=description,
icon_url=icon_url, installed=installed, version=version, icon_url=icon_url, installed=installed, version=version,
categories=categories) categories=categories)
self.url = url self.url = url
self.installation_dir = installation_dir self.installation_dir = installation_dir
self.desktop_entry = desktop_entry self.desktop_entry = desktop_entry
self.set_custom_icon(custom_icon)
def has_history(self): def has_history(self):
return False return False
@@ -27,7 +27,7 @@ class WebApplication(SoftwarePackage):
@staticmethod @staticmethod
def _get_cached_attrs() -> tuple: 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): def can_be_downgraded(self):
return False return False
@@ -45,9 +45,15 @@ class WebApplication(SoftwarePackage):
def get_default_icon_path(self) -> str: def get_default_icon_path(self) -> str:
return resource.get_path('img/web.png', ROOT_DIR) 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()) 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): def is_application(self):
return True return True
@@ -76,6 +82,8 @@ class WebApplication(SoftwarePackage):
if val and hasattr(self, attr): if val and hasattr(self, attr):
setattr(self, attr, val) setattr(self, attr, val)
self.set_custom_icon(self.custom_icon)
def can_be_run(self) -> bool: def can_be_run(self) -> bool:
return self.installed and self.installation_dir return self.installed and self.installation_dir
@@ -91,3 +99,10 @@ class WebApplication(SoftwarePackage):
def get_autostart_path(self) -> str: def get_autostart_path(self) -> str:
if self.desktop_entry: if self.desktop_entry:
return '{}/.config/autostart/{}'.format(Path.home(), self.desktop_entry.split('/')[-1]) 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

View File

@@ -3,6 +3,8 @@ web.environment.install=Installing {}
web.waiting.env_updater=Updating environment web.waiting.env_updater=Updating environment
web.env.checking=Checking the Web installation 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.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.options_dialog.title=Installation options
web.install.error=An error has happened during the {} installation web.install.error=An error has happened during the {} installation
web.install.nativefier.error.unknown=Have a look at the {} to identify the reason 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.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.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.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.install_dir.not_found=The installation directory {} was not found
web.uninstall.error.remove_dir=It was not possible to remove {} web.uninstall.error.remove_dir=It was not possible to remove {}
web.info.1_url=URL web.info.1_url=URL

View File

@@ -3,6 +3,8 @@ web.environment.nativefier=Instalando {}
web.waiting.env_updater=Atualizando ambiente web.waiting.env_updater=Atualizando ambiente
web.env.checking=Verificando o ambiente de instalação Web 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.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 web.install.options_dialog.title=Opções de instalação
wen.install.error=Ocorreu um erro durante a instalação de {} wen.install.error=Ocorreu um erro durante a instalação de {}
web.install.nativefier.error.unknown=Dê uma olhada em {} para identificar o motivo 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.label=Iniciar minimizado
web.install.option.tray.min.tip=O aplicativo será iniciado minimizado como um ícone da bandeja do sistema 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.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.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.uninstall.error.remove=Não foi possível remover {}
web.info.1_url=URL 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.6_desktop_entry=atalho
web.info.7_exec_file=executável web.info.7_exec_file=executável
web.info.8_icon_path=ícone web.info.8_icon_path=ícone
web.info.9_size=tamanho web.info.9_size=tamanho

View File

@@ -1,14 +1,16 @@
from pathlib import Path
from typing import Tuple from typing import Tuple
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QRadioButton, QGroupBox, QCheckBox, QComboBox, QGridLayout, QWidget, \ 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, \ from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \
TextInputComponent, FormComponent TextInputComponent, FormComponent, FileChooserComponent
from bauh.view.qt import css from bauh.view.qt import css
from bauh.view.util import resource from bauh.view.util import resource
from bauh.view.util.translation import I18n
class RadioButtonQt(QRadioButton): class RadioButtonQt(QRadioButton):
@@ -257,9 +259,10 @@ class IconButton(QWidget):
class FormQt(QGroupBox): 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 '') super(FormQt, self).__init__(model.label if model.label else '')
self.model = model self.model = model
self.i18n = i18n
self.setLayout(QFormLayout()) self.setLayout(QFormLayout())
self.setStyleSheet(css.GROUP_BOX) self.setStyleSheet(css.GROUP_BOX)
@@ -273,6 +276,9 @@ class FormQt(QGroupBox):
label = QLabel(c.label.capitalize() if c.label else '') label = QLabel(c.label.capitalize() if c.label else '')
field = ComboBoxQt(c) field = ComboBoxQt(c)
self.layout().addRow(label, field) self.layout().addRow(label, field)
elif isinstance(c, FileChooserComponent):
label, field = self._new_file_chooser(c)
self.layout().addRow(label, field)
else: else:
raise Exception('Unsupported component type {}'.format(c.__class__.__name__)) raise Exception('Unsupported component type {}'.format(c.__class__.__name__))
@@ -300,6 +306,35 @@ class FormQt(QGroupBox):
line_edit.textChanged.connect(update_model) line_edit.textChanged.connect(update_model)
return QLabel(c.label.capitalize() if c.label else ''), line_edit 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): def new_single_select(model: SingleSelectComponent):
if model.type == SelectViewType.RADIO: if model.type == SelectViewType.RADIO:

View File

@@ -48,7 +48,7 @@ class ConfirmationDialog(QMessageBox):
elif isinstance(comp, TextInputComponent): elif isinstance(comp, TextInputComponent):
inst = TextInputQt(comp) inst = TextInputQt(comp)
elif isinstance(comp, FormComponent): elif isinstance(comp, FormComponent):
inst = FormQt(comp) inst = FormQt(comp, i18n)
else: else:
raise Exception("Cannot render instances of " + comp.__class__.__name__) raise Exception("Cannot render instances of " + comp.__class__.__name__)

View File

@@ -208,4 +208,8 @@ details=detalls
communication=comunicació communication=comunicació
administration=administració administration=administració
settings=configuració settings=configuració
address=adreça 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

View File

@@ -162,4 +162,8 @@ details=details
communication=Kommunikation communication=Kommunikation
administration=Verwaltung administration=Verwaltung
settings=Einstellungen settings=Einstellungen
address=Adresse address=Adresse
view.components.file_chooser.placeholder=Klicken Sie hier, um auszuwählen
files=Dateien
all_files=Alle Dateien
file_chooser.title=Dateiauswahl

View File

@@ -166,4 +166,8 @@ administration=administration
audio=audio audio=audio
video=video video=video
settings=settings settings=settings
address=address address=address
view.components.file_chooser.placeholder=Click here to select
files=files
all_files=all files
file_chooser.title=File selector

View File

@@ -207,4 +207,8 @@ details=detalles
communication=comunicación communication=comunicación
administration=administración administration=administración
settings=configuraciones settings=configuraciones
address=dirección 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

View File

@@ -163,4 +163,8 @@ details=dettagli
communication=comunicazione communication=comunicazione
administration=amministrazione administration=amministrazione
settings=impostazioni settings=impostazioni
address=indirizzo address=indirizzo
view.components.file_chooser.placeholder=Fai clic qui per selezionare
files=files
all_files=tutti i files
file_chooser.title=Selettore file

View File

@@ -210,4 +210,8 @@ details=detalhes
communication=comunicação communication=comunicação
messaging=mensagem messaging=mensagem
administration=administração administration=administração
address=endereço address=endereço
view.components.file_chooser.placeholder=Clique aqui para selecionar
files=arquivos
all_files=todos os arquivos
file_chooser.title=Seletor arquivos