[ui][settings] sketch

This commit is contained in:
Vinícius Moreira
2020-01-23 18:28:23 -03:00
parent 535e6ab3bb
commit c8a4370ff0
9 changed files with 189 additions and 26 deletions

View File

@@ -11,6 +11,7 @@ from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.disk import DiskCacheLoader 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, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction
from bauh.api.abstract.view import FormComponent, ViewComponent
class SearchResult: class SearchResult:
@@ -272,3 +273,9 @@ class SoftwareManager(ABC):
Removes all data created by the SoftwareManager instance Removes all data created by the SoftwareManager instance
""" """
pass pass
def get_settings(self) -> ViewComponent:
"""
:return: a form abstraction with all available settings
"""
pass

View File

@@ -38,9 +38,6 @@ class InputOption:
if not label: if not label:
raise Exception("'label' must be a not blank string") raise Exception("'label' must be a not blank string")
if value is None:
raise Exception("'value' must be a not blank string")
self.id = id_ self.id = id_
self.label = label self.label = label
self.value = value self.value = value
@@ -132,3 +129,18 @@ class FileChooserComponent(ViewComponent):
self.label = label self.label = label
self.allowed_extensions = allowed_extensions self.allowed_extensions = allowed_extensions
self.file_path = None self.file_path = None
class TabComponent(ViewComponent):
def __init__(self, label: str, content: ViewComponent, id_: str = None):
super(TabComponent, self).__init__(id_=id_)
self.label = label
self.content = content
class TabGroupComponent(ViewComponent):
def __init__(self, components: List[TabComponent], id_: str = None):
super(TabGroupComponent, self).__init__(id_=id_)
self.components = components

View File

@@ -15,7 +15,8 @@ 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 PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \ from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \
SuggestionPriority SuggestionPriority
from bauh.api.abstract.view import MessageType from bauh.api.abstract.view import MessageType, FormComponent, InputOption, SingleSelectComponent, SelectViewType, \
ViewComponent
from bauh.commons.category import CategoriesDownloader from bauh.commons.category import CategoriesDownloader
from bauh.commons.html import bold from bauh.commons.html import bold
from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, new_root_subprocess, \ from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, new_root_subprocess, \
@@ -883,3 +884,26 @@ class ArchManager(SoftwareManager):
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]: def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
pass pass
def get_settings(self) -> ViewComponent:
config = read_config()
optz_opts = [InputOption(label='on'.capitalize(), value=True),
InputOption(label='off'.capitalize(), value=False)]
trans_check_opts = [InputOption(label='on'.capitalize(), value=True),
InputOption(label='off'.capitalize(), value=False)]
fields = [
SingleSelectComponent(label='Optimizations',
options=optz_opts,
default_option=[o for o in optz_opts if o.value == config['optimize']][0],
max_per_line=len(optz_opts),
type_=SelectViewType.RADIO),
SingleSelectComponent(label='Transitive dependency checking',
options=trans_check_opts,
default_option=[o for o in trans_check_opts if o.value == config['optimize']][0],
max_per_line=len(trans_check_opts),
type_=SelectViewType.RADIO)]
return FormComponent([FormComponent(fields, label='Installation')])

View File

@@ -8,7 +8,8 @@ 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 PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \ from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \
SuggestionPriority SuggestionPriority
from bauh.api.abstract.view import MessageType from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \
ViewComponent
from bauh.commons import user from bauh.commons import user
from bauh.commons.html import strip_html, bold from bauh.commons.html import strip_html, bold
from bauh.commons.system import SystemProcess, ProcessHandler, SimpleProcess from bauh.commons.system import SystemProcess, ProcessHandler, SimpleProcess
@@ -415,3 +416,19 @@ class FlatpakManager(SoftwareManager):
traceback.print_exc() traceback.print_exc()
return urls return urls
def get_settings(self) -> ViewComponent:
fields = []
config = read_config()
install_opts = [InputOption(label='system'.capitalize(), value='system'),
InputOption(label='user'.capitalize(), value='user'),
InputOption(label='ask'.capitalize(), value=None)]
fields.append(SingleSelectComponent(label='Level',
options=install_opts,
default_option=[o for o in install_opts if o.value == config['installation_level']][0],
max_per_line=len(install_opts),
type_=SelectViewType.RADIO))
return FormComponent([FormComponent(fields, 'Installation')])

View File

@@ -8,8 +8,11 @@ from bauh.api.abstract.controller import SoftwareManager, SearchResult, Applicat
from bauh.api.abstract.disk import DiskCacheLoader 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, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction
from bauh.api.abstract.view import FormComponent, ViewComponent, TabGroupComponent, TabComponent, SingleSelectComponent, \
InputOption
from bauh.api.exception import NoInternetException from bauh.api.exception import NoInternetException
from bauh.commons import internet from bauh.commons import internet
from bauh.view.core import config
RE_IS_URL = re.compile(r'^https?://.+') RE_IS_URL = re.compile(r'^https?://.+')
@@ -398,3 +401,15 @@ class GenericSoftwareManager(SoftwareManager):
def get_working_managers(self): def get_working_managers(self):
return [m for m in self.managers if self._can_work(m)] return [m for m in self.managers if self._can_work(m)]
def get_settings(self) -> ViewComponent:
settings_forms = []
for man in self.managers:
if self._can_work(man):
man_comp = man.get_settings()
if man_comp:
settings_forms.append(TabComponent(man.__class__.__name__, man_comp))
return TabGroupComponent(settings_forms)

View File

@@ -4,11 +4,11 @@ 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, QFileDialog QLabel, QSizePolicy, QLineEdit, QToolButton, QHBoxLayout, QFormLayout, QFileDialog, QTabWidget
from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \ from bauh.api.abstract.view import SingleSelectComponent, InputOption, MultipleSelectComponent, SelectViewType, \
TextInputComponent, FormComponent, FileChooserComponent TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, TabGroupComponent
from bauh.view.qt import css, view_utils 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 from bauh.view.util.translation import I18n
@@ -83,6 +83,34 @@ class ComboBoxQt(QComboBox):
self.setToolTip(self.model.value.tooltip) self.setToolTip(self.model.value.tooltip)
class RadioBoxQt(QWidget):
def __init__(self, model: SingleSelectComponent, parent: QWidget = None):
super(RadioBoxQt, self).__init__(parent=parent)
self.model = model
grid = QGridLayout()
self.setLayout(grid)
line, col = 0, 0
for op in model.options:
comp = RadioButtonQt(op, model)
comp.setText(op.label)
comp.setToolTip(op.tooltip)
if model.value and model.value == op:
self.value = comp
comp.setChecked(True)
grid.addWidget(comp, line, col)
if col + 1 == self.model.max_per_line:
line += 1
col = 0
else:
col += 1
class RadioSelectQt(QGroupBox): class RadioSelectQt(QGroupBox):
def __init__(self, model: SingleSelectComponent): def __init__(self, model: SingleSelectComponent):
@@ -291,11 +319,13 @@ class FormQt(QGroupBox):
self.layout().addRow(label, field) self.layout().addRow(label, field)
elif isinstance(c, SingleSelectComponent): elif isinstance(c, SingleSelectComponent):
label = QLabel(c.label.capitalize() if c.label else '') label = QLabel(c.label.capitalize() if c.label else '')
field = ComboBoxQt(c) field = ComboBoxQt(c) if c.type == SelectViewType.COMBO else RadioBoxQt(c)
self.layout().addRow(label, field) self.layout().addRow(label, field)
elif isinstance(c, FileChooserComponent): elif isinstance(c, FileChooserComponent):
label, field = self._new_file_chooser(c) label, field = self._new_file_chooser(c)
self.layout().addRow(label, field) self.layout().addRow(label, field)
elif isinstance(c, FormComponent):
self.layout().addRow(FormQt(c, self.i18n))
else: else:
raise Exception('Unsupported component type {}'.format(c.__class__.__name__)) raise Exception('Unsupported component type {}'.format(c.__class__.__name__))
@@ -353,7 +383,18 @@ class FormQt(QGroupBox):
return QLabel(c.label if c.label else ''), chooser return QLabel(c.label if c.label else ''), chooser
def new_single_select(model: SingleSelectComponent): class TabGroupQt(QTabWidget):
def __init__(self, model: TabGroupComponent, i18n: I18n, parent: QWidget = None):
super(TabGroupQt, self).__init__(parent=parent)
self.model = model
self.setTabPosition(QTabWidget.West)
for c in model.components:
self.addTab(to_widget(c.content, i18n), c.label)
def new_single_select(model: SingleSelectComponent) -> QWidget:
if model.type == SelectViewType.RADIO: if model.type == SelectViewType.RADIO:
return RadioSelectQt(model) return RadioSelectQt(model)
elif model.type == SelectViewType.COMBO: elif model.type == SelectViewType.COMBO:
@@ -370,3 +411,18 @@ def new_spacer(min_width: int = None) -> QWidget:
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
return spacer return spacer
def to_widget(comp: ViewComponent, i18n: I18n, parent: QWidget = None) -> QWidget:
if isinstance(comp, SingleSelectComponent):
return new_single_select(comp)
elif isinstance(comp, MultipleSelectComponent):
return MultipleSelectQt(comp, None)
elif isinstance(comp, TextInputComponent):
return TextInputQt(comp)
elif isinstance(comp, FormComponent):
return FormQt(comp, i18n)
elif isinstance(comp, TabGroupComponent):
return TabGroupQt(comp, i18n, parent)
else:
raise Exception("Cannot render instances of " + comp.__class__.__name__)

View File

@@ -3,10 +3,9 @@ from typing import List
from PyQt5.QtCore import QSize from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent, TextInputComponent, \ from bauh.api.abstract.view import ViewComponent
FormComponent
from bauh.view.qt import css from bauh.view.qt import css
from bauh.view.qt.components import MultipleSelectQt, new_single_select, TextInputQt, FormQt from bauh.view.qt.components import to_widget
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
@@ -43,17 +42,7 @@ class ConfirmationDialog(QMessageBox):
height = 0 height = 0
for idx, comp in enumerate(components): for idx, comp in enumerate(components):
if isinstance(comp, SingleSelectComponent): inst = to_widget(comp, i18n)
inst = new_single_select(comp)
elif isinstance(comp, MultipleSelectComponent):
inst = MultipleSelectQt(comp, None)
elif isinstance(comp, TextInputComponent):
inst = TextInputQt(comp)
elif isinstance(comp, FormComponent):
inst = FormQt(comp, i18n)
else:
raise Exception("Cannot render instances of " + comp.__class__.__name__)
height += inst.sizeHint().height() height += inst.sizeHint().height()
if inst.sizeHint().width() > width: if inst.sizeHint().width() > width:

29
bauh/view/qt/settings.py Normal file
View File

@@ -0,0 +1,29 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolBar, QSizePolicy, QToolButton
from bauh.api.abstract.controller import SoftwareManager
from bauh.view.qt.components import to_widget
from bauh.view.util.translation import I18n
class SettingsWindow(QWidget):
def __init__(self, manager: SoftwareManager, i18n: I18n, parent: QWidget = None):
super(SettingsWindow, self).__init__(parent=parent)
self.setWindowTitle('Settings')
self.setLayout(QVBoxLayout())
self.manager = manager
self.i18n = i18n
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
settings_model = self.manager.get_settings()
self.layout().addWidget(to_widget(settings_model, i18n))
action_bar = QToolBar()
action_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
bt_save = QToolButton()
bt_save.setText('Save')
action_bar.addWidget(bt_save)
self.layout().addWidget(action_bar)

View File

@@ -17,16 +17,17 @@ from bauh.api.http import HttpClient
from bauh.commons import user from bauh.commons import user
from bauh.commons.html import bold from bauh.commons.html import bold
from bauh.view.core.controller import GenericSoftwareManager from bauh.view.core.controller import GenericSoftwareManager
from bauh.view.qt import dialog, commons, qt_utils, root from bauh.view.qt import dialog, commons, qt_utils, root, view_utils
from bauh.view.qt.about import AboutDialog from bauh.view.qt.about import AboutDialog
from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton
from bauh.view.qt.components import new_spacer, InputFilter, IconButton from bauh.view.qt.components import new_spacer, InputFilter, IconButton, to_widget
from bauh.view.qt.confirmation import ConfirmationDialog from bauh.view.qt.confirmation import ConfirmationDialog
from bauh.view.qt.gem_selector import GemSelectorPanel from bauh.view.qt.gem_selector import GemSelectorPanel
from bauh.view.qt.history import HistoryDialog from bauh.view.qt.history import HistoryDialog
from bauh.view.qt.info import InfoDialog from bauh.view.qt.info import InfoDialog
from bauh.view.qt.root import ask_root_password from bauh.view.qt.root import ask_root_password
from bauh.view.qt.screenshots import ScreenshotsDialog from bauh.view.qt.screenshots import ScreenshotsDialog
from bauh.view.qt.settings import SettingsWindow
from bauh.view.qt.styles import StylesComboBox from bauh.view.qt.styles import StylesComboBox
from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \ from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, VerifyModels, FindSuggestions, ListWarnings, \ GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, VerifyModels, FindSuggestions, ListWarnings, \
@@ -345,6 +346,7 @@ class ManageWindow(QWidget):
self.thread_warnings = ListWarnings(man=manager, i18n=i18n) self.thread_warnings = ListWarnings(man=manager, i18n=i18n)
self.thread_warnings.signal_warnings.connect(self._show_warnings) self.thread_warnings.signal_warnings.connect(self._show_warnings)
self.settings_window = None
def set_tray_icon(self, tray_icon): def set_tray_icon(self, tray_icon):
self.tray_icon = tray_icon self.tray_icon = tray_icon
@@ -1163,6 +1165,14 @@ class ManageWindow(QWidget):
show_panel_after_restart=bool(self.tray_icon)) show_panel_after_restart=bool(self.tray_icon))
gem_panel.show() gem_panel.show()
def show_settings_window(self):
self.settings_window = SettingsWindow(self.manager, self.i18n)
self.settings_window.setMinimumWidth(int(self.screen_size.width() / 4))
self.settings_window.resize(self.size())
self.settings_window.adjustSize()
qt_utils.centralize(self.settings_window)
self.settings_window.show()
def _show_settings_menu(self): def _show_settings_menu(self):
menu_row = QMenu() menu_row = QMenu()
@@ -1173,6 +1183,10 @@ class ManageWindow(QWidget):
action_gems.triggered.connect(self.show_gems_selector) action_gems.triggered.connect(self.show_gems_selector)
menu_row.addAction(action_gems) menu_row.addAction(action_gems)
action_settings = QAction('Settings') # TODO
action_settings.triggered.connect(self.show_settings_window)
menu_row.addAction(action_settings)
action_about = QAction(self.i18n['manage_window.settings.about']) action_about = QAction(self.i18n['manage_window.settings.about'])
action_about.setIcon(QIcon(resource.get_path('img/about.svg'))) action_about.setIcon(QIcon(resource.get_path('img/about.svg')))
action_about.triggered.connect(self._show_about) action_about.triggered.connect(self._show_about)