mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 00:04:15 +02:00
[fix][ui][settings] inputs badly aligned
This commit is contained in:
@@ -68,7 +68,8 @@ class SelectViewType(Enum):
|
|||||||
|
|
||||||
class SingleSelectComponent(InputViewComponent):
|
class SingleSelectComponent(InputViewComponent):
|
||||||
|
|
||||||
def __init__(self, type_: SelectViewType, label: str, options: List[InputOption], default_option: InputOption = None, max_per_line: int = 1, tooltip: str = None, id_: str = None):
|
def __init__(self, type_: SelectViewType, label: str, options: List[InputOption], default_option: InputOption = None,
|
||||||
|
max_per_line: int = 1, tooltip: str = None, max_width: int = -1, id_: str = None):
|
||||||
super(SingleSelectComponent, self).__init__(id_=id_)
|
super(SingleSelectComponent, self).__init__(id_=id_)
|
||||||
self.type = type_
|
self.type = type_
|
||||||
self.label = label
|
self.label = label
|
||||||
@@ -76,6 +77,7 @@ class SingleSelectComponent(InputViewComponent):
|
|||||||
self.value = default_option
|
self.value = default_option
|
||||||
self.max_per_line = max_per_line
|
self.max_per_line = max_per_line
|
||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
|
self.max_width = max_width
|
||||||
|
|
||||||
def get_selected(self):
|
def get_selected(self):
|
||||||
if self.value:
|
if self.value:
|
||||||
@@ -92,7 +94,7 @@ class MultipleSelectComponent(InputViewComponent):
|
|||||||
raise Exception("'options' cannot be None or empty")
|
raise Exception("'options' cannot be None or empty")
|
||||||
|
|
||||||
self.options = options
|
self.options = options
|
||||||
self.spaces= spaces
|
self.spaces = spaces
|
||||||
self.label = label
|
self.label = label
|
||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
self.values = default_options if default_options else set()
|
self.values = default_options if default_options else set()
|
||||||
@@ -108,9 +110,10 @@ class MultipleSelectComponent(InputViewComponent):
|
|||||||
|
|
||||||
class TextComponent(ViewComponent):
|
class TextComponent(ViewComponent):
|
||||||
|
|
||||||
def __init__(self, html: str, id_: str = None):
|
def __init__(self, html: str, max_width: int = -1, id_: str = None):
|
||||||
super(TextComponent, self).__init__(id_=id_)
|
super(TextComponent, self).__init__(id_=id_)
|
||||||
self.value = html
|
self.value = html
|
||||||
|
self.max_width = max_width
|
||||||
|
|
||||||
|
|
||||||
class TwoStateButtonComponent(ViewComponent):
|
class TwoStateButtonComponent(ViewComponent):
|
||||||
@@ -124,7 +127,8 @@ class TwoStateButtonComponent(ViewComponent):
|
|||||||
|
|
||||||
class TextInputComponent(ViewComponent):
|
class TextInputComponent(ViewComponent):
|
||||||
|
|
||||||
def __init__(self, label: str, value: str = '', placeholder: str = None, tooltip: str = None, read_only: bool =False, id_: str = None, only_int: bool = False):
|
def __init__(self, label: str, value: str = '', placeholder: str = None, tooltip: str = None, read_only: bool =False,
|
||||||
|
id_: str = None, only_int: bool = False, max_width: int = -1):
|
||||||
super(TextInputComponent, self).__init__(id_=id_)
|
super(TextInputComponent, self).__init__(id_=id_)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.value = value
|
self.value = value
|
||||||
@@ -132,6 +136,7 @@ class TextInputComponent(ViewComponent):
|
|||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
self.read_only = read_only
|
self.read_only = read_only
|
||||||
self.only_int = only_int
|
self.only_int = only_int
|
||||||
|
self.max_width = max_width
|
||||||
|
|
||||||
def get_value(self) -> str:
|
def get_value(self) -> str:
|
||||||
if self.value is not None:
|
if self.value is not None:
|
||||||
@@ -161,12 +166,14 @@ class FormComponent(ViewComponent):
|
|||||||
|
|
||||||
class FileChooserComponent(ViewComponent):
|
class FileChooserComponent(ViewComponent):
|
||||||
|
|
||||||
def __init__(self, allowed_extensions: Set[str] = None, label: str = None, tooltip: str = None, file_path: str = None, id_: str = None):
|
def __init__(self, allowed_extensions: Set[str] = None, label: str = None, tooltip: str = None,
|
||||||
|
file_path: str = None, max_width: int = -1, id_: str = None):
|
||||||
super(FileChooserComponent, self).__init__(id_=id_)
|
super(FileChooserComponent, self).__init__(id_=id_)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.allowed_extensions = allowed_extensions
|
self.allowed_extensions = allowed_extensions
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
|
self.max_width = max_width
|
||||||
|
|
||||||
|
|
||||||
class TabComponent(ViewComponent):
|
class TabComponent(ViewComponent):
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from bauh.api.abstract.view import FormComponent, ViewComponent, TabGroupCompone
|
|||||||
TextComponent
|
TextComponent
|
||||||
from bauh.api.exception import NoInternetException
|
from bauh.api.exception import NoInternetException
|
||||||
from bauh.commons import internet
|
from bauh.commons import internet
|
||||||
from bauh.commons.html import bold
|
|
||||||
from bauh.view.core import config
|
from bauh.view.core import config
|
||||||
from bauh.view.core.config import read_config
|
from bauh.view.core.config import read_config
|
||||||
from bauh.view.util import translation
|
from bauh.view.util import translation
|
||||||
@@ -409,7 +408,7 @@ 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 _gen_bool_component(self, label: str, tooltip: str, value: bool, id_: str) -> SingleSelectComponent:
|
def _gen_bool_component(self, label: str, tooltip: str, value: bool, id_: str, max_width: int = 200) -> SingleSelectComponent:
|
||||||
opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True),
|
opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True),
|
||||||
InputOption(label=self.i18n['no'].capitalize(), value=False)]
|
InputOption(label=self.i18n['no'].capitalize(), value=False)]
|
||||||
|
|
||||||
@@ -419,14 +418,17 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
type_=SelectViewType.RADIO,
|
type_=SelectViewType.RADIO,
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
max_per_line=len(opts),
|
max_per_line=len(opts),
|
||||||
|
max_width=max_width,
|
||||||
id_=id_)
|
id_=id_)
|
||||||
|
|
||||||
def _gen_general_settings(self, core_config: dict) -> TabComponent:
|
def _gen_general_settings(self, core_config: dict) -> TabComponent:
|
||||||
locale_opts = [InputOption(label=self.i18n['locale.{}'.format(k)].capitalize(), value=k) for k in translation.get_available_keys()]
|
locale_opts = [InputOption(label=self.i18n['locale.{}'.format(k)].capitalize(), value=k) for k in translation.get_available_keys()]
|
||||||
|
|
||||||
select_locale = SingleSelectComponent(label=self.i18n['core.config.locale.label'],
|
select_locale = SingleSelectComponent(label=self.i18n['core.config.locale.label'],
|
||||||
options=locale_opts,
|
options=locale_opts,
|
||||||
default_option=[l for l in locale_opts if l.value == core_config['locale']][0],
|
default_option=[l for l in locale_opts if l.value == core_config['locale']][0],
|
||||||
type_=SelectViewType.COMBO,
|
type_=SelectViewType.COMBO,
|
||||||
|
max_width=150,
|
||||||
id_='locale')
|
id_='locale')
|
||||||
|
|
||||||
select_sysnotify = self._gen_bool_component(label=self.i18n['core.config.system.notifications'].capitalize(),
|
select_sysnotify = self._gen_bool_component(label=self.i18n['core.config.system.notifications'].capitalize(),
|
||||||
@@ -443,6 +445,7 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
tooltip=self.i18n['core.config.suggestions.by_type.tip'],
|
tooltip=self.i18n['core.config.suggestions.by_type.tip'],
|
||||||
value=str(core_config['suggestions']['by_type']),
|
value=str(core_config['suggestions']['by_type']),
|
||||||
only_int=True,
|
only_int=True,
|
||||||
|
max_width=150,
|
||||||
id_="sugs_by_type")
|
id_="sugs_by_type")
|
||||||
|
|
||||||
sub_comps = [FormComponent([select_locale, select_sysnotify, select_sugs, inp_sugs], spaces=False)]
|
sub_comps = [FormComponent([select_locale, select_sysnotify, select_sugs, inp_sugs], spaces=False)]
|
||||||
@@ -460,12 +463,14 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
tooltip=self.i18n['core.config.mem_cache.data_exp.tip'],
|
tooltip=self.i18n['core.config.mem_cache.data_exp.tip'],
|
||||||
value=str(core_config['memory_cache']['data_expiration']),
|
value=str(core_config['memory_cache']['data_expiration']),
|
||||||
only_int=True,
|
only_int=True,
|
||||||
|
max_width=150,
|
||||||
id_="data_exp")
|
id_="data_exp")
|
||||||
|
|
||||||
input_icon_exp = TextInputComponent(label=self.i18n['core.config.mem_cache.icon_exp'],
|
input_icon_exp = TextInputComponent(label=self.i18n['core.config.mem_cache.icon_exp'],
|
||||||
tooltip=self.i18n['core.config.mem_cache.icon_exp.tip'],
|
tooltip=self.i18n['core.config.mem_cache.icon_exp.tip'],
|
||||||
value=str(core_config['memory_cache']['icon_expiration']),
|
value=str(core_config['memory_cache']['icon_expiration']),
|
||||||
only_int=True,
|
only_int=True,
|
||||||
|
max_width=150,
|
||||||
id_="icon_exp")
|
id_="icon_exp")
|
||||||
|
|
||||||
select_dep_check = self._gen_bool_component(label=self.i18n['core.config.system.dep_checking'],
|
select_dep_check = self._gen_bool_component(label=self.i18n['core.config.system.dep_checking'],
|
||||||
@@ -486,6 +491,7 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
tooltip=self.i18n['core.config.updates.interval.tip'],
|
tooltip=self.i18n['core.config.updates.interval.tip'],
|
||||||
only_int=True,
|
only_int=True,
|
||||||
value=str(core_config['updates']['check_interval']),
|
value=str(core_config['updates']['check_interval']),
|
||||||
|
max_width=250,
|
||||||
id_="updates_interval")
|
id_="updates_interval")
|
||||||
|
|
||||||
allowed_exts = {'png', 'svg', 'jpg', 'jpeg', 'ico', 'xpm'}
|
allowed_exts = {'png', 'svg', 'jpg', 'jpeg', 'ico', 'xpm'}
|
||||||
@@ -493,12 +499,14 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
label=self.i18n["core.config.ui.tray.default_icon"].capitalize(),
|
label=self.i18n["core.config.ui.tray.default_icon"].capitalize(),
|
||||||
tooltip=self.i18n["core.config.ui.tray.default_icon.tip"].capitalize(),
|
tooltip=self.i18n["core.config.ui.tray.default_icon.tip"].capitalize(),
|
||||||
file_path=str(core_config['ui']['tray']['default_icon']) if core_config['ui']['tray']['default_icon'] else None,
|
file_path=str(core_config['ui']['tray']['default_icon']) if core_config['ui']['tray']['default_icon'] else None,
|
||||||
|
max_width=250,
|
||||||
allowed_extensions=allowed_exts)
|
allowed_extensions=allowed_exts)
|
||||||
|
|
||||||
select_up_icon = FileChooserComponent(id_='up_icon',
|
select_up_icon = FileChooserComponent(id_='up_icon',
|
||||||
label=self.i18n["core.config.ui.tray.updates_icon"].capitalize(),
|
label=self.i18n["core.config.ui.tray.updates_icon"].capitalize(),
|
||||||
tooltip=self.i18n["core.config.ui.tray.updates_icon.tip"].capitalize(),
|
tooltip=self.i18n["core.config.ui.tray.updates_icon.tip"].capitalize(),
|
||||||
file_path=str(core_config['ui']['tray']['updates_icon']) if core_config['ui']['tray']['updates_icon'] else None,
|
file_path=str(core_config['ui']['tray']['updates_icon']) if core_config['ui']['tray']['updates_icon'] else None,
|
||||||
|
max_width=250,
|
||||||
allowed_extensions=allowed_exts)
|
allowed_extensions=allowed_exts)
|
||||||
|
|
||||||
sub_comps = [FormComponent([input_update_interval, select_def_icon, select_up_icon], spaces=False)]
|
sub_comps = [FormComponent([input_update_interval, select_def_icon, select_up_icon], spaces=False)]
|
||||||
@@ -517,12 +525,14 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
options=style_opts,
|
options=style_opts,
|
||||||
default_option=[o for o in style_opts if o.value == cur_style][0],
|
default_option=[o for o in style_opts if o.value == cur_style][0],
|
||||||
type_=SelectViewType.COMBO,
|
type_=SelectViewType.COMBO,
|
||||||
|
max_width=150,
|
||||||
id_="style")
|
id_="style")
|
||||||
|
|
||||||
input_maxd = TextInputComponent(label=self.i18n['core.config.ui.max_displayed'].capitalize(),
|
input_maxd = TextInputComponent(label=self.i18n['core.config.ui.max_displayed'].capitalize(),
|
||||||
tooltip=self.i18n['core.config.ui.max_displayed.tip'].capitalize(),
|
tooltip=self.i18n['core.config.ui.max_displayed.tip'].capitalize(),
|
||||||
only_int=True,
|
only_int=True,
|
||||||
id_="table_max",
|
id_="table_max",
|
||||||
|
max_width=150,
|
||||||
value=str(core_config['ui']['table']['max_displayed']))
|
value=str(core_config['ui']['table']['max_displayed']))
|
||||||
|
|
||||||
select_dicons = self._gen_bool_component(label=self.i18n['core.config.download.icons'],
|
select_dicons = self._gen_bool_component(label=self.i18n['core.config.download.icons'],
|
||||||
|
|||||||
@@ -79,11 +79,15 @@ class TwoStateButtonQt(QSlider):
|
|||||||
self.model.state = bool(state)
|
self.model.state = bool(state)
|
||||||
|
|
||||||
|
|
||||||
class ComboBoxQt(QComboBox):
|
class FormComboBoxQt(QComboBox):
|
||||||
|
|
||||||
def __init__(self, model: SingleSelectComponent):
|
def __init__(self, model: SingleSelectComponent):
|
||||||
super(ComboBoxQt, self).__init__()
|
super(FormComboBoxQt, self).__init__()
|
||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
|
if model.max_width > 0:
|
||||||
|
self.setMaximumWidth(model.max_width)
|
||||||
|
|
||||||
for idx, op in enumerate(self.model.options):
|
for idx, op in enumerate(self.model.options):
|
||||||
self.addItem(op.label, op.value)
|
self.addItem(op.label, op.value)
|
||||||
|
|
||||||
@@ -101,13 +105,16 @@ class ComboBoxQt(QComboBox):
|
|||||||
self.setToolTip(self.model.value.tooltip)
|
self.setToolTip(self.model.value.tooltip)
|
||||||
|
|
||||||
|
|
||||||
class RadioBoxQt(QWidget):
|
class FormRadioSelectQt(QWidget):
|
||||||
|
|
||||||
def __init__(self, model: SingleSelectComponent, parent: QWidget = None):
|
def __init__(self, model: SingleSelectComponent, parent: QWidget = None):
|
||||||
super(RadioBoxQt, self).__init__(parent=parent)
|
super(FormRadioSelectQt, self).__init__(parent=parent)
|
||||||
self.model = model
|
self.model = model
|
||||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
if model.max_width > 0:
|
||||||
|
self.setMaximumWidth(model.max_width)
|
||||||
|
|
||||||
grid = QGridLayout()
|
grid = QGridLayout()
|
||||||
self.setLayout(grid)
|
self.setLayout(grid)
|
||||||
|
|
||||||
@@ -130,6 +137,9 @@ class RadioBoxQt(QWidget):
|
|||||||
else:
|
else:
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
|
if model.max_width <= 0:
|
||||||
|
self.setMaximumWidth(self.sizeHint().width())
|
||||||
|
|
||||||
|
|
||||||
class RadioSelectQt(QGroupBox):
|
class RadioSelectQt(QGroupBox):
|
||||||
|
|
||||||
@@ -168,7 +178,7 @@ class ComboSelectQt(QGroupBox):
|
|||||||
self.setLayout(QGridLayout())
|
self.setLayout(QGridLayout())
|
||||||
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
||||||
self.layout().addWidget(QLabel(model.label + ' :'), 0, 0)
|
self.layout().addWidget(QLabel(model.label + ' :'), 0, 0)
|
||||||
self.layout().addWidget(ComboBoxQt(model), 0, 1)
|
self.layout().addWidget(FormComboBoxQt(model), 0, 1)
|
||||||
|
|
||||||
|
|
||||||
class TextInputQt(QGroupBox):
|
class TextInputQt(QGroupBox):
|
||||||
@@ -180,6 +190,9 @@ class TextInputQt(QGroupBox):
|
|||||||
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
||||||
self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
|
self.layout().addWidget(QLabel(model.label.capitalize() + ' :' if model.label else ''), 0, 0)
|
||||||
|
|
||||||
|
if self.model.max_width > 0:
|
||||||
|
self.setMaximumWidth(self.model.max_width)
|
||||||
|
|
||||||
self.text_input = QLineEdit()
|
self.text_input = QLineEdit()
|
||||||
|
|
||||||
if model.only_int:
|
if model.only_int:
|
||||||
@@ -202,15 +215,6 @@ class TextInputQt(QGroupBox):
|
|||||||
def _update_model(self, text: str):
|
def _update_model(self, text: str):
|
||||||
self.model.value = text
|
self.model.value = text
|
||||||
|
|
||||||
# class ComboSelectQt(QGroupBox):
|
|
||||||
#
|
|
||||||
# def __init__(self, model: SingleSelectComponent):
|
|
||||||
# super(ComboSelectQt, self).__init__(model.label + ' :')
|
|
||||||
# self.model = model
|
|
||||||
# self.setLayout(QGridLayout())
|
|
||||||
# self.setStyleSheet('QGridLayout {margin-left: 0} QLabel { font-weight: bold}')
|
|
||||||
# self.layout().addWidget(ComboBoxQt(model), 0, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class MultipleSelectQt(QGroupBox):
|
class MultipleSelectQt(QGroupBox):
|
||||||
|
|
||||||
@@ -223,7 +227,8 @@ class MultipleSelectQt(QGroupBox):
|
|||||||
|
|
||||||
if model.label:
|
if model.label:
|
||||||
line = 1
|
line = 1
|
||||||
self.layout().addWidget(QLabel(), 0, 1)
|
pre_label = QLabel()
|
||||||
|
self.layout().addWidget(pre_label, 0, 1)
|
||||||
else:
|
else:
|
||||||
line = 0
|
line = 0
|
||||||
|
|
||||||
@@ -264,7 +269,8 @@ class MultipleSelectQt(QGroupBox):
|
|||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
if model.label:
|
if model.label:
|
||||||
self.layout().addWidget(QLabel(), line + 1, 1)
|
pos_label = QLabel()
|
||||||
|
self.layout().addWidget(pos_label, line + 1, 1)
|
||||||
|
|
||||||
|
|
||||||
class FormMultipleSelectQt(QWidget):
|
class FormMultipleSelectQt(QWidget):
|
||||||
@@ -413,8 +419,8 @@ class FormQt(QGroupBox):
|
|||||||
self.layout().addRow(label, field)
|
self.layout().addRow(label, field)
|
||||||
elif isinstance(c, SingleSelectComponent):
|
elif isinstance(c, SingleSelectComponent):
|
||||||
label = self._new_label(c)
|
label = self._new_label(c)
|
||||||
field = ComboBoxQt(c) if c.type == SelectViewType.COMBO else RadioBoxQt(c)
|
field = FormComboBoxQt(c) if c.type == SelectViewType.COMBO else FormRadioSelectQt(c)
|
||||||
self.layout().addRow(label, field)
|
self.layout().addRow(label, self._wrap(field, c))
|
||||||
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)
|
||||||
@@ -490,12 +496,25 @@ class FormQt(QGroupBox):
|
|||||||
if c.tooltip:
|
if c.tooltip:
|
||||||
label.layout().addWidget(self.gen_tip_icon(c.tooltip))
|
label.layout().addWidget(self.gen_tip_icon(c.tooltip))
|
||||||
|
|
||||||
return label, line_edit
|
return label, self._wrap(line_edit, c)
|
||||||
|
|
||||||
|
def _wrap(self, comp: QWidget, model: ViewComponent) -> QWidget:
|
||||||
|
field_container = QWidget()
|
||||||
|
field_container.setLayout(QHBoxLayout())
|
||||||
|
|
||||||
|
if model.max_width > 0:
|
||||||
|
field_container.setMaximumWidth(model.max_width)
|
||||||
|
|
||||||
|
field_container.layout().addWidget(comp)
|
||||||
|
return field_container
|
||||||
|
|
||||||
def _new_file_chooser(self, c: FileChooserComponent) -> Tuple[QLabel, QLineEdit]:
|
def _new_file_chooser(self, c: FileChooserComponent) -> Tuple[QLabel, QLineEdit]:
|
||||||
chooser = QLineEdit()
|
chooser = QLineEdit()
|
||||||
chooser.setReadOnly(True)
|
chooser.setReadOnly(True)
|
||||||
|
|
||||||
|
if c.max_width > 0:
|
||||||
|
chooser.setMaximumWidth(c.max_width)
|
||||||
|
|
||||||
if c.file_path:
|
if c.file_path:
|
||||||
chooser.setText(c.file_path)
|
chooser.setText(c.file_path)
|
||||||
|
|
||||||
@@ -528,7 +547,7 @@ class FormQt(QGroupBox):
|
|||||||
chooser.mousePressEvent = open_chooser
|
chooser.mousePressEvent = open_chooser
|
||||||
|
|
||||||
label = self._new_label(c)
|
label = self._new_label(c)
|
||||||
return label, chooser
|
return label, self._wrap(chooser, c)
|
||||||
|
|
||||||
|
|
||||||
class TabGroupQt(QTabWidget):
|
class TabGroupQt(QTabWidget):
|
||||||
|
|||||||
Reference in New Issue
Block a user