class LayoutMixin(object): # {{{ def __init__(self): if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'bd', 'cb') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'cb', 'bd') # }}} self.status_bar = StatusBar(self) stylename = unicode(self.style().objectName()) for x in button_order: button = getattr(self, x+'_splitter').button button.setIconSize(QSize(24, 24)) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) m = self.library_view.model() if m.rowCount(None) > 0: self.library_view.set_current_row(0) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) if self.cover_flow: self.cover_flow.dataChanged() def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) if self.cover_flow: self.cover_flow.dataChanged() def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state()
class LayoutMixin: # {{{ def __init__(self, *args, **kwargs): pass def init_layout_mixin(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book details'), I('book.png'), orientation=Qt.Orientation.Vertical, parent=self, side_index=1, shortcut='Shift+Alt+D') self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('sb', 'tb', 'bd', 'gv', 'cb', 'qv') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book details'), I('book.png'), initial_side_size=200, orientation=Qt.Orientation.Horizontal, parent=self, side_index=1, shortcut='Shift+Alt+D') self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('sb', 'tb', 'cb', 'gv', 'qv', 'bd') # }}} # This must use the base method to find the plugin because it hasn't # been fully initialized yet self.qv = find_plugin('Quickview') if self.qv and self.qv.actual_plugin_: self.qv = self.qv.actual_plugin_ self.status_bar = StatusBar(self) stylename = str(self.style().objectName()) self.grid_view_button = GridViewButton(self) self.search_bar_button = SearchBarButton(self) self.grid_view_button.toggled.connect(self.toggle_grid_view) self.search_bar_button.toggled.connect(self.toggle_search_bar) self.layout_buttons = [] for x in button_order: if hasattr(self, x + '_splitter'): button = getattr(self, x + '_splitter').button else: if x == 'gv': button = self.grid_view_button elif x == 'qv': if self.qv is None: continue button = self.qv.qv_button else: button = self.search_bar_button self.layout_buttons.append(button) button.setVisible(False) if ismacos and stylename != 'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) if gprefs['show_layout_buttons']: for b in self.layout_buttons: b.setVisible(True) self.status_bar.addPermanentWidget(b) else: self.layout_button = b = QToolButton(self) b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor) b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) b.setText(_('Layout')), b.setIcon(QIcon(I('config.png'))) b.setMenu(LayoutMenu(self)) b.setToolTip(_( 'Show and hide various parts of the calibre main window')) self.status_bar.addPermanentWidget(b) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.ConnectionType.QueuedConnection) self.book_details.open_cover_with.connect(self.bd_open_cover_with, type=Qt.ConnectionType.QueuedConnection) self.book_details.open_fmt_with.connect(self.bd_open_fmt_with, type=Qt.ConnectionType.QueuedConnection) self.book_details.edit_book.connect(self.bd_edit_book, type=Qt.ConnectionType.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.ConnectionType.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.ConnectionType.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) self.book_details.search_requested.connect(self.set_search_string_with_append) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.remove_metadata_item.connect( self.iactions['Edit Metadata'].remove_metadata_item) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.set_cover_from_format.connect( self.iactions['Edit Metadata'].set_cover_from_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.ConnectionType.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_category.connect(self.manage_category_triggerred) self.book_details.find_in_tag_browser.connect(self.find_in_tag_browser_triggered) self.book_details.edit_identifiers.connect(self.edit_identifiers_triggerred) self.book_details.compare_specific_format.connect(self.compare_format) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.FocusReason.OtherFocusReason) def set_search_string_with_append(self, expression, append=''): current = self.search.text().strip() if append: expr = f'{current} {append} {expression}' if current else expression else: expr = expression self.search.set_search_string(expr) def edit_identifiers_triggerred(self): book_id = self.library_view.current_book db = self.current_db.new_api identifiers = db.field_for('identifiers', book_id, default_value={}) from calibre.gui2.metadata.basic_widgets import Identifiers d = Identifiers(identifiers, self) if d.exec() == QDialog.DialogCode.Accepted: identifiers = d.get_identifiers() db.set_field('identifiers', {book_id: identifiers}) self.iactions['Edit Metadata'].refresh_books_after_metadata_edit({book_id}) def manage_category_triggerred(self, field, value): if field and value: if field == 'authors': self.do_author_sort_edit(self, value, select_sort=False, select_link=False, lookup_author=True) elif field: self.do_tags_list_edit(value, field) def find_in_tag_browser_triggered(self, field, value): if field and value: tb = self.stack.tb_widget tb.set_focus_to_find_box() tb.item_search.lineEdit().setText(field + ':=' + value) tb.do_find() def toggle_grid_view(self, show): self.library_view.alternate_views.show_view('grid' if show else None) self.sort_sep.setVisible(show) self.sort_button.setVisible(show) def toggle_search_bar(self, show): self.search_bar.setVisible(show) if show: self.search.setFocus(Qt.FocusReason.OtherFocusReason) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) self.refresh_cover_browser() def bd_open_cover_with(self, book_id, entry): cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__') if cpath: if entry is None: open_local_file(cpath) return from calibre.gui2.open_with import run_program run_program(entry, cpath, self) def bd_open_fmt_with(self, book_id, fmt, entry): path = self.current_db.new_api.format_abspath(book_id, fmt) if path: from calibre.gui2.open_with import run_program run_program(entry, path, self) else: fmt = fmt.upper() error_dialog(self, _('No %s format') % fmt, _( 'The book {0} does not have the {1} format').format( self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')), fmt), show=True) def bd_edit_book(self, book_id, fmt): from calibre.gui2.device import BusyCursor with BusyCursor(): self.iactions['Tweak ePub'].ebook_edit_format(book_id, fmt) def open_with_action_triggerred(self, fmt, entry, *args): book_id = self.library_view.current_book if book_id is not None: if fmt == 'cover_image': self.bd_open_cover_with(book_id, entry) else: self.bd_open_fmt_with(book_id, fmt, entry) def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) self.refresh_cover_browser() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def compare_format(self, book_id, fmt): db = self.current_db.new_api ofmt = fmt if fmt.startswith('ORIGINAL_'): fmt = fmt.partition('_')[-1] else: ofmt = 'ORIGINAL_' + fmt path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt) from calibre.gui2.tweak_book.diff.main import compare_books compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial( self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt)) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() self.grid_view_button.save_state() self.search_bar_button.save_state() if self.qv: self.qv.qv_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state() self.grid_view_button.restore_state() self.search_bar_button.restore_state() # Can't do quickview here because the gui isn't totally set up. Do it in ui def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)
class LayoutMixin(object): # {{{ def __init__(self, *args, **kwargs): pass def init_layout_mixin(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut='Shift+Alt+D') self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'bd', 'gv', 'cb') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut='Shift+Alt+D') self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'cb', 'gv', 'bd') # }}} self.status_bar = StatusBar(self) stylename = unicode(self.style().objectName()) self.grid_view_button = GridViewButton(self) self.grid_view_button.toggled.connect(self.toggle_grid_view) for x in button_order: button = self.grid_view_button if x == 'gv' else getattr( self, x + '_splitter').button button.setIconSize(QSize(24, 24)) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect( self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect( self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect( self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.open_cover_with.connect(self.bd_open_cover_with, type=Qt.QueuedConnection) self.book_details.open_fmt_with.connect(self.bd_open_fmt_with, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect( self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect( self.iactions['View'].view_format_by_id) self.book_details.search_requested.connect( self.search.set_search_string) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_author.connect( lambda author: self.do_author_sort_edit( self, author, select_sort=False, select_link=False)) self.book_details.compare_specific_format.connect(self.compare_format) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def toggle_grid_view(self, show): self.library_view.alternate_views.show_view('grid' if show else None) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) if self.cover_flow: self.cover_flow.dataChanged() def bd_open_cover_with(self, book_id, entry): cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__') if cpath: from calibre.gui2.open_with import run_program run_program(entry, cpath, self) def bd_open_fmt_with(self, book_id, fmt, entry): path = self.current_db.new_api.format_abspath(book_id, fmt) if path: from calibre.gui2.open_with import run_program run_program(entry, path, self) else: fmt = fmt.upper() error_dialog(self, _('No %s format') % fmt, _('The book {0} does not have the {1} format').format( self.current_db.new_api.field_for( 'title', book_id, default_value=_('Unknown')), fmt), show=True) def open_with_action_triggerred(self, fmt, entry, *args): book_id = self.library_view.current_book if book_id is not None: if fmt == 'cover_image': self.bd_open_cover_with(book_id, entry) else: self.bd_open_fmt_with(book_id, fmt, entry) def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) if self.cover_flow: self.cover_flow.dataChanged() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def compare_format(self, book_id, fmt): db = self.current_db.new_api ofmt = fmt if fmt.startswith('ORIGINAL_'): fmt = fmt.partition('_')[-1] else: ofmt = 'ORIGINAL_' + fmt path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath( book_id, fmt) from calibre.gui2.tweak_book.diff.main import compare_books compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial( self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt)) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x + '_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x + '_splitter') s.update_desired_state() s.save_state() self.grid_view_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x + '_splitter').restore_state() self.grid_view_button.restore_state() def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)
class LayoutMixin(object): # {{{ def __init__(self, *args, **kwargs): pass def init_layout_mixin(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut='Shift+Alt+D') self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'bd', 'gv', 'cb') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut='Shift+Alt+D') self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'cb', 'gv', 'bd') # }}} self.status_bar = StatusBar(self) stylename = unicode(self.style().objectName()) self.grid_view_button = GridViewButton(self) self.grid_view_button.toggled.connect(self.toggle_grid_view) for x in button_order: button = self.grid_view_button if x == 'gv' else getattr(self, x+'_splitter').button button.setIconSize(QSize(24, 24)) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.open_cover_with.connect(self.bd_open_cover_with, type=Qt.QueuedConnection) self.book_details.open_fmt_with.connect(self.bd_open_fmt_with, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) self.book_details.search_requested.connect(self.search.set_search_string) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.remove_metadata_item.connect( self.iactions['Edit Metadata'].remove_metadata_item) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.set_cover_from_format.connect( self.iactions['Edit Metadata'].set_cover_from_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_author.connect(lambda author:self.do_author_sort_edit(self, author, select_sort=False, select_link=False)) self.book_details.compare_specific_format.connect(self.compare_format) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def toggle_grid_view(self, show): self.library_view.alternate_views.show_view('grid' if show else None) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) self.refresh_cover_browser() def bd_open_cover_with(self, book_id, entry): cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__') if cpath: from calibre.gui2.open_with import run_program run_program(entry, cpath, self) def bd_open_fmt_with(self, book_id, fmt, entry): path = self.current_db.new_api.format_abspath(book_id, fmt) if path: from calibre.gui2.open_with import run_program run_program(entry, path, self) else: fmt = fmt.upper() error_dialog(self, _('No %s format') % fmt, _( 'The book {0} does not have the {1} format').format( self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')), fmt), show=True) def open_with_action_triggerred(self, fmt, entry, *args): book_id = self.library_view.current_book if book_id is not None: if fmt == 'cover_image': self.bd_open_cover_with(book_id, entry) else: self.bd_open_fmt_with(book_id, fmt, entry) def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) self.refresh_cover_browser() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def compare_format(self, book_id, fmt): db = self.current_db.new_api ofmt = fmt if fmt.startswith('ORIGINAL_'): fmt = fmt.partition('_')[-1] else: ofmt = 'ORIGINAL_' + fmt path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt) from calibre.gui2.tweak_book.diff.main import compare_books compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial( self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt)) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() self.grid_view_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state() self.grid_view_button.restore_state() def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)
class LayoutMixin(object): # {{{ def __init__(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'bd', 'cb', 'gv') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'cb', 'bd', 'gv') # }}} self.status_bar = StatusBar(self) stylename = unicode(self.style().objectName()) self.grid_view_button = GridViewButton(self) self.grid_view_button.toggled.connect(self.toggle_grid_view) for x in button_order: button = self.grid_view_button if x == 'gv' else getattr(self, x+'_splitter').button button.setIconSize(QSize(24, 24)) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_author.connect(lambda author:self.do_author_sort_edit(self, author, select_sort=False, select_link=False)) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def toggle_grid_view(self, show): self.library_view.alternate_views.show_view('grid' if show else None) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) if self.cover_flow: self.cover_flow.dataChanged() def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) if self.cover_flow: self.cover_flow.dataChanged() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() self.grid_view_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state() self.grid_view_button.restore_state() def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)
class LayoutMixin(object): # {{{ def __init__(self): if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'bd', 'cb') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut=_('Shift+Alt+D')) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('tb', 'cb', 'bd') # }}} self.status_bar = StatusBar(self) stylename = unicode(self.style().objectName()) for x in button_order: button = getattr(self, x + '_splitter').button button.setIconSize(QSize(24, 24)) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect( self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect( self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect( self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect( self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect( self.iactions['View'].view_format_by_id) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) if self.cover_flow: self.cover_flow.dataChanged() def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) if self.cover_flow: self.cover_flow.dataChanged() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x + '_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x + '_splitter') s.update_desired_state() s.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x + '_splitter').restore_state() def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)
class LayoutMixin(object): # {{{ def __init__(self, *args, **kwargs): pass def init_layout_mixin(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book details'), I('book.png'), orientation=Qt.Vertical, parent=self, side_index=1, shortcut='Shift+Alt+D') self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('sb', 'tb', 'bd', 'gv', 'cb', 'qv') # }}} else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book details'), I('book.png'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1, shortcut='Shift+Alt+D') self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) button_order = ('sb', 'tb', 'cb', 'gv', 'qv', 'bd') # }}} # This must use the base method to find the plugin because it hasn't # been fully initialized yet self.qv = find_plugin('Quickview') if self.qv and self.qv.actual_plugin_: self.qv = self.qv.actual_plugin_ self.status_bar = StatusBar(self) stylename = unicode_type(self.style().objectName()) self.grid_view_button = GridViewButton(self) self.search_bar_button = SearchBarButton(self) self.grid_view_button.toggled.connect(self.toggle_grid_view) self.search_bar_button.toggled.connect(self.toggle_search_bar) self.layout_buttons = [] for x in button_order: if hasattr(self, x + '_splitter'): button = getattr(self, x + '_splitter').button else: if x == 'gv': button = self.grid_view_button elif x == 'qv': if self.qv is None: continue button = self.qv.qv_button else: button = self.search_bar_button self.layout_buttons.append(button) button.setVisible(False) if isosx and stylename != u'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) if gprefs['show_layout_buttons']: for b in self.layout_buttons: b.setVisible(True) self.status_bar.addPermanentWidget(b) else: self.layout_button = b = QToolButton(self) b.setAutoRaise(True), b.setCursor(Qt.PointingHandCursor) b.setPopupMode(b.InstantPopup) b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) b.setText(_('Layout')), b.setIcon(QIcon(I('config.png'))) b.setMenu(LayoutMenu(self)) b.setToolTip(_( 'Show and hide various parts of the calibre main window')) self.status_bar.addPermanentWidget(b) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) self.book_details.cover_changed.connect(self.bd_cover_changed, type=Qt.QueuedConnection) self.book_details.open_cover_with.connect(self.bd_open_cover_with, type=Qt.QueuedConnection) self.book_details.open_fmt_with.connect(self.bd_open_fmt_with, type=Qt.QueuedConnection) self.book_details.edit_book.connect(self.bd_edit_book, type=Qt.QueuedConnection) self.book_details.cover_removed.connect(self.bd_cover_removed, type=Qt.QueuedConnection) self.book_details.remote_file_dropped.connect( self.iactions['Add Books'].remote_file_dropped_on_book, type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) self.book_details.search_requested.connect(self.search.set_search_string) self.book_details.remove_specific_format.connect( self.iactions['Remove Books'].remove_format_by_id) self.book_details.remove_metadata_item.connect( self.iactions['Edit Metadata'].remove_metadata_item) self.book_details.save_specific_format.connect( self.iactions['Save To Disk'].save_library_format_by_ids) self.book_details.restore_specific_format.connect( self.iactions['Remove Books'].restore_format) self.book_details.set_cover_from_format.connect( self.iactions['Edit Metadata'].set_cover_from_format) self.book_details.copy_link.connect(self.bd_copy_link, type=Qt.QueuedConnection) self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_category.connect(self.manage_category_triggerred) self.book_details.edit_identifiers.connect(self.edit_identifiers_triggerred) self.book_details.compare_specific_format.connect(self.compare_format) m = self.library_view.model() if m.rowCount(None) > 0: QTimer.singleShot(0, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) def edit_identifiers_triggerred(self): book_id = self.library_view.current_book db = self.current_db.new_api identifiers = db.field_for('identifiers', book_id, default_value={}) from calibre.gui2.metadata.basic_widgets import Identifiers d = Identifiers(identifiers, self) if d.exec_() == d.Accepted: identifiers = d.get_identifiers() db.set_field('identifiers', {book_id: identifiers}) self.iactions['Edit Metadata'].refresh_books_after_metadata_edit({book_id}) def manage_category_triggerred(self, field, value): if field and value: if field == 'authors': self.do_author_sort_edit(self, value, select_sort=False, select_link=False) elif field: self.do_tags_list_edit(value, field) def toggle_grid_view(self, show): self.library_view.alternate_views.show_view('grid' if show else None) self.sort_sep.setVisible(show) self.sort_button.setVisible(show) def toggle_search_bar(self, show): self.search_bar.setVisible(show) if show: self.search.setFocus(Qt.OtherFocusReason) def bd_cover_changed(self, id_, cdata): self.library_view.model().db.set_cover(id_, cdata) self.refresh_cover_browser() def bd_open_cover_with(self, book_id, entry): cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__') if cpath: from calibre.gui2.open_with import run_program run_program(entry, cpath, self) def bd_open_fmt_with(self, book_id, fmt, entry): path = self.current_db.new_api.format_abspath(book_id, fmt) if path: from calibre.gui2.open_with import run_program run_program(entry, path, self) else: fmt = fmt.upper() error_dialog(self, _('No %s format') % fmt, _( 'The book {0} does not have the {1} format').format( self.current_db.new_api.field_for('title', book_id, default_value=_('Unknown')), fmt), show=True) def bd_edit_book(self, book_id, fmt): from calibre.gui2.device import BusyCursor with BusyCursor(): self.iactions['Tweak ePub'].ebook_edit_format(book_id, fmt) def open_with_action_triggerred(self, fmt, entry, *args): book_id = self.library_view.current_book if book_id is not None: if fmt == 'cover_image': self.bd_open_cover_with(book_id, entry) else: self.bd_open_fmt_with(book_id, fmt, entry) def bd_cover_removed(self, id_): self.library_view.model().db.remove_cover(id_, commit=True, notify=False) self.refresh_cover_browser() def bd_copy_link(self, url): if url: QApplication.clipboard().setText(url) def compare_format(self, book_id, fmt): db = self.current_db.new_api ofmt = fmt if fmt.startswith('ORIGINAL_'): fmt = fmt.partition('_')[-1] else: ofmt = 'ORIGINAL_' + fmt path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt) from calibre.gui2.tweak_book.diff.main import compare_books compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial( self.iactions['Remove Books'].restore_format, book_id, ofmt), names=(ofmt, fmt)) def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() for x in ('cb', 'tb', 'bd'): s = getattr(self, x+'_splitter') s.update_desired_state() s.save_state() self.grid_view_button.save_state() self.search_bar_button.save_state() if self.qv: self.qv.qv_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called for x in ('cb', 'tb', 'bd'): getattr(self, x+'_splitter').restore_state() self.grid_view_button.restore_state() self.search_bar_button.restore_state() # Can't do quickview here because the gui isn't totally set up. Do it in ui def update_status_bar(self, *args): v = self.current_view() selected = len(v.selectionModel().selectedRows()) library_total, total, current = v.model().counts() self.status_bar.update_state(library_total, total, current, selected)