class ShortcutConfig(QWidget): def __init__(self, model, parent=None): QWidget.__init__(self, parent) self._layout = QHBoxLayout() self.setLayout(self._layout) self.view = QListView(self) self._layout.addWidget(self.view) self.view.setModel(model) self.delegate = Delegate() self.view.setItemDelegate(self.delegate) self.delegate.sizeHintChanged.connect(self.scrollTo, type=Qt.QueuedConnection) def scrollTo(self, index): self.view.scrollTo(index, self.view.EnsureVisible) @property def is_editing(self): return self.view.state() == self.view.EditingState
def create_filterable_names_list(names, filter_text=None, parent=None, model=NamesModel): nl = QListView(parent) nl.m = m = model(names, parent=nl) m.filtered.connect(lambda all_items: nl.scrollTo(m.index(0))) nl.setModel(m) if model is NamesModel: nl.d = NamesDelegate(nl) nl.setItemDelegate(nl.d) f = QLineEdit(parent) f.setPlaceholderText(filter_text or '') f.textEdited.connect(m.filter) return nl, f
class EditRules(QWidget): # {{{ changed = pyqtSignal() def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QGridLayout(self) self.setLayout(l) self.enabled = c = QCheckBox(self) l.addWidget(c, l.rowCount(), 0, 1, 2) c.setVisible(False) c.stateChanged.connect(self.changed) self.l1 = l1 = QLabel('') l1.setWordWrap(True) l.addWidget(l1, l.rowCount(), 0, 1, 2) self.add_button = QPushButton(QIcon(I('plus.png')), _('&Add rule'), self) self.remove_button = QPushButton(QIcon(I('minus.png')), _('&Remove rule(s)'), self) self.add_button.clicked.connect(self.add_rule) self.remove_button.clicked.connect(self.remove_rule) l.addWidget(self.add_button, l.rowCount(), 0) l.addWidget(self.remove_button, l.rowCount() - 1, 1) self.g = g = QGridLayout() self.rules_view = QListView(self) self.rules_view.doubleClicked.connect(self.edit_rule) self.rules_view.setSelectionMode(self.rules_view.ExtendedSelection) self.rules_view.setAlternatingRowColors(True) self.rtfd = RichTextDelegate(parent=self.rules_view, max_width=400) self.rules_view.setItemDelegate(self.rtfd) g.addWidget(self.rules_view, 0, 0, 2, 1) self.up_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) b.setToolTip(_('Move the selected rule up')) b.clicked.connect(self.move_up) g.addWidget(b, 0, 1, 1, 1, Qt.AlignTop) self.down_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) b.setToolTip(_('Move the selected rule down')) b.clicked.connect(self.move_down) g.addWidget(b, 1, 1, 1, 1, Qt.AlignBottom) l.addLayout(g, l.rowCount(), 0, 1, 2) l.setRowStretch(l.rowCount() - 1, 10) self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')), _('Add ad&vanced rule'), self) b.clicked.connect(self.add_advanced) self.hb = hb = QHBoxLayout() l.addLayout(hb, l.rowCount(), 0, 1, 2) hb.addWidget(b) hb.addStretch(10) self.export_button = b = QPushButton(_('E&xport'), self) b.clicked.connect(self.export_rules) b.setToolTip(_('Export these rules to a file')) hb.addWidget(b) self.import_button = b = QPushButton(_('&Import'), self) b.setToolTip(_('Import rules from a file')) b.clicked.connect(self.import_rules) hb.addWidget(b) def initialize(self, fm, prefs, mi, pref_name): self.pref_name = pref_name self.model = RulesModel(prefs, fm, self.pref_name) self.rules_view.setModel(self.model) self.fm = fm self.mi = mi if pref_name == 'column_color_rules': text = _( 'You can control the color of columns in the' ' book list by creating "rules" that tell calibre' ' what color to use. Click the "Add rule" button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'column_icon_rules': text = _( 'You can add icons to columns in the' ' book list by creating "rules" that tell calibre' ' what icon to use. Click the "Add rule" button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'cover_grid_icon_rules': text = _('You can add emblems (small icons) that are displayed on the side of covers' ' in the Cover grid by creating "rules" that tell calibre' ' what image to use. Click the "Add rule" button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') self.enabled.setVisible(True) self.enabled.setChecked(gprefs['show_emblems']) self.enabled.setText(_('Show &emblems next to the covers')) self.enabled.stateChanged.connect(self.enabled_toggled) self.enabled.setToolTip(_( 'If checked, you can tell calibre to display icons of your choosing' ' next to the covers shown in the Cover grid, controlled by the' ' metadata of the book.')) self.enabled_toggled() self.l1.setText('<p>'+ text) def enabled_toggled(self): enabled = self.enabled.isChecked() for x in ('add_advanced_button', 'rules_view', 'up_button', 'down_button', 'add_button', 'remove_button'): getattr(self, x).setEnabled(enabled) def add_rule(self): d = RuleEditor(self.model.fm, self.pref_name) d.add_blank_condition() if d.exec_() == d.Accepted: kind, col, r = d.rule if kind and r and col: idx = self.model.add_rule(kind, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def add_advanced(self): if self.pref_name == 'column_color_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='') if td.exec_() == td.Accepted: col, r = td.rule if r and col: idx = self.model.add_rule('color', col, r) self.rules_view.scrollTo(idx) self.changed.emit() else: if self.pref_name == 'cover_grid_icon_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, doing_emblem=True) else: td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, icon_field_key='') if td.exec_() == td.Accepted: typ, col, r = td.rule if typ and r and col: idx = self.model.add_rule(typ, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def edit_rule(self, index): try: kind, col, rule = self.model.data(index, Qt.UserRole) except: return if isinstance(rule, Rule): d = RuleEditor(self.model.fm, self.pref_name) d.apply_rule(kind, col, rule) elif self.pref_name == 'column_color_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, color_field=col) elif self.pref_name == 'cover_grid_icon_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, doing_emblem=True) else: d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, icon_field_key=col, icon_rule_kind=kind) if d.exec_() == d.Accepted: if len(d.rule) == 2: # Convert template dialog rules to a triple d.rule = ('color', d.rule[0], d.rule[1]) kind, col, r = d.rule if kind and r is not None and col: self.model.replace_rule(index, kind, col, r) self.rules_view.scrollTo(index) self.changed.emit() def get_selected_row(self, txt): sm = self.rules_view.selectionModel() rows = list(sm.selectedRows()) if not rows: error_dialog(self, _('No rule selected'), _('No rule selected for %s.')%txt, show=True) return None return sorted(rows, reverse=True) def remove_rule(self): rows = self.get_selected_row(_('removal')) if rows is not None: for row in rows: self.model.remove_rule(row) self.changed.emit() def move_up(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, -1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def move_down(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, 1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def clear(self): self.model.clear() self.changed.emit() def commit(self, prefs): self.model.commit(prefs) if self.pref_name == 'cover_grid_icon_rules': gprefs['show_emblems'] = self.enabled.isChecked() def export_rules(self): path = choose_save_file(self, 'export-coloring-rules', _('Choose file to export to'), filters=[(_('Rules'), ['rules'])], all_files=False, initial_filename=self.pref_name + '.rules') if path: rules = { 'version': self.model.EXIM_VERSION, 'type': self.model.pref_name, 'rules': self.model.rules_as_list(for_export=True) } with lopen(path, 'wb') as f: f.write(json.dumps(rules, indent=2)) def import_rules(self): files = choose_files(self, 'import-coloring-rules', _('Choose file to import from'), filters=[(_('Rules'), ['rules'])], all_files=False, select_only_single_file=True) if files: with lopen(files[0], 'rb') as f: raw = f.read() try: rules = json.loads(raw) if rules['version'] != self.model.EXIM_VERSION: raise ValueError('Unsupported rules version: {}'.format(rules['version'])) if rules['type'] != self.pref_name: raise ValueError('Rules are not of the correct type') rules = list(rules['rules']) except Exception as e: return error_dialog(self, _('No valid rules found'), _( 'No valid rules were found in {}.').format(files[0]), det_msg=as_unicode(e), show=True) self.model.import_rules(rules) self.changed.emit()
class EditRules(QWidget): # {{{ changed = pyqtSignal() def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QGridLayout(self) self.setLayout(l) self.enabled = c = QCheckBox(self) l.addWidget(c, l.rowCount(), 0, 1, 2) c.setVisible(False) c.stateChanged.connect(self.changed) self.l1 = l1 = QLabel('') l1.setWordWrap(True) l.addWidget(l1, l.rowCount(), 0, 1, 2) self.add_button = QPushButton(QIcon(I('plus.png')), _('Add Rule'), self) self.remove_button = QPushButton(QIcon(I('minus.png')), _('Remove Rule(s)'), self) self.add_button.clicked.connect(self.add_rule) self.remove_button.clicked.connect(self.remove_rule) l.addWidget(self.add_button, l.rowCount(), 0) l.addWidget(self.remove_button, l.rowCount() - 1, 1) self.g = g = QGridLayout() self.rules_view = QListView(self) self.rules_view.doubleClicked.connect(self.edit_rule) self.rules_view.setSelectionMode(self.rules_view.ExtendedSelection) self.rules_view.setAlternatingRowColors(True) self.rtfd = RichTextDelegate(parent=self.rules_view, max_width=400) self.rules_view.setItemDelegate(self.rtfd) g.addWidget(self.rules_view, 0, 0, 2, 1) self.up_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) b.setToolTip(_('Move the selected rule up')) b.clicked.connect(self.move_up) g.addWidget(b, 0, 1, 1, 1, Qt.AlignTop) self.down_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) b.setToolTip(_('Move the selected rule down')) b.clicked.connect(self.move_down) g.addWidget(b, 1, 1, 1, 1, Qt.AlignBottom) l.addLayout(g, l.rowCount(), 0, 1, 2) l.setRowStretch(l.rowCount() - 1, 10) self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')), _('Add Advanced Rule'), self) b.clicked.connect(self.add_advanced) l.addWidget(b, l.rowCount(), 0, 1, 2) def initialize(self, fm, prefs, mi, pref_name): self.pref_name = pref_name self.model = RulesModel(prefs, fm, self.pref_name) self.rules_view.setModel(self.model) self.fm = fm self.mi = mi if pref_name == 'column_color_rules': text = _( 'You can control the color of columns in the' ' book list by creating "rules" that tell calibre' ' what color to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'column_icon_rules': text = _( 'You can add icons to columns in the' ' book list by creating "rules" that tell calibre' ' what icon to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'cover_grid_icon_rules': text = _( 'You can add emblems (small icons) that are displayed on the side of covers' ' in the cover grid by creating "rules" that tell calibre' ' what image to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') self.enabled.setVisible(True) self.enabled.setChecked(gprefs['show_emblems']) self.enabled.setText(_('Show &emblems next to the covers')) self.enabled.stateChanged.connect(self.enabled_toggled) self.enabled.setToolTip( _('If checked, you can tell calibre to display icons of your choosing' ' next to the covers shown in the cover grid, controlled by the' ' metadata of the book.')) self.enabled_toggled() self.l1.setText('<p>' + text) def enabled_toggled(self): enabled = self.enabled.isChecked() for x in ('add_advanced_button', 'rules_view', 'up_button', 'down_button', 'add_button', 'remove_button'): getattr(self, x).setEnabled(enabled) def add_rule(self): d = RuleEditor(self.model.fm, self.pref_name) d.add_blank_condition() if d.exec_() == d.Accepted: kind, col, r = d.rule if kind and r and col: idx = self.model.add_rule(kind, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def add_advanced(self): if self.pref_name == 'column_color_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='') if td.exec_() == td.Accepted: col, r = td.rule if r and col: idx = self.model.add_rule('color', col, r) self.rules_view.scrollTo(idx) self.changed.emit() else: if self.pref_name == 'cover_grid_icon_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, doing_emblem=True) else: td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, icon_field_key='') if td.exec_() == td.Accepted: typ, col, r = td.rule if typ and r and col: idx = self.model.add_rule(typ, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def edit_rule(self, index): try: kind, col, rule = self.model.data(index, Qt.UserRole) except: return if isinstance(rule, Rule): d = RuleEditor(self.model.fm, self.pref_name) d.apply_rule(kind, col, rule) elif self.pref_name == 'column_color_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, color_field=col) elif self.pref_name == 'cover_grid_icon_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, doing_emblem=True) else: d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, icon_field_key=col, icon_rule_kind=kind) if d.exec_() == d.Accepted: if len(d.rule) == 2: # Convert template dialog rules to a triple d.rule = ('color', d.rule[0], d.rule[1]) kind, col, r = d.rule if kind and r is not None and col: self.model.replace_rule(index, kind, col, r) self.rules_view.scrollTo(index) self.changed.emit() def get_selected_row(self, txt): sm = self.rules_view.selectionModel() rows = list(sm.selectedRows()) if not rows: error_dialog(self, _('No rule selected'), _('No rule selected for %s.') % txt, show=True) return None return sorted(rows, reverse=True) def remove_rule(self): rows = self.get_selected_row(_('removal')) if rows is not None: for row in rows: self.model.remove_rule(row) self.changed.emit() def move_up(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, -1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def move_down(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, 1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def clear(self): self.model.clear() self.changed.emit() def commit(self, prefs): self.model.commit(prefs) if self.pref_name == 'cover_grid_icon_rules': gprefs['show_emblems'] = self.enabled.isChecked()
class EditRules(QWidget): # {{{ changed = pyqtSignal() def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QGridLayout(self) self.setLayout(l) self.enabled = c = QCheckBox(self) l.addWidget(c, l.rowCount(), 0, 1, 2) c.setVisible(False) c.stateChanged.connect(self.changed) self.l1 = l1 = QLabel('') l1.setWordWrap(True) l.addWidget(l1, l.rowCount(), 0, 1, 2) self.add_button = QPushButton(QIcon(I('plus.png')), _('Add Rule'), self) self.remove_button = QPushButton(QIcon(I('minus.png')), _('Remove Rule'), self) self.add_button.clicked.connect(self.add_rule) self.remove_button.clicked.connect(self.remove_rule) l.addWidget(self.add_button, l.rowCount(), 0) l.addWidget(self.remove_button, l.rowCount() - 1, 1) self.g = g = QGridLayout() self.rules_view = QListView(self) self.rules_view.doubleClicked.connect(self.edit_rule) self.rules_view.setSelectionMode(self.rules_view.SingleSelection) self.rules_view.setAlternatingRowColors(True) self.rtfd = RichTextDelegate(parent=self.rules_view, max_width=400) self.rules_view.setItemDelegate(self.rtfd) g.addWidget(self.rules_view, 0, 0, 2, 1) self.up_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) b.setToolTip(_('Move the selected rule up')) b.clicked.connect(self.move_up) g.addWidget(b, 0, 1, 1, 1, Qt.AlignTop) self.down_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) b.setToolTip(_('Move the selected rule down')) b.clicked.connect(self.move_down) g.addWidget(b, 1, 1, 1, 1, Qt.AlignBottom) l.addLayout(g, l.rowCount(), 0, 1, 2) l.setRowStretch(l.rowCount() - 1, 10) self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')), _('Add Advanced Rule'), self) b.clicked.connect(self.add_advanced) l.addWidget(b, l.rowCount(), 0, 1, 2) def initialize(self, fm, prefs, mi, pref_name): self.pref_name = pref_name self.model = RulesModel(prefs, fm, self.pref_name) self.rules_view.setModel(self.model) self.fm = fm self.mi = mi if pref_name == 'column_color_rules': text = _( 'You can control the color of columns in the' ' book list by creating "rules" that tell calibre' ' what color to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'column_icon_rules': text = _( 'You can add icons to columns in the' ' book list by creating "rules" that tell calibre' ' what icon to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') elif pref_name == 'cover_grid_icon_rules': text = _('You can add emblems (small icons) that are displayed on the side of covers' ' in the cover grid by creating "rules" that tell calibre' ' what image to use. Click the Add Rule button below' ' to get started.<p>You can <b>change an existing rule</b> by' ' double clicking it.') self.enabled.setVisible(True) self.enabled.setChecked(gprefs['show_emblems']) self.enabled.setText(_('Show &emblems next to the covers')) self.enabled.stateChanged.connect(self.enabled_toggled) self.enabled.setToolTip(_( 'If checked, you can tell calibre to displays icons of your choosing' ' next to the covers shown in the cover grid, controlled by the' ' metadata of the book.')) self.enabled_toggled() self.l1.setText('<p>'+ text) def enabled_toggled(self): enabled = self.enabled.isChecked() for x in ('add_advanced_button', 'rules_view', 'up_button', 'down_button', 'add_button', 'remove_button'): getattr(self, x).setEnabled(enabled) def add_rule(self): d = RuleEditor(self.model.fm, self.pref_name) d.add_blank_condition() if d.exec_() == d.Accepted: kind, col, r = d.rule if kind and r and col: idx = self.model.add_rule(kind, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def add_advanced(self): if self.pref_name == 'column_color_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='') if td.exec_() == td.Accepted: col, r = td.rule if r and col: idx = self.model.add_rule('color', col, r) self.rules_view.scrollTo(idx) self.changed.emit() else: if self.pref_name == 'cover_grid_icon_rules': td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, doing_emblem=True) else: td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, icon_field_key='') if td.exec_() == td.Accepted: typ, col, r = td.rule if typ and r and col: idx = self.model.add_rule(typ, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def edit_rule(self, index): try: kind, col, rule = self.model.data(index, Qt.UserRole) except: return if isinstance(rule, Rule): d = RuleEditor(self.model.fm, self.pref_name) d.apply_rule(kind, col, rule) elif self.pref_name == 'column_color_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, color_field=col) elif self.pref_name == 'cover_grid_icon_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, doing_emblem=True) else: d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, icon_field_key=col, icon_rule_kind=kind) if d.exec_() == d.Accepted: if len(d.rule) == 2: # Convert template dialog rules to a triple d.rule = ('color', d.rule[0], d.rule[1]) kind, col, r = d.rule if kind and r is not None and col: self.model.replace_rule(index, kind, col, r) self.rules_view.scrollTo(index) self.changed.emit() def get_selected_row(self, txt): sm = self.rules_view.selectionModel() rows = list(sm.selectedRows()) if not rows: error_dialog(self, _('No rule selected'), _('No rule selected for %s.')%txt, show=True) return None return rows[0] def remove_rule(self): row = self.get_selected_row(_('removal')) if row is not None: self.model.remove_rule(row) self.changed.emit() def move_up(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, -1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def move_down(self): idx = self.rules_view.currentIndex() if idx.isValid(): idx = self.model.move(idx, 1) if idx is not None: sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) self.changed.emit() def clear(self): self.model.clear() self.changed.emit() def commit(self, prefs): self.model.commit(prefs) if self.pref_name == 'cover_grid_icon_rules': gprefs['show_emblems'] = self.enabled.isChecked()