# -*- coding: utf-8 -*- from functools import partial from PyQt5 import QtCore, QtGui, QtWidgets from chordpro.renderers.html import get_options, get_option_groups from ukatali.util import coerce_bool, convert_units SUPPORTED_SIZES = { QtGui.QPageSize.A4: 'A4', QtGui.QPageSize.A5: 'A5', QtGui.QPageSize.B5: 'B5', QtGui.QPageSize.B6: 'B6', QtGui.QPageSize.Letter: 'Letter', QtGui.QPageSize.Legal: 'Legal', QtGui.QPageSize.ExecutiveStandard: 'Executive' } SUPPORTED_UNITS = { QtGui.QPageSize.Millimeter: 'mm', QtGui.QPageSize.Point: 'pt', QtGui.QPageSize.Inch: 'in' } SUPPORTED_ORIENTATIONS = { 'Portrait': QtGui.QPageLayout.Portrait, 'Landscape': QtGui.QPageLayout.Landscape } class ConfigureDialog(QtWidgets.QDialog): def __init__(self, parent=None): super().__init__(parent) self.settings = QtCore.QSettings() self.option_widgets = {} self.option_values = {} self.editor_values = {} self.export_values = {} self.setup_ui() self.setup_options() def setup_ui(self): self.setObjectName('ConfigureDialog') self.resize(600, 400) self.configure_layout = QtWidgets.QVBoxLayout(self) self.configure_layout.setObjectName("configure_layout") self.category_layout = QtWidgets.QHBoxLayout() self.category_layout.setObjectName('category_layout') self.configure_layout.addLayout(self.category_layout) self.category_list_widget = QtWidgets.QListWidget(self) self.category_list_widget.setFixedWidth(100) self.category_list_widget.addItems(['', '', '', '']) self.category_list_widget.setObjectName('category_list_widget') self.category_layout.addWidget(self.category_list_widget) self.category_stack = QtWidgets.QStackedWidget(self) self.category_stack.setObjectName('category_stack') self.category_layout.addWidget(self.category_stack) # General settings self.general_page = QtWidgets.QWidget() self.general_page.setObjectName('general_page') self.category_stack.addWidget(self.general_page) # Editor settings self.editor_page = QtWidgets.QWidget() self.editor_page.setObjectName('editor_page') self.category_stack.addWidget(self.editor_page) self.editor_layout = QtWidgets.QFormLayout(self.editor_page) self.editor_layout.setObjectName('editor_layout') self.editor_font_combobox = QtWidgets.QFontComboBox(self.editor_page) self.editor_font_combobox.setFontFilters(QtWidgets.QFontComboBox.MonospacedFonts) self.editor_font_combobox.setCurrentFont(QtGui.QFont('monospace')) self.editor_font_combobox.setObjectName('editor_font_combobox') self.editor_layout.addRow('Font', self.editor_font_combobox) self.editor_size_spinbox = QtWidgets.QSpinBox(self.editor_page) self.editor_size_spinbox.setValue(10) self.editor_size_spinbox.setObjectName('editor_size_spinbox') self.editor_layout.addRow('Size', self.editor_size_spinbox) # Renderer settings self.renderer_page = QtWidgets.QWidget() self.renderer_page.setObjectName('renderer_page') self.category_stack.addWidget(self.renderer_page) self.render_layout = QtWidgets.QVBoxLayout(self.renderer_page) self.render_options_combobox = QtWidgets.QComboBox(self.renderer_page) self.render_options_combobox.setObjectName("render_options_combobox") self.render_layout.addWidget(self.render_options_combobox) self.render_options_stack = QtWidgets.QStackedWidget(self) self.render_options_stack.setObjectName("render_options_stack") self.render_layout.addWidget(self.render_options_stack) # Export settings self.export_page = QtWidgets.QWidget() self.export_page.setObjectName('export_page') self.category_stack.addWidget(self.export_page) self.export_layout = QtWidgets.QFormLayout(self.export_page) self.export_layout.setObjectName('export_layout') self.export_size_combobox = QtWidgets.QComboBox(self.export_page) for idx, (size_id, size_name) in enumerate(SUPPORTED_SIZES.items()): self.export_size_combobox.addItem(size_name) self.export_size_combobox.setItemData(idx, size_id) self.export_size_combobox.setCurrentIndex(0) self.export_size_combobox.setObjectName('export_size_combobox') self.export_layout.addRow('Page size', self.export_size_combobox) self.export_orientation_layout = QtWidgets.QHBoxLayout() self.export_orientation_layout.setObjectName('export_orientation_layout') self.export_orientation_portrait_radio = QtWidgets.QRadioButton(self.export_page) self.export_orientation_portrait_radio.setChecked(True) self.export_orientation_portrait_radio.setObjectName('export_orientation_portrait_radio') self.export_orientation_layout.addWidget(self.export_orientation_portrait_radio) self.export_orientation_landscape_radio = QtWidgets.QRadioButton(self.export_page) self.export_orientation_landscape_radio.setObjectName('export_orientation_landscape_radio') self.export_orientation_layout.addWidget(self.export_orientation_landscape_radio) self.export_layout.addRow('Orientation', self.export_orientation_layout) self.export_unit_combobox = QtWidgets.QComboBox(self.export_page) for idx, (unit_id, unit_name) in enumerate(SUPPORTED_UNITS.items()): self.export_unit_combobox.addItem(unit_name) self.export_unit_combobox.setItemData(idx, unit_id) self.export_unit_combobox.setCurrentIndex(0) self.export_unit_combobox.setObjectName('export_unit_combobox') self.export_layout.addRow('Unit', self.export_unit_combobox) self.export_margin_top_spinbox = QtWidgets.QDoubleSpinBox(self.export_page) self.export_margin_top_spinbox.setValue(20) self.export_margin_top_spinbox.setSuffix(self.export_unit_combobox.currentText()) self.export_margin_top_spinbox.setObjectName('export_margin_top_spinbox') self.export_layout.addRow('Top margin', self.export_margin_top_spinbox) self.export_margin_left_spinbox = QtWidgets.QDoubleSpinBox(self.export_page) self.export_margin_left_spinbox.setValue(20) self.export_margin_left_spinbox.setSuffix(self.export_unit_combobox.currentText()) self.export_margin_left_spinbox.setObjectName('export_margin_left_spinbox') self.export_layout.addRow('Left margin', self.export_margin_left_spinbox) self.export_margin_right_spinbox = QtWidgets.QDoubleSpinBox(self.export_page) self.export_margin_right_spinbox.setValue(20) self.export_margin_right_spinbox.setSuffix(self.export_unit_combobox.currentText()) self.export_margin_right_spinbox.setObjectName('export_margin_right_spinbox') self.export_layout.addRow('Right margin', self.export_margin_right_spinbox) self.export_margin_bottom_spinbox = QtWidgets.QDoubleSpinBox(self.export_page) self.export_margin_bottom_spinbox.setValue(20) self.export_margin_bottom_spinbox.setSuffix(self.export_unit_combobox.currentText()) self.export_margin_bottom_spinbox.setObjectName('export_margin_bottom_spinbox') self.export_layout.addRow('Bottom margin', self.export_margin_bottom_spinbox) # Buttons self.button_box = QtWidgets.QDialogButtonBox(self) self.button_box.setOrientation(QtCore.Qt.Horizontal) self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) self.button_box.setObjectName("button_box") self.configure_layout.addWidget(self.button_box) self.retranslate_ui() self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.category_list_widget.currentRowChanged.connect(self.category_stack.setCurrentIndex) self.render_options_combobox.activated.connect(self.render_options_stack.setCurrentIndex) on_editor_font_changed = partial(self._set_editor_value, 'font') self.editor_font_combobox.currentFontChanged.connect(on_editor_font_changed) on_editor_size_changed = partial(self._set_editor_value, 'size') self.editor_size_spinbox.valueChanged.connect(on_editor_size_changed) on_export_size_changed = partial(self._set_export_value, 'page_size') self.export_size_combobox.currentIndexChanged.connect(on_export_size_changed) self.export_unit_combobox.currentIndexChanged.connect(self.on_export_unit_changed) on_export_margin_left_changed = partial(self._set_export_value, 'margin_left') self.export_margin_left_spinbox.valueChanged.connect(on_export_margin_left_changed) on_export_margin_right_changed = partial(self._set_export_value, 'margin_right') self.export_margin_right_spinbox.valueChanged.connect(on_export_margin_right_changed) on_export_margin_top_changed = partial(self._set_export_value, 'margin_top') self.export_margin_top_spinbox.valueChanged.connect(on_export_margin_top_changed) on_export_margin_bottom_changed = partial(self._set_export_value, 'margin_bottom') self.export_margin_bottom_spinbox.valueChanged.connect(on_export_margin_bottom_changed) on_export_orientation_portait_clicked = partial(self._set_export_value, 'orientation') self.export_orientation_portrait_radio.toggled.connect(on_export_orientation_portait_clicked) def retranslate_ui(self): _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("ConfigureDialog", "Configure")) self.category_list_widget.item(0).setText(_translate('ConfigureDialog', 'General')) self.category_list_widget.item(1).setText(_translate('ConfigureDialog', 'Editor')) self.category_list_widget.item(2).setText(_translate('ConfigureDialog', 'Render')) self.category_list_widget.item(3).setText(_translate('ConfigureDialog', 'PDF')) self.export_orientation_portrait_radio.setText(_translate('ConfigureDialog', 'Portrait')) self.export_orientation_landscape_radio.setText(_translate('ConfigureDialog', 'Landscape')) def _set_option_value(self, name, value): """Set an option value""" self.option_values[name] = value def _set_editor_value(self, name, value): """Set an editor value""" self.editor_values[name] = value def _set_export_value(self, name, value): """Set an export value""" if name == 'page_size': value = self.export_size_combobox.itemData(value) elif name == 'units': value = self.export_unit_combobox.itemData(value) elif name == 'orientation': value = QtGui.QPageLayout.Portrait if value else QtGui.QPageLayout.Landscape self.export_values[name] = value def setup_options(self): for group in get_option_groups(): pretty_group = group.replace('_', ' ').title() self.render_options_combobox.addItem(pretty_group) page_widget = QtWidgets.QWidget() page_layout = QtWidgets.QFormLayout(page_widget) for name, details in get_options(group).items(): pretty_name = name.replace(group, '').replace('_', ' ').title() self.option_values[name] = details['default'] widget = None set_value = partial(self._set_option_value, name) if details["type"] is int: widget = QtWidgets.QSpinBox(page_widget) if details['default']: widget.setValue(details['default']) widget.valueChanged.connect(set_value) elif details["type"] is bool: widget = QtWidgets.QCheckBox(page_widget) widget.setChecked(coerce_bool(details['default'])) widget.toggled.connect(set_value) elif 'font' in name: widget = QtWidgets.QFontComboBox(page_widget) if details['default']: widget.setFont(QtGui.QFont(details['default'])) widget.currentFontChanged.connect(set_value) else: widget = QtWidgets.QLineEdit(page_widget) if details['default']: widget.setText(details['default']) widget.textChanged.connect(set_value) if widget: widget.setObjectName(name) self.option_widgets[name] = widget page_layout.addRow(pretty_name, widget) if details['description']: label = QtWidgets.QLabel(page_widget) label.setText(details['description']) page_layout.addRow('', label) self.render_options_stack.addWidget(page_widget) def load_settings(self): """Load the settings""" # Editor settings self.settings.beginGroup('editor') if self.settings.contains('font'): value = QtGui.QFont(self.settings.value('font')) self.editor_values['font'] = value self.editor_font_combobox.setCurrentFont(value) if self.settings.contains('size'): value = int(self.settings.value('size')) self.editor_values['size'] = value self.editor_size.setValue(value) self.settings.endGroup() # Renderer settings self.settings.beginGroup('render') for name, details in get_options().items(): if self.settings.contains(name): value = self.settings.value(name) if value is None: continue if details['type'] is int: value = int(value) self.option_widgets[name].setValue(value) elif details['type'] is bool: value = coerce_bool(value) self.option_widgets[name].setChecked(value) elif 'font' in name: value = QtGui.QFont(value) self.option_widgets[name].setCurrentFont(value) else: self.option_widgets[name].setText(value) self.option_values[name] = value self.settings.endGroup() # PDF settings self.settings.beginGroup('export') if self.settings.contains('page_size'): value = int(self.settings.value('page_size')) self.export_values['page_size'] = value self.export_size_combobox.setCurrentText(SUPPORTED_SIZES[value]) if self.settings.contains('unit'): value = int(self.settings.value('unit')) self.export_values['unit'] = value self.export_unit_combobox.setCurrentText(SUPPORTED_UNITS[value]) if self.settings.contains('orientation'): value = int(self.settings.value('orientation')) self.export_values['orientation'] = value self.export_orientation_portrait_radio.setChecked(value == QtGui.QPageLayout.Portrait) margins = [ ('margin_left', self.export_margin_left_spinbox), ('margin_right', self.export_margin_right_spinbox), ('margin_top', self.export_margin_top_spinbox), ('margin_bottom', self.export_margin_bottom_spinbox), ] for name, spinbox in margins: if self.settings.contains(name): value = float(self.settings.value(name)) self.export_values[name] = value spinbox.setValue(value) self.settings.endGroup() def save_settings(self): """Save the settings""" # Editor settings self.settings.beginGroup('editor') for name, value in self.editor_values.items(): if isinstance(value, QtGui.QFont): value = value.family() self.settings.setValue(name, value) self.settings.endGroup() # Renderer settings self.settings.beginGroup('render') for name, value in self.option_values.items(): if isinstance(value, QtGui.QFont): value = value.family() self.settings.setValue(name, value) self.settings.endGroup() # Export settings self.settings.beginGroup('export') for name, value in self.export_values.items(): self.settings.setValue(name, value) self.settings.endGroup() def exec(self): """Execute the dialog""" self.load_settings() return super().exec() def accept(self): """The user clicked the "OK" button""" self.save_settings() return super().accept() def on_export_unit_changed(self, value): self._set_export_value('unit', self.export_unit_combobox.itemData(value)) new_suffix = self.export_unit_combobox.itemText(value) for spinbox in [self.export_margin_bottom_spinbox, self.export_margin_left_spinbox, self.export_margin_right_spinbox, self.export_margin_top_spinbox]: old_suffix = spinbox.suffix() spinbox.setSuffix(new_suffix) spinbox.setValue(convert_units(spinbox.value(), old_suffix, new_suffix)) if new_suffix == 'in': spinbox.setSingleStep(0.1) else: spinbox.setSingleStep(1.0)