%PDF- %PDF-
Direktori : /lib/calibre/calibre/gui2/dialogs/ |
Current File : //lib/calibre/calibre/gui2/dialogs/multisort.py |
#!/usr/bin/env python3 # License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> from qt.core import ( QAbstractItemView, QDialogButtonBox, QInputDialog, QLabel, QListWidget, QListWidgetItem, QMenu, QSize, Qt, QVBoxLayout ) from calibre import prepare_string_for_xml from calibre.gui2 import error_dialog from calibre.gui2.actions.sort import SORT_HIDDEN_PREF from calibre.gui2.widgets2 import Dialog from calibre.utils.icu import primary_sort_key ascending_symbol = '⏷' descending_symbol = '⏶' class ChooseMultiSort(Dialog): def __init__(self, db, is_device_connected=False, parent=None, hidden_pref=SORT_HIDDEN_PREF): self.db = db.new_api self.hidden_fields = set(self.db.pref(SORT_HIDDEN_PREF, default=()) or ()) if not is_device_connected: self.hidden_fields.add('ondevice') fm = self.db.field_metadata self.key_map = fm.ui_sortable_field_keys().copy() self.name_map = {v:k for k, v in self.key_map.items()} self.all_names = sorted(self.name_map, key=primary_sort_key) self.sort_order_map = dict.fromkeys(self.key_map, True) super().__init__(_('Sort by multiple columns'), 'multisort-chooser', parent=parent) def sizeHint(self): return QSize(600, 400) def setup_ui(self): self.vl = vl = QVBoxLayout(self) self.la = la = QLabel(_( 'Pick multiple columns to sort by. Drag and drop to re-arrange. Higher columns are more important.' ' Ascending or descending order can be toggled by clicking the column name at the bottom' ' of this dialog, after having selected it.')) la.setWordWrap(True) vl.addWidget(la) self.order_label = la = QLabel('\xa0') la.setTextFormat(Qt.TextFormat.RichText) la.setWordWrap(True) la.linkActivated.connect(self.link_activated) self.column_list = cl = QListWidget(self) vl.addWidget(cl) vl.addWidget(la) vl.addWidget(self.bb) for name in self.all_names: i = QListWidgetItem(cl) i.setText(name) i.setData(Qt.ItemDataRole.UserRole, self.name_map[name]) cl.addItem(i) i.setCheckState(Qt.CheckState.Unchecked) if self.name_map[name] in self.hidden_fields: i.setHidden(True) cl.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove) cl.currentRowChanged.connect(self.current_changed) cl.itemDoubleClicked.connect(self.item_double_clicked) cl.setCurrentRow(0) cl.itemChanged.connect(self.update_order_label) cl.model().rowsMoved.connect(self.update_order_label) self.clear_button = b = self.bb.addButton(_('&Clear'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Clear all selected columns')) b.setAutoDefault(False) b.clicked.connect(self.clear) self.save_button = b = self.bb.addButton(_('&Save'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Save this sort order for easy re-use')) b.clicked.connect(self.save) b.setAutoDefault(False) self.load_button = b = self.bb.addButton(_('&Load'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Load previously saved settings')) b.setAutoDefault(False) self.load_menu = QMenu(b) b.setMenu(self.load_menu) self.load_menu.aboutToShow.connect(self.populate_load_menu) def clear(self): for item in self.iteritems(): item.setCheckState(Qt.CheckState.Unchecked) self.column_list.sortItems() def item_double_clicked(self, item): cs = item.checkState() item.setCheckState(Qt.CheckState.Checked if cs == Qt.CheckState.Unchecked else Qt.CheckState.Unchecked) def current_changed(self): self.update_order_label() @property def current_sort_spec(self): ans = [] for item in self.iteritems(): if item.checkState() == Qt.CheckState.Checked: k = item.data(Qt.ItemDataRole.UserRole) ans.append((k, self.sort_order_map[k])) return ans def update_order_label(self): t = '' for i, (k, ascending) in enumerate(self.current_sort_spec): name = self.key_map[k] symbol = ascending_symbol if ascending else descending_symbol if i != 0: t += ' :: ' q = bytes.hex(k.encode('utf-8')) dname = prepare_string_for_xml(name).replace(" ", " ") t += f' <a href="{q}" style="text-decoration: none">{dname} {symbol}</a>' if t: t = _('Effective sort') + ': ' + t self.order_label.setText(t) def link_activated(self, url): key = bytes.fromhex(url).decode('utf-8') self.sort_order_map[key] ^= True self.update_order_label() def no_column_selected_error(self): return error_dialog(self, _('No sort selected'), _( 'You must select at least one column on which to sort'), show=True) def accept(self): if not self.current_sort_spec: return self.no_column_selected_error() super().accept() @property def saved_specs(self): return self.db.pref('saved_multisort_specs', {}).copy() @saved_specs.setter def saved_specs(self, val): self.db.set_pref('saved_multisort_specs', val.copy()) def save(self): spec = self.current_sort_spec if not spec: return self.no_column_selected_error() name, ok = QInputDialog.getText(self, _('Choose name'), _('Choose a name for these settings')) if ok: q = self.saved_specs q[name] = spec self.saved_specs = q def populate_load_menu(self): m = self.load_menu m.clear() specs = self.saved_specs if not specs: m.addAction(_('No saved sorts available')) return for name in sorted(specs, key=primary_sort_key): ac = m.addAction(name, self.load_spec) ac.setObjectName(name) m.addSeparator() m = m.addMenu(_('Remove saved sort')) for name in sorted(specs, key=primary_sort_key): ac = m.addAction(name, self.remove_spec) ac.setObjectName(name) def load_spec(self): name = self.sender().objectName() spec = self.saved_specs[name] self.apply_spec(spec) def remove_spec(self): name = self.sender().objectName() q = self.saved_specs if q.pop(name, None): self.saved_specs = q def iteritems(self): cl = self.column_list return (cl.item(i) for i in range(cl.count())) def apply_spec(self, spec): self.clear() cl = self.column_list imap = {item.data(Qt.ItemDataRole.UserRole): item for item in self.iteritems()} for key, ascending in reversed(spec): item = imap.get(key) if item is not None: item = cl.takeItem(cl.row(item)) cl.insertItem(0, item) self.sort_order_map[key] = ascending item.setCheckState(Qt.CheckState.Checked) if __name__ == '__main__': from calibre.gui2 import Application app = Application([]) from calibre.library import db d = ChooseMultiSort(db()) d.exec() print(d.current_sort_spec) del d del app