mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 01:14:15 +02:00
1298 lines
43 KiB
Python
1298 lines
43 KiB
Python
import os
|
|
import traceback
|
|
from pathlib import Path
|
|
from typing import Tuple, Dict, Optional, Set
|
|
|
|
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, 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, \
|
|
ViewComponentAlignment
|
|
from bauh.view.util.translation import I18n
|
|
|
|
|
|
class QtComponentsManager:
|
|
|
|
def __init__(self):
|
|
self.components = {}
|
|
self.groups = {}
|
|
self.group_of_groups = {}
|
|
self._saved_states = {}
|
|
|
|
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)
|
|
|
|
def register_group(self, group_id: int, subgroups: bool, *ids: int):
|
|
if not subgroups:
|
|
self.groups[group_id] = {*ids}
|
|
else:
|
|
self.group_of_groups[group_id] = {*ids}
|
|
|
|
def get_subgroups(self, root_group: int) -> Set[str]:
|
|
return self.group_of_groups.get(root_group, set())
|
|
|
|
def set_components_visible(self, visible: bool, *ids: int):
|
|
if ids:
|
|
for cid in ids:
|
|
self.set_component_visible(cid, visible)
|
|
else:
|
|
for cid in self.components:
|
|
self.set_component_visible(cid, visible)
|
|
|
|
def set_component_visible(self, cid: int, visible: bool):
|
|
comp = self.components.get(cid)
|
|
if comp and self._is_visible(comp) != visible:
|
|
self._save_state(comp)
|
|
self._set_visible(comp, visible)
|
|
|
|
def set_component_enabled(self, cid: int, enabled: bool):
|
|
comp = self.components.get(cid)
|
|
if comp and self._is_enabled(comp) != enabled:
|
|
self._save_state(comp)
|
|
self._set_enabled(comp, enabled)
|
|
|
|
def set_component_read_only(self, cid: int, read_only: bool):
|
|
comp = self.components.get(cid)
|
|
if comp and self._supports_read_only(comp) and self._is_read_only(comp) != read_only:
|
|
self._save_state(comp)
|
|
self._set_read_only(comp, read_only)
|
|
|
|
def set_components_enabled(self, enabled: bool, *ids: int):
|
|
if ids:
|
|
for cid in ids:
|
|
self.set_component_enabled(cid, enabled)
|
|
else:
|
|
for cid in self.components:
|
|
self.set_component_enabled(cid, enabled)
|
|
|
|
def restore_previous_states(self, *ids: int):
|
|
if ids:
|
|
for cid in ids:
|
|
self.restore_previous_state(cid)
|
|
else:
|
|
for cid in self.components:
|
|
self.restore_previous_state(cid)
|
|
|
|
def restore_previous_group_state(self, group_id: int):
|
|
ids = self.groups.get(group_id)
|
|
|
|
if ids:
|
|
self.restore_previous_states(*ids)
|
|
|
|
def restore_previous_groups_states(self, *groups: int):
|
|
if groups:
|
|
for group in groups:
|
|
self.restore_previous_group_state(group)
|
|
|
|
def set_group_visible(self, group_id: int, visible: bool):
|
|
ids = self.groups.get(group_id)
|
|
|
|
if ids:
|
|
self.set_components_visible(visible, *ids)
|
|
|
|
def set_groups_visible(self, visible: bool, *groups: int):
|
|
if groups:
|
|
for group in groups:
|
|
self.set_group_visible(group, visible)
|
|
|
|
def set_group_enabled(self, group_id: int, enabled: bool):
|
|
ids = self.groups.get(group_id)
|
|
|
|
if ids:
|
|
self.set_components_enabled(enabled, *ids)
|
|
|
|
def restore_previous_state(self, cid: int):
|
|
comp = self.components.get(cid)
|
|
|
|
if comp:
|
|
previous_state = {**comp[2]}
|
|
self._restore_state(comp, previous_state)
|
|
|
|
def _set_visible(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]], visible: bool):
|
|
if comp[1]:
|
|
comp[1].setVisible(visible)
|
|
else:
|
|
comp[0].setVisible(visible)
|
|
|
|
def _set_enabled(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]], enabled: bool):
|
|
comp[0].setEnabled(enabled)
|
|
|
|
def _set_read_only(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]], read_only: bool):
|
|
comp[0].setReadOnly(read_only)
|
|
|
|
def _supports_read_only(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]]) -> bool:
|
|
return isinstance(comp, QLineEdit)
|
|
|
|
def is_visible(self, cid: int) -> bool:
|
|
comp = self.components.get(cid)
|
|
return self._is_visible(comp) if comp else False
|
|
|
|
def _is_visible(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]]) -> bool:
|
|
return comp[1].isVisible() if comp[1] else comp[0].isVisible()
|
|
|
|
def _is_enabled(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]]) -> bool:
|
|
return comp[0].isEnabled()
|
|
|
|
def _is_read_only(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]]) -> bool:
|
|
return comp[0].isReadOnly() if self._supports_read_only(comp) else False
|
|
|
|
def _save_state(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]]):
|
|
comp[2]['v'] = self._is_visible(comp)
|
|
comp[2]['e'] = self._is_enabled(comp)
|
|
comp[2]['r'] = self._is_read_only(comp)
|
|
|
|
def list_visible_from_group(self, group_id: int) -> Set[str]:
|
|
ids = self.groups.get(group_id)
|
|
if ids:
|
|
return {cid for cid in ids if self.is_visible(cid)}
|
|
|
|
def disable_visible_from_groups(self, *groups):
|
|
if groups:
|
|
for group in groups:
|
|
ids = self.list_visible_from_group(group)
|
|
|
|
if ids:
|
|
self.set_components_enabled(False, *ids)
|
|
|
|
def disable_visible(self):
|
|
self.set_components_enabled(False, *{cid for cid in self.components if self.is_visible(cid)})
|
|
|
|
def enable_visible(self):
|
|
self.set_components_enabled(True, *{cid for cid in self.components if self.is_visible(cid)})
|
|
|
|
def enable_visible_from_groups(self, *groups):
|
|
if groups:
|
|
for group in groups:
|
|
ids = self.list_visible_from_group(group)
|
|
|
|
if ids:
|
|
self.set_components_enabled(True, *ids)
|
|
|
|
def save_state(self, cid: int, state_id: int):
|
|
comp = self.components.get(cid)
|
|
|
|
if comp:
|
|
self._save_state(comp)
|
|
states = self._saved_states.get(state_id)
|
|
|
|
if states is None:
|
|
states = {}
|
|
self._saved_states[state_id] = states
|
|
|
|
states[cid] = {**comp[2]}
|
|
|
|
def save_states(self, state_id: int, *ids, only_visible: bool = False):
|
|
for cid in (ids if ids else self.components):
|
|
if not only_visible or self.is_visible(cid):
|
|
self.save_state(cid, state_id)
|
|
|
|
def save_group_state(self, group_id: int, state_id: int):
|
|
ids = self.groups.get(group_id)
|
|
|
|
if ids:
|
|
self.save_states(state_id, *ids)
|
|
|
|
def save_groups_states(self, state_id: int, *group_ids):
|
|
if group_ids:
|
|
for group_id in group_ids:
|
|
self.save_group_state(group_id, state_id)
|
|
|
|
def _restore_state(self, comp: Tuple[QWidget, Optional[QAction], Dict[str, bool]], state: Dict[str, bool]):
|
|
self._save_state(comp)
|
|
|
|
if state['v'] != self._is_visible(comp):
|
|
self._set_visible(comp, state['v'])
|
|
|
|
if state['e'] != self._is_enabled(comp):
|
|
self._set_enabled(comp, state['e'])
|
|
|
|
if state['r'] != self._is_read_only(comp):
|
|
self._set_read_only(comp, state['r'])
|
|
|
|
def restore_group_state(self, group_id: int, state_id: int):
|
|
states = self._saved_states.get(state_id)
|
|
|
|
if states:
|
|
ids = self.groups.get(group_id)
|
|
|
|
if ids:
|
|
for cid in ids:
|
|
comp_state = states.get(cid)
|
|
|
|
if comp_state:
|
|
comp = self.components.get(cid)
|
|
|
|
if comp:
|
|
self._restore_state(comp, comp_state)
|
|
|
|
def restore_groups_state(self, state_id: int, *group_ids):
|
|
if group_ids:
|
|
for group_id in group_ids:
|
|
self.restore_group_state(group_id, state_id)
|
|
|
|
def restore_state(self, state_id: int):
|
|
state = self._saved_states.get(state_id)
|
|
|
|
if state:
|
|
for cid, cstate in state.items():
|
|
comp = self.components.get(cid)
|
|
|
|
if comp:
|
|
self._restore_state(comp, cstate)
|
|
|
|
del self._saved_states[state_id]
|
|
|
|
def clear_saved_states(self):
|
|
self._saved_states.clear()
|
|
|
|
def remove_saved_state(self, state_id: int):
|
|
if state_id in self._saved_states:
|
|
del self._saved_states[state_id]
|
|
|
|
|
|
def map_alignment(alignment: ViewComponentAlignment) -> Optional[int]:
|
|
if alignment == ViewComponentAlignment.CENTER:
|
|
return Qt.AlignCenter
|
|
elif alignment == ViewComponentAlignment.LEFT:
|
|
return Qt.AlignLeft
|
|
elif alignment == ViewComponentAlignment.RIGHT:
|
|
return Qt.AlignRight
|
|
elif alignment == ViewComponentAlignment.HORIZONTAL_CENTER:
|
|
return Qt.AlignHCenter
|
|
elif alignment == ViewComponentAlignment.VERTICAL_CENTER:
|
|
return Qt.AlignVCenter
|
|
elif alignment == ViewComponentAlignment.BOTTOM:
|
|
return Qt.AlignBottom
|
|
elif alignment == ViewComponentAlignment.TOP:
|
|
return Qt.AlignTop
|
|
else:
|
|
return
|
|
|
|
|
|
class RadioButtonQt(QRadioButton):
|
|
|
|
def __init__(self, model: InputOption, model_parent: SingleSelectComponent):
|
|
super(RadioButtonQt, self).__init__()
|
|
self.model = model
|
|
self.model_parent = model_parent
|
|
self.toggled.connect(self._set_checked)
|
|
self.setCursor(QCursor(Qt.PointingHandCursor))
|
|
|
|
if model_parent.id:
|
|
self.setProperty('parent', model_parent.id)
|
|
|
|
if model.icon_path:
|
|
if model.icon_path.startswith('/'):
|
|
self.setIcon(QIcon(model.icon_path))
|
|
else:
|
|
self.setIcon(QIcon.fromTheme(model.icon_path))
|
|
|
|
if self.model.read_only:
|
|
self.setAttribute(Qt.WA_TransparentForMouseEvents)
|
|
self.setFocusPolicy(Qt.NoFocus)
|
|
|
|
if model.extra_properties:
|
|
for name, val in model.extra_properties.items():
|
|
self.setProperty(name, val)
|
|
|
|
def _set_checked(self, checked: bool):
|
|
if checked:
|
|
self.model_parent.value = self.model
|
|
|
|
|
|
class CheckboxQt(QCheckBox):
|
|
|
|
def __init__(self, model: InputOption, model_parent: MultipleSelectComponent, callback):
|
|
super(CheckboxQt, self).__init__()
|
|
self.model = model
|
|
self.model_parent = model_parent
|
|
self.stateChanged.connect(self._set_checked)
|
|
self.callback = callback
|
|
self.setText(model.label)
|
|
self.setToolTip(model.tooltip)
|
|
|
|
if model.icon_path:
|
|
if model.icon_path.startswith('/'):
|
|
self.setIcon(QIcon(model.icon_path))
|
|
else:
|
|
self.setIcon(QIcon.fromTheme(model.icon_path))
|
|
|
|
if model.read_only:
|
|
self.setAttribute(Qt.WA_TransparentForMouseEvents)
|
|
self.setFocusPolicy(Qt.NoFocus)
|
|
else:
|
|
self.setCursor(QCursor(Qt.PointingHandCursor))
|
|
|
|
if model.extra_properties:
|
|
for name, val in model.extra_properties.items():
|
|
self.setProperty(name, val)
|
|
|
|
def _set_checked(self, state):
|
|
checked = state == 2
|
|
|
|
if checked:
|
|
self.model_parent.values.add(self.model)
|
|
else:
|
|
if self.model in self.model_parent.values:
|
|
self.model_parent.values.remove(self.model)
|
|
|
|
if self.callback:
|
|
self.callback(self.model, checked)
|
|
|
|
|
|
class TwoStateButtonQt(QSlider):
|
|
|
|
def __init__(self, model: TwoStateButtonComponent):
|
|
super(TwoStateButtonQt, self).__init__(Qt.Horizontal)
|
|
self.model = model
|
|
self.setMaximum(1)
|
|
self.valueChanged.connect(self._change_state)
|
|
|
|
def mousePressEvent(self, QMouseEvent):
|
|
self.setValue(1 if self.value() == 0 else 0)
|
|
|
|
def _change_state(self, state: int):
|
|
self.model.state = bool(state)
|
|
|
|
|
|
class FormComboBoxQt(QComboBox):
|
|
|
|
def __init__(self, model: SingleSelectComponent):
|
|
super(FormComboBoxQt, self).__init__()
|
|
self.model = model
|
|
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
|
self.setCursor(QCursor(Qt.PointingHandCursor))
|
|
self.view().setCursor(QCursor(Qt.PointingHandCursor))
|
|
self.setEditable(True)
|
|
self.lineEdit().setReadOnly(True)
|
|
|
|
if model.alignment:
|
|
comp_alignment = map_alignment(model.alignment)
|
|
|
|
if comp_alignment is not None:
|
|
self.lineEdit().setAlignment(comp_alignment)
|
|
|
|
if model.max_width > 0:
|
|
self.setMaximumWidth(int(model.max_width))
|
|
|
|
for idx, op in enumerate(self.model.options):
|
|
icon = QIcon(op.icon_path) if op.icon_path else QIcon()
|
|
self.addItem(icon, op.label, op.value)
|
|
|
|
if op.tooltip:
|
|
self.setItemData(idx, op.tooltip, Qt.ToolTipRole)
|
|
|
|
if model.value and model.value == op: # default
|
|
self.setCurrentIndex(idx)
|
|
self.setToolTip(model.value.tooltip)
|
|
|
|
self.currentIndexChanged.connect(self._set_selected)
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
def _set_selected(self, idx: int):
|
|
self.model.value = self.model.options[idx]
|
|
self.setToolTip(self.model.value.tooltip)
|
|
|
|
|
|
class FormRadioSelectQt(QWidget):
|
|
|
|
def __init__(self, model: SingleSelectComponent, parent: QWidget = None):
|
|
super(FormRadioSelectQt, self).__init__(parent=parent)
|
|
self.model = model
|
|
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
|
|
self.setProperty('opts', str(len(self.model.options) if self.model.options else 0))
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
if model.max_width and model.max_width > 0:
|
|
self.setMaximumWidth(int(model.max_width))
|
|
|
|
grid = QGridLayout()
|
|
self.setLayout(grid)
|
|
|
|
line, col = 0, 0
|
|
for op in model.options:
|
|
comp = RadioButtonQt(op, model)
|
|
comp.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
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
|
|
|
|
if model.max_width is not None and model.max_width <= 0:
|
|
self.setMaximumWidth(int(self.sizeHint().width()))
|
|
|
|
|
|
class RadioSelectQt(QGroupBox):
|
|
|
|
def __init__(self, model: SingleSelectComponent):
|
|
super(RadioSelectQt, self).__init__(model.label + ' :' if model.label else None)
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
if not model.label:
|
|
self.setProperty('no_label', 'true')
|
|
|
|
self.model = model
|
|
|
|
grid = QGridLayout()
|
|
self.setLayout(grid)
|
|
|
|
self.setProperty('opts', str(len(model.options)) if model.options else '0')
|
|
|
|
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 ComboSelectQt(QGroupBox):
|
|
|
|
def __init__(self, model: SingleSelectComponent):
|
|
super(ComboSelectQt, self).__init__()
|
|
self.model = model
|
|
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)
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
|
|
class QLineEditObserver(QLineEdit, ViewObserver):
|
|
|
|
def __init__(self, **kwargs):
|
|
super(QLineEditObserver, self).__init__(**kwargs)
|
|
|
|
def on_change(self, change: str):
|
|
if self.text() != change:
|
|
self.setText(change if change is not None else '')
|
|
|
|
|
|
class QPlainTextEditObserver(QPlainTextEdit, ViewObserver):
|
|
|
|
def __init__(self, **kwargs):
|
|
super(QPlainTextEditObserver, self).__init__(**kwargs)
|
|
|
|
def on_change(self, change: str):
|
|
self.setText(change)
|
|
|
|
def setText(self, text: str):
|
|
if text != self.toPlainText():
|
|
self.setPlainText(text if text is not None else '')
|
|
|
|
def setCursorPosition(self, idx: int):
|
|
self.textCursor().setPosition(idx)
|
|
|
|
|
|
class TextInputQt(QGroupBox):
|
|
|
|
def __init__(self, model: TextInputComponent):
|
|
super(TextInputQt, self).__init__()
|
|
self.model = model
|
|
self.setLayout(QGridLayout())
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
if self.model.max_width and self.model.max_width > 0:
|
|
self.setMaximumWidth(int(self.model.max_width))
|
|
|
|
self.text_input = QLineEditObserver() if model.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver()
|
|
|
|
if model.only_int:
|
|
self.text_input.setValidator(QIntValidator())
|
|
|
|
if model.placeholder:
|
|
self.text_input.setPlaceholderText(model.placeholder)
|
|
|
|
if model.min_width >= 0:
|
|
self.text_input.setMinimumWidth(int(model.min_width))
|
|
|
|
if model.min_height >= 0:
|
|
self.text_input.setMinimumHeight(int(model.min_height))
|
|
|
|
if model.tooltip:
|
|
self.text_input.setToolTip(model.tooltip)
|
|
|
|
if model.value is not None:
|
|
self.text_input.setText(model.value)
|
|
self.text_input.setCursorPosition(0)
|
|
|
|
self.text_input.textChanged.connect(self._update_model)
|
|
|
|
self.model.observers.append(self.text_input)
|
|
self.layout().addWidget(self.text_input, 0, 1)
|
|
|
|
def _update_model(self, *args):
|
|
change = args[0] if args else self.text_input.toPlainText()
|
|
self.model.set_value(val=change, caller=self)
|
|
|
|
|
|
class MultipleSelectQt(QGroupBox):
|
|
|
|
def __init__(self, model: MultipleSelectComponent, callback):
|
|
super(MultipleSelectQt, self).__init__(model.label if model.label else None)
|
|
self.model = model
|
|
self._layout = QGridLayout()
|
|
self.setLayout(self._layout)
|
|
|
|
if model.min_width and model.min_width > 0:
|
|
self.setMinimumWidth(int(model.min_width))
|
|
|
|
if model.max_width and model.max_width > 0:
|
|
self.setMaximumWidth(int(model.max_width))
|
|
|
|
if model.max_height and model.max_height > 0:
|
|
self.setMaximumHeight(int(model.max_height))
|
|
|
|
if model.label:
|
|
line = 1
|
|
pre_label = QLabel()
|
|
self.layout().addWidget(pre_label, 0, 1)
|
|
else:
|
|
line = 0
|
|
|
|
col = 0
|
|
|
|
for op in model.options:
|
|
comp = CheckboxQt(op, model, callback)
|
|
|
|
if model.values and op in model.values:
|
|
self.value = comp
|
|
comp.setChecked(True)
|
|
|
|
widget = QWidget()
|
|
widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
|
widget.setLayout(QHBoxLayout())
|
|
widget.layout().addWidget(comp)
|
|
|
|
if model.opt_max_width and model.opt_max_width > 0:
|
|
widget.setMinimumWidth(int(model.opt_max_width))
|
|
|
|
if op.tooltip:
|
|
help_icon = QLabel()
|
|
|
|
if op.extra_properties and op.extra_properties.get('warning') == 'true':
|
|
help_icon.setProperty('warning_icon', 'true')
|
|
else:
|
|
help_icon.setProperty('help_icon', 'true')
|
|
|
|
help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
|
help_icon.setToolTip(op.tooltip)
|
|
widget.layout().addWidget(help_icon)
|
|
|
|
self._layout.addWidget(widget, line, col)
|
|
|
|
if col + 1 == self.model.max_per_line:
|
|
line += 1
|
|
col = 0
|
|
else:
|
|
col += 1
|
|
|
|
if model.label:
|
|
pos_label = QLabel()
|
|
self.layout().addWidget(pos_label, line + 1, 1)
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
|
|
class FormMultipleSelectQt(QWidget):
|
|
|
|
def __init__(self, model: MultipleSelectComponent, parent: QWidget = None):
|
|
super(FormMultipleSelectQt, self).__init__(parent=parent)
|
|
self.model = model
|
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
|
|
|
if model.min_width and model.min_width > 0:
|
|
self.setMinimumWidth(int(model.min_width))
|
|
|
|
if model.max_width and model.max_width > 0:
|
|
self.setMaximumWidth(int(model.max_width))
|
|
|
|
if model.max_height and model.max_height > 0:
|
|
self.setMaximumHeight(int(model.max_height))
|
|
|
|
self._layout = QGridLayout()
|
|
self.setLayout(self._layout)
|
|
|
|
if model.label:
|
|
line = 1
|
|
self._layout.addWidget(QLabel(), 0, 1)
|
|
else:
|
|
line = 0
|
|
|
|
col = 0
|
|
|
|
for op in model.options:
|
|
comp = CheckboxQt(op, model, None)
|
|
|
|
if model.values and op in model.values:
|
|
self.value = comp
|
|
comp.setChecked(True)
|
|
|
|
widget = QWidget()
|
|
widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
|
widget.setLayout(QHBoxLayout())
|
|
widget.layout().addWidget(comp)
|
|
|
|
if model.opt_max_width and model.opt_max_width > 0:
|
|
widget.setMinimumWidth(int(model.opt_max_width))
|
|
|
|
if op.tooltip:
|
|
help_icon = QLabel()
|
|
|
|
if op.extra_properties and op.extra_properties.get('warning') == 'true':
|
|
help_icon.setProperty('warning_icon', 'true')
|
|
else:
|
|
help_icon.setProperty('help_icon', 'true')
|
|
|
|
help_icon.setToolTip(op.tooltip)
|
|
help_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
|
widget.layout().addWidget(help_icon)
|
|
|
|
self._layout.addWidget(widget, line, col)
|
|
|
|
if col + 1 == self.model.max_per_line:
|
|
line += 1
|
|
col = 0
|
|
else:
|
|
col += 1
|
|
|
|
if model.label:
|
|
self.layout().addWidget(QLabel(), line + 1, 1)
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
|
|
class InputFilter(QLineEdit):
|
|
|
|
def __init__(self, on_key_press):
|
|
super(InputFilter, self).__init__()
|
|
self.on_key_press = on_key_press
|
|
self.last_text = ''
|
|
self.typing = QTimer()
|
|
self.typing.timeout.connect(self.notify_text_change)
|
|
|
|
def notify_text_change(self):
|
|
text = self.text().strip()
|
|
|
|
if text != self.last_text:
|
|
self.last_text = text
|
|
self.on_key_press()
|
|
|
|
def keyPressEvent(self, event):
|
|
super(InputFilter, self).keyPressEvent(event)
|
|
|
|
if self.typing.isActive():
|
|
return
|
|
|
|
self.typing.start(3000)
|
|
|
|
def get_text(self):
|
|
return self.last_text
|
|
|
|
def setText(self, p_str):
|
|
super(InputFilter, self).setText(p_str)
|
|
self.last_text = p_str
|
|
|
|
|
|
class IconButton(QToolButton):
|
|
|
|
def __init__(self, action, i18n: I18n, align: int = Qt.AlignCenter, tooltip: str = None, expanding: bool = False):
|
|
super(IconButton, self).__init__()
|
|
self.setCursor(QCursor(Qt.PointingHandCursor))
|
|
self.clicked.connect(action)
|
|
self.i18n = i18n
|
|
self.default_tootip = tooltip
|
|
self.setSizePolicy(QSizePolicy.Expanding if expanding else QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
|
|
if tooltip:
|
|
self.setToolTip(tooltip)
|
|
|
|
def setEnabled(self, enabled):
|
|
super(IconButton, self).setEnabled(enabled)
|
|
|
|
if not enabled:
|
|
self.setToolTip(self.i18n['icon_button.tooltip.disabled'])
|
|
else:
|
|
self.setToolTip(self.default_tootip)
|
|
|
|
|
|
class PanelQt(QWidget):
|
|
|
|
def __init__(self, model: PanelComponent, i18n: I18n, parent: QWidget = None):
|
|
super(PanelQt, self).__init__(parent=parent)
|
|
self.model = model
|
|
self.i18n = i18n
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
self.setLayout(QVBoxLayout())
|
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
|
|
if model.components:
|
|
for c in model.components:
|
|
self.layout().addWidget(to_widget(c, i18n))
|
|
|
|
|
|
class FormQt(QGroupBox):
|
|
|
|
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())
|
|
|
|
if model.id:
|
|
self.setObjectName(model.id)
|
|
|
|
if model.min_width and model.min_width > 0:
|
|
self.setMinimumWidth(model.min_width)
|
|
|
|
if model.spaces:
|
|
self.layout().addRow(QLabel(), QLabel())
|
|
|
|
for idx, c in enumerate(model.components):
|
|
if isinstance(c, TextInputComponent):
|
|
label, field = self._new_text_input(c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, SingleSelectComponent):
|
|
label = self._new_label(c)
|
|
form = FormComboBoxQt(c) if c.type == SelectViewType.COMBO else FormRadioSelectQt(c)
|
|
field = self._wrap(form, c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, RangeInputComponent):
|
|
label = self._new_label(c)
|
|
field = self._wrap(self._new_range_input(c), c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, FileChooserComponent):
|
|
label, field = self._new_file_chooser(c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, FormComponent):
|
|
label, field = None, FormQt(c, self.i18n)
|
|
self.layout().addRow(field)
|
|
elif isinstance(c, TwoStateButtonComponent):
|
|
label, field = self._new_label(c), TwoStateButtonQt(c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, MultipleSelectComponent):
|
|
label, field = self._new_label(c), FormMultipleSelectQt(c)
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, TextComponent):
|
|
label, field = self._new_label(c), QWidget()
|
|
self.layout().addRow(label, field)
|
|
elif isinstance(c, RangeInputComponent):
|
|
label, field = self._new_label(c), self._new_range_input(c)
|
|
self.layout().addRow(label, field)
|
|
else:
|
|
raise Exception('Unsupported component type {}'.format(c.__class__.__name__))
|
|
|
|
if label: # to prevent C++ wrap errors
|
|
setattr(self, 'label_{}'.format(idx), label)
|
|
|
|
if field: # to prevent C++ wrap errors
|
|
setattr(self, 'field_{}'.format(idx), field)
|
|
|
|
if model.spaces:
|
|
self.layout().addRow(QLabel(), QLabel())
|
|
|
|
def _new_label(self, comp) -> QWidget:
|
|
label = QWidget()
|
|
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
label.setLayout(QHBoxLayout())
|
|
label_comp = QLabel()
|
|
label.layout().addWidget(label_comp)
|
|
|
|
if hasattr(comp, 'min_width') and comp.min_width is not None and comp.min_width > 0:
|
|
label_comp.setMinimumWidth(comp.min_width)
|
|
|
|
if hasattr(comp, 'size') and comp.size is not None:
|
|
label_comp.setStyleSheet("QLabel { font-size: " + str(comp.size) + "px }")
|
|
|
|
if hasattr(comp, 'get_label'):
|
|
text = comp.get_label()
|
|
else:
|
|
attr = 'label' if hasattr(comp, 'label') else 'value'
|
|
text = getattr(comp, attr)
|
|
|
|
if text:
|
|
if hasattr(comp, 'capitalize_label') and getattr(comp, 'capitalize_label'):
|
|
label_comp.setText(text.capitalize())
|
|
else:
|
|
label_comp.setText(text)
|
|
|
|
if comp.tooltip:
|
|
label.layout().addWidget(self.gen_tip_icon(comp.tooltip))
|
|
|
|
return label
|
|
|
|
def gen_tip_icon(self, tip: str) -> QLabel:
|
|
tip_icon = QLabel()
|
|
tip_icon.setProperty('tip_icon', 'true')
|
|
tip_icon.setToolTip(tip.strip())
|
|
tip_icon.setCursor(QCursor(Qt.WhatsThisCursor))
|
|
return tip_icon
|
|
|
|
def _new_text_input(self, c: TextInputComponent) -> Tuple[QLabel, QLineEdit]:
|
|
view = QLineEditObserver() if c.type == TextInputType.SINGLE_LINE else QPlainTextEditObserver()
|
|
view.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
|
|
|
if c.id:
|
|
view.setObjectName(c.id)
|
|
|
|
if c.min_width >= 0:
|
|
view.setMinimumWidth(int(c.min_width))
|
|
|
|
if c.min_height >= 0:
|
|
view.setMinimumHeight(int(c.min_height))
|
|
|
|
if c.only_int:
|
|
view.setValidator(QIntValidator())
|
|
|
|
if c.tooltip:
|
|
view.setToolTip(c.tooltip)
|
|
|
|
if c.placeholder:
|
|
view.setPlaceholderText(c.placeholder)
|
|
|
|
if c.value is not None:
|
|
view.setText(str(c.value))
|
|
view.setCursorPosition(0)
|
|
|
|
if c.read_only:
|
|
view.setEnabled(False)
|
|
|
|
def update_model(text: str):
|
|
c.set_value(val=text, caller=view)
|
|
|
|
view.textChanged.connect(update_model)
|
|
c.observers.append(view)
|
|
|
|
label = QWidget()
|
|
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
label.setLayout(QHBoxLayout())
|
|
|
|
label_component = QLabel()
|
|
label.layout().addWidget(label_component)
|
|
|
|
if label:
|
|
label_component.setText(c.get_label())
|
|
|
|
if c.tooltip:
|
|
label.layout().addWidget(self.gen_tip_icon(c.tooltip))
|
|
|
|
return label, self._wrap(view, c)
|
|
|
|
def _new_range_input(self, model: RangeInputComponent) -> QSpinBox:
|
|
spinner = QSpinBox()
|
|
spinner.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
|
spinner.setCursor(QCursor(Qt.PointingHandCursor))
|
|
spinner.setMinimum(model.min)
|
|
spinner.setMaximum(model.max)
|
|
spinner.setSingleStep(model.step)
|
|
spinner.setValue(model.value if model.value is not None else model.min)
|
|
|
|
if model.id:
|
|
spinner.setObjectName(model.id)
|
|
|
|
if model.tooltip:
|
|
spinner.setToolTip(model.tooltip)
|
|
|
|
def _update_value():
|
|
model.value = spinner.value()
|
|
|
|
spinner.valueChanged.connect(_update_value)
|
|
return spinner
|
|
|
|
def _wrap(self, comp: QWidget, model: ViewComponent) -> QWidget:
|
|
field_container = QWidget()
|
|
field_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
field_container.setLayout(QHBoxLayout())
|
|
field_container.layout().setContentsMargins(0, 0, 0, 0)
|
|
field_container.layout().setSpacing(0)
|
|
field_container.layout().setAlignment(Qt.AlignLeft)
|
|
field_container.setProperty('wrapper', 'true')
|
|
field_container.setProperty('wrapped_type', comp.__class__.__name__)
|
|
|
|
if model.id:
|
|
field_container.setProperty('wrapped', model.id)
|
|
|
|
if model.max_width and model.max_width > 0:
|
|
field_container.setMaximumWidth(int(model.max_width))
|
|
|
|
field_container.layout().addWidget(comp)
|
|
return field_container
|
|
|
|
def _new_file_chooser(self, c: FileChooserComponent) -> Tuple[QLabel, QLineEdit]:
|
|
chooser = QLineEditObserver()
|
|
chooser.setProperty('file_chooser', 'true')
|
|
chooser.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
|
chooser.setReadOnly(True)
|
|
|
|
if c.id:
|
|
chooser.setObjectName(c.id)
|
|
|
|
if c.max_width and c.max_width > 0:
|
|
chooser.setMaximumWidth(int(c.max_width))
|
|
|
|
if c.file_path:
|
|
chooser.setText(c.file_path)
|
|
chooser.setCursorPosition(0)
|
|
|
|
c.observers.append(chooser)
|
|
chooser.setPlaceholderText(self.i18n['view.components.file_chooser.placeholder'])
|
|
|
|
def open_chooser(e):
|
|
if c.allowed_extensions:
|
|
sorted_exts = [e for e in c.allowed_extensions if e != '*']
|
|
sorted_exts.sort()
|
|
|
|
if '*' in c.allowed_extensions:
|
|
sorted_exts.append('*')
|
|
|
|
exts = ';;'.join((f'*.{e}' for e in sorted_exts))
|
|
else:
|
|
exts = '{} (*);;'.format(self.i18n['all_files'].capitalize())
|
|
|
|
if c.file_path and os.path.isfile(c.file_path):
|
|
cur_path = c.file_path
|
|
elif c.search_path and os.path.exists(c.search_path):
|
|
cur_path = c.search_path
|
|
else:
|
|
cur_path = str(Path.home())
|
|
|
|
if c.directory:
|
|
opts = QFileDialog.DontUseNativeDialog
|
|
opts |= QFileDialog.ShowDirsOnly
|
|
file_path = QFileDialog.getExistingDirectory(self, self.i18n['file_chooser.title'], cur_path, options=opts)
|
|
else:
|
|
file_path, _ = QFileDialog.getOpenFileName(self, self.i18n['file_chooser.title'], cur_path, exts, options=QFileDialog.DontUseNativeDialog)
|
|
|
|
if file_path:
|
|
c.set_file_path(file_path)
|
|
|
|
chooser.setCursorPosition(0)
|
|
|
|
def clean_path():
|
|
c.set_file_path(None)
|
|
|
|
chooser.mousePressEvent = open_chooser
|
|
|
|
label = self._new_label(c)
|
|
wrapped = self._wrap(chooser, c)
|
|
|
|
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
|
|
|
|
|
|
class TabGroupQt(QTabWidget):
|
|
|
|
def __init__(self, model: TabGroupComponent, i18n: I18n, parent: QWidget = None):
|
|
super(TabGroupQt, self).__init__(parent=parent)
|
|
self.model = model
|
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
|
self.setTabPosition(QTabWidget.North)
|
|
|
|
for c in model.tabs:
|
|
try:
|
|
icon = QIcon(c.icon_path) if c.icon_path else QIcon()
|
|
except Exception:
|
|
traceback.print_exc()
|
|
icon = QIcon()
|
|
|
|
scroll = QScrollArea()
|
|
scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
scroll.setFrameShape(QFrame.NoFrame)
|
|
scroll.setWidgetResizable(True)
|
|
scroll.setWidget(to_widget(c.get_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:
|
|
return RadioSelectQt(model)
|
|
elif model.type == SelectViewType.COMBO:
|
|
return ComboSelectQt(model)
|
|
else:
|
|
raise Exception("Unsupported type {}".format(model.type))
|
|
|
|
|
|
def new_spacer(min_width: Optional[int] = None, min_height: Optional[int] = None, max_width: Optional[int] = None) -> QWidget:
|
|
spacer = QWidget()
|
|
spacer.setProperty('spacer', 'true')
|
|
|
|
if min_width is not None and min_width >= 0:
|
|
spacer.setMinimumWidth(int(min_width))
|
|
|
|
if max_width is not None and max_width >= 0:
|
|
spacer.setMaximumWidth(max_width)
|
|
|
|
if min_height is not None and min_height >= 0:
|
|
spacer.setMaximumHeight(int(min_height))
|
|
|
|
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
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, RangeInputComponent):
|
|
return RangeInputQt(comp)
|
|
elif isinstance(comp, FormComponent):
|
|
return FormQt(comp, i18n)
|
|
elif isinstance(comp, TabGroupComponent):
|
|
return TabGroupQt(comp, i18n, parent)
|
|
elif isinstance(comp, PanelComponent):
|
|
return PanelQt(comp, i18n, parent)
|
|
elif isinstance(comp, TwoStateButtonComponent):
|
|
return TwoStateButtonQt(comp)
|
|
elif isinstance(comp, TextComponent):
|
|
label = QLabel(comp.value)
|
|
|
|
if comp.min_width is not None and comp.min_width > 0:
|
|
label.setMinimumWidth(comp.min_width)
|
|
|
|
if comp.max_width is not None and comp.max_width > 0:
|
|
label.setMinimumWidth(comp.max_width)
|
|
|
|
if comp.size is not None:
|
|
label.setStyleSheet("QLabel { font-size: " + str(comp.size) + "px }")
|
|
|
|
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
|
return label
|
|
elif isinstance(comp, SpacerComponent):
|
|
return new_spacer()
|
|
else:
|
|
raise Exception("Cannot render instances of " + comp.__class__.__name__)
|
|
|
|
|
|
class RangeInputQt(QGroupBox):
|
|
|
|
def __init__(self, model: RangeInputComponent):
|
|
super(RangeInputQt, self).__init__()
|
|
self.model = model
|
|
self.setLayout(QGridLayout())
|
|
self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
|
|
|
|
if self.model.max_width > 0:
|
|
self.setMaximumWidth(int(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)
|
|
self.spinner.setValue(model.value if model.value is not None else model.min)
|
|
|
|
if model.tooltip:
|
|
self.spinner.setToolTip(model.tooltip)
|
|
|
|
self.layout().addWidget(self.spinner, 0, 1)
|
|
|
|
self.spinner.valueChanged.connect(self._update_value)
|
|
|
|
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: Qt.Alignment = Qt.AlignRight,
|
|
policy_width: QSizePolicy.Policy = QSizePolicy.Minimum,
|
|
policy_height: QSizePolicy.Policy = QSizePolicy.Preferred):
|
|
super(QCustomToolbar, self).__init__(parent=parent)
|
|
self.setProperty('container', 'true')
|
|
self.setSizePolicy(policy_width, policy_height)
|
|
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))
|