def genesis(self): md = self.qaction.menu() cm = partial(self.create_menu_action, md) cm('individual', _('Edit metadata individually'), icon=self.qaction.icon(), triggered=partial(self.edit_metadata, False, bulk=False)) cm('bulk', _('Edit metadata in bulk'), triggered=partial(self.edit_metadata, False, bulk=True)) md.addSeparator() cm('download', _('Download metadata and covers'), triggered=partial(self.download_metadata, ids=None), shortcut='Ctrl+D') self.metadata_menu = md self.metamerge_menu = mb = QMenu() cm2 = partial(self.create_menu_action, mb) cm2('merge delete', _('Merge into first selected book - delete others'), triggered=self.merge_books) mb.addSeparator() cm2('merge keep', _('Merge into first selected book - keep others'), triggered=partial(self.merge_books, safe_merge=True), shortcut='Alt+M') mb.addSeparator() cm2('merge formats', _('Merge only formats into first selected book - delete others'), triggered=partial(self.merge_books, merge_only_formats=True), shortcut='Alt+Shift+M') self.merge_menu = mb md.addSeparator() self.action_copy = cm('copy', _('Copy metadata'), icon='edit-copy.png', triggered=self.copy_metadata) self.action_paset = cm('paste', _('Paste metadata'), icon='edit-paste.png', triggered=self.paste_metadata) self.action_merge = cm('merge', _('Merge book records'), icon='merge_books.png', shortcut=_('M'), triggered=self.merge_books) self.action_merge.setMenu(mb) self.qaction.triggered.connect(self.edit_metadata) ac = QAction(_('Copy URL to show book in calibre'), self.gui) ac.setToolTip(_('Copy URLs to show the currently selected books in calibre, to the system clipboard')) ac.triggered.connect(self.copy_show_link) self.gui.addAction(ac) self.gui.keyboard.register_shortcut( self.unique_name + ' - ' + 'copy_show_book', ac.text(), description=ac.toolTip(), action=ac, group=self.action_spec[0]) ac = QAction(_('Copy URL to open book in calibre'), self.gui) ac.triggered.connect(self.copy_view_link) ac.setToolTip(_('Copy URLs to open the currently selected books in calibre, to the system clipboard')) self.gui.addAction(ac) self.gui.keyboard.register_shortcut( self.unique_name + ' - ' + 'copy_view_book', ac.text(), description=ac.toolTip(), action=ac, group=self.action_spec[0])
class SearchBarButton(LayoutButton): # {{{ def __init__(self, gui): sc = 'Alt+Shift+F' LayoutButton.__init__(self, I('search.png'), _('Search bar'), parent=gui, shortcut=sc) self.set_state_to_hide() self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self) gui.addAction(self.action_toggle) gui.keyboard.register_shortcut('search bar toggle' + self.label, str(self.action_toggle.text()), default_keys=(sc,), action=self.action_toggle) self.action_toggle.triggered.connect(self.toggle) self.action_toggle.changed.connect(self.update_shortcut) self.toggled.connect(self.update_state) def update_state(self, checked): if checked: self.set_state_to_hide() else: self.set_state_to_show() def save_state(self): gprefs['search bar visible'] = bool(self.isChecked()) def restore_state(self): self.setChecked(bool(gprefs.get('search bar visible', True)))
class GridViewButton(LayoutButton): # {{{ def __init__(self, gui): sc = 'Alt+Shift+G' LayoutButton.__init__(self, I('grid.png'), _('Cover grid'), parent=gui, shortcut=sc) self.set_state_to_show() self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self) gui.addAction(self.action_toggle) gui.keyboard.register_shortcut('grid view toggle' + self.label, str(self.action_toggle.text()), default_keys=(sc,), action=self.action_toggle) self.action_toggle.triggered.connect(self.toggle) self.action_toggle.changed.connect(self.update_shortcut) self.toggled.connect(self.update_state) def update_state(self, checked): if checked: self.set_state_to_hide() else: self.set_state_to_show() def save_state(self): gprefs['grid view visible'] = bool(self.isChecked()) def restore_state(self): if gprefs.get('grid view visible', False): self.toggle()
class Splitter(QSplitter): state_changed = pyqtSignal(object) reapply_sizes = pyqtSignal(object) def __init__(self, name, label, icon, initial_show=True, initial_side_size=120, connect_button=True, orientation=Qt.Orientation.Horizontal, side_index=0, parent=None, shortcut=None, hide_handle_on_single_panel=True): QSplitter.__init__(self, parent) self.reapply_sizes.connect(self.setSizes, type=Qt.ConnectionType.QueuedConnection) self.hide_handle_on_single_panel = hide_handle_on_single_panel if hide_handle_on_single_panel: self.state_changed.connect(self.update_handle_width) self.original_handle_width = self.handleWidth() self.resize_timer = QTimer(self) self.resize_timer.setSingleShot(True) self.desired_side_size = initial_side_size self.desired_show = initial_show self.resize_timer.setInterval(5) self.resize_timer.timeout.connect(self.do_resize) self.setOrientation(orientation) self.side_index = side_index self._name = name self.label = label self.initial_side_size = initial_side_size self.initial_show = initial_show self.splitterMoved.connect(self.splitter_moved, type=Qt.ConnectionType.QueuedConnection) self.button = LayoutButton(icon, label, self, shortcut=shortcut) if connect_button: self.button.clicked.connect(self.double_clicked) if shortcut is not None: self.action_toggle = QAction(QIcon(icon), _('Toggle') + ' ' + label, self) self.action_toggle.changed.connect(self.update_shortcut) self.action_toggle.triggered.connect(self.toggle_triggered) if parent is not None: parent.addAction(self.action_toggle) if hasattr(parent, 'keyboard'): parent.keyboard.register_shortcut( 'splitter %s %s' % (name, label), str(self.action_toggle.text()), default_keys=(shortcut, ), action=self.action_toggle) else: self.action_toggle.setShortcut(shortcut) else: self.action_toggle.setShortcut(shortcut) def update_shortcut(self): self.button.update_shortcut(self.action_toggle) def toggle_triggered(self, *args): self.toggle_side_pane() def createHandle(self): return SplitterHandle(self.orientation(), self) def initialize(self): for i in range(self.count()): h = self.handle(i) if h is not None: h.splitter_moved() self.state_changed.emit(not self.is_side_index_hidden) def splitter_moved(self, *args): self.desired_side_size = self.side_index_size self.state_changed.emit(not self.is_side_index_hidden) def update_handle_width(self, not_one_panel): self.setHandleWidth(self.original_handle_width if not_one_panel else 0) @property def is_side_index_hidden(self): sizes = list(self.sizes()) try: return sizes[self.side_index] == 0 except IndexError: return True @property def save_name(self): ori = 'horizontal' if self.orientation() == Qt.Orientation.Horizontal \ else 'vertical' return self._name + '_' + ori def print_sizes(self): if self.count() > 1: print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ') print(list(self.sizes())[self.other_index]) @property def side_index_size(self): if self.count() < 2: return 0 return self.sizes()[self.side_index] @side_index_size.setter def side_index_size(self, val): if self.count() < 2: return side_index_hidden = self.is_side_index_hidden if val == 0 and not side_index_hidden: self.save_state() sizes = list(self.sizes()) for i in range(len(sizes)): sizes[i] = val if i == self.side_index else 10 self.setSizes(sizes) sizes = list(self.sizes()) total = sum(sizes) total_needs_adjustment = self.hide_handle_on_single_panel and side_index_hidden if total_needs_adjustment: total -= self.original_handle_width for i in range(len(sizes)): sizes[i] = val if i == self.side_index else total - val self.setSizes(sizes) self.initialize() if total_needs_adjustment: # the handle visibility and therefore size distribution will change # when the event loop ticks self.reapply_sizes.emit(sizes) def do_resize(self, *args): orig = self.desired_side_size QSplitter.resizeEvent(self, self._resize_ev) if orig > 20 and self.desired_show: c = 0 while abs(self.side_index_size - orig) > 10 and c < 5: self.apply_state(self.get_state(), save_desired=False) c += 1 def resizeEvent(self, ev): if self.resize_timer.isActive(): self.resize_timer.stop() self._resize_ev = ev self.resize_timer.start() def get_state(self): if self.count() < 2: return (False, 200) return (self.desired_show, self.desired_side_size) def apply_state(self, state, save_desired=True): if state[0]: self.side_index_size = state[1] if save_desired: self.desired_side_size = self.side_index_size else: self.side_index_size = 0 self.desired_show = state[0] def default_state(self): return (self.initial_show, self.initial_side_size) # Public API {{{ def update_desired_state(self): self.desired_show = not self.is_side_index_hidden def save_state(self): if self.count() > 1: gprefs[self.save_name + '_state'] = self.get_state() @property def other_index(self): return (self.side_index + 1) % 2 def restore_state(self): if self.count() > 1: state = gprefs.get(self.save_name + '_state', self.default_state()) self.apply_state(state, save_desired=False) self.desired_side_size = state[1] def toggle_side_pane(self, hide=None): if hide is None: action = 'show' if self.is_side_index_hidden else 'hide' else: action = 'hide' if hide else 'show' getattr(self, action + '_side_pane')() def show_side_pane(self): if self.count() < 2 or not self.is_side_index_hidden: return if self.desired_side_size == 0: self.desired_side_size = self.initial_side_size self.apply_state((True, self.desired_side_size)) def hide_side_pane(self): if self.count() < 2 or self.is_side_index_hidden: return self.apply_state((False, self.desired_side_size)) def double_clicked(self, *args): self.toggle_side_pane()
def create_action(self, spec=None, attr='qaction', shortcut_name=None, persist_shortcut=False): if spec is None: spec = self.action_spec text, icon, tooltip, shortcut = spec if icon is not None: action = QAction(QIcon(I(icon)), text, self.gui) else: action = QAction(text, self.gui) if attr == 'qaction': if hasattr(self.action_menu_clone_qaction, 'rstrip'): mt = str(self.action_menu_clone_qaction) else: mt = action.text() self.menuless_qaction = ma = QAction(action.icon(), mt, self.gui) ma.triggered.connect(action.trigger) for a in ((action, ma) if attr == 'qaction' else (action, )): a.setAutoRepeat(self.auto_repeat) text = tooltip if tooltip else text a.setToolTip(text) a.setStatusTip(text) a.setWhatsThis(text) shortcut_action = action desc = tooltip if tooltip else None if attr == 'qaction': shortcut_action = ma if shortcut is not None: keys = ((shortcut, ) if isinstance(shortcut, string_or_bytes) else tuple(shortcut)) if shortcut_name is None and spec[0]: shortcut_name = str(spec[0]) if shortcut_name and self.action_spec[0] and not ( attr == 'qaction' and self.popup_type == QToolButton.ToolButtonPopupMode.InstantPopup): try: self.gui.keyboard.register_shortcut( self.unique_name + ' - ' + attr, shortcut_name, default_keys=keys, action=shortcut_action, description=desc, group=self.action_spec[0], persist_shortcut=persist_shortcut) except NameConflict as e: try: prints(str(e)) except: pass shortcut_action.setShortcuts([ QKeySequence(key, QKeySequence.SequenceFormat.PortableText) for key in keys ]) else: self.shortcut_action_for_context_menu = shortcut_action if ismacos: # In Qt 5 keyboard shortcuts dont work unless the # action is explicitly added to the main window self.gui.addAction(shortcut_action) if attr is not None: setattr(self, attr, action) if attr == 'qaction' and self.action_add_menu: menu = QMenu() action.setMenu(menu) if self.action_menu_clone_qaction: menu.addAction(self.menuless_qaction) return action