def plugin_to_index(self, plugin): for i, category in enumerate(self.categories): parent = self.index(i, 0, QModelIndex()) for j, p in enumerate(self._data[category]): if plugin == p: return self.index(j, 0, parent) return QModelIndex()
def find_next(self, idx, query, backwards=False): query = query.strip() if not query: return idx matches = self.parse(query) if not matches: return idx if idx.parent().isValid(): loc = (idx.parent().row(), idx.row()) else: loc = (idx.row(), -1) if loc not in matches: return self.find(query) if len(matches) == 1: return QModelIndex() matches = list(sorted(matches)) i = matches.index(loc) if backwards: ans = i - 1 if i - 1 >= 0 else len(matches) - 1 else: ans = i + 1 if i + 1 < len(matches) else 0 ans = matches[ans] return self.index(ans[0], 0, QModelIndex()) if ans[1] < 0 else \ self.index(ans[1], 0, self.index(ans[0], 0, QModelIndex()))
def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() if parent.isValid(): return self.createIndex(row, column, 1 + parent.row()) else: return self.createIndex(row, column, 0)
def plugin_to_index_by_properties(self, plugin): for i, category in enumerate(self.categories): parent = self.index(i, 0, QModelIndex()) for j, p in enumerate(self._data[category]): if plugin.name == p.name and plugin.type == p.type and \ plugin.author == p.author and plugin.version == p.version: return self.index(j, 0, parent) return QModelIndex()
def parent(self, index): if not index.isValid(): return QModelIndex() child_item = index.internalPointer() parent_item = child_item.parent if parent_item is self.root or parent_item is None: return QModelIndex() ans = self.createIndex(parent_item.row(), 0, parent_item) return ans
def find(self, query): query = query.strip() if not query: return QModelIndex() matches = self.parse(query) if not matches: return QModelIndex() matches = list(sorted(matches)) c, p = matches[0] cat_idx = self.index(c, 0, QModelIndex()) if p == -1: return cat_idx return self.index(p, 0, cat_idx)
def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QModelIndex() if not parent.isValid(): parent_item = self.root else: parent_item = parent.internalPointer() try: child_item = parent_item.children[row] except IndexError: return QModelIndex() ans = self.createIndex(row, column, child_item) return ans
def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) self.path_to_book = getattr(mi, 'path', None) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.marked = mi.marked self.resize_cover() html = render_html(mi, True, self, pref_name='popup_book_display_fields') set_html(mi, html, self.details) self.update_cover_tooltip()
def update_result(self, plugin_name, width, height, data): if plugin_name.endswith('}'): # multi cover plugin plugin_name = plugin_name.partition('{')[0] plugin = [plugin for plugin in self.plugin_map if plugin.name == plugin_name] if not plugin: return plugin = plugin[0] last_row = max(self.plugin_map[plugin]) pmap = self.load_pixmap(data) if pmap.isNull(): return self.beginInsertRows(QModelIndex(), last_row, last_row) for rows in itervalues(self.plugin_map): for i in range(len(rows)): if rows[i] >= last_row: rows[i] += 1 self.plugin_map[plugin].insert(-1, last_row) self.covers.insert(last_row, self.get_item(plugin_name, pmap, waiting=False)) self.endInsertRows() else: # single cover plugin idx = None for plugin, rows in iteritems(self.plugin_map): if plugin.name == plugin_name: idx = rows[0] break if idx is None: return pmap = self.load_pixmap(data) if pmap.isNull(): return self.covers[idx] = self.get_item(plugin_name, pmap, waiting=False) self.dataChanged.emit(self.index(idx), self.index(idx))
def edit_metadata_for(self, rows, book_ids, bulk=None): previous = self.gui.library_view.currentIndex() if bulk or (bulk is None and len(rows) > 1): return self.do_edit_bulk_metadata(rows, book_ids) current_row = 0 row_list = rows editing_multiple = len(row_list) > 1 if not editing_multiple: cr = row_list[0] row_list = \ list(range(self.gui.library_view.model().rowCount(QModelIndex()))) current_row = row_list.index(cr) view = self.gui.library_view.alternate_views.current_view try: hpos = view.horizontalScrollBar().value() except Exception: hpos = 0 changed, rows_to_refresh = self.do_edit_metadata(row_list, current_row, editing_multiple) m = self.gui.library_view.model() if rows_to_refresh: m.refresh_rows(rows_to_refresh) if changed: self.refresh_books_after_metadata_edit(changed, previous) if self.gui.library_view.alternate_views.current_view is view: if hasattr(view, 'restore_hpos'): view.restore_hpos(hpos) else: view.horizontalScrollBar().setValue(hpos)
def get_ids_from_selected_rows(): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) < 2: rows = range(self.gui.library_view.model().rowCount( QModelIndex())) ids = list(map(self.gui.library_view.model().id, rows)) return ids
def generate_catalog(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) < 2: rows = range(self.gui.library_view.model().rowCount(QModelIndex())) ids = list(map(self.gui.library_view.model().id, rows)) if not ids: return error_dialog(self.gui, _('No books selected'), _('No books selected for catalog generation'), show=True) db = self.gui.library_view.model().db dbspec = {} for id in ids: dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)} # Calling gui2.tools:generate_catalog() ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager, db) if ret is None: return func, args, desc, out, sync, title = ret fmt = os.path.splitext(out)[1][1:].upper() job = self.gui.job_manager.run_job(self.Dispatcher( self.catalog_generated), func, args=args, description=desc) job.catalog_file_path = out job.fmt = fmt job.catalog_sync, job.catalog_title = sync, title self.gui.status_bar.show_message(_('Generating %s catalog...') % fmt)
def text_edited(self, *args): if self.no_popup: return self.update_completions() select_first = len(self.mcompleter.model().current_prefix) > 0 if not select_first: self.mcompleter.setCurrentIndex(QModelIndex()) self.complete(select_first=select_first)
def selectAll(self): # We re-implement this to ensure that only indexes from column 0 are # selected. The base class implementation selects all columns. This # causes problems with selection syncing, see # https://bugs.launchpad.net/bugs/1236348 m = self.model() sm = self.selectionModel() sel = QItemSelection(m.index(0, 0), m.index(m.rowCount(QModelIndex())-1, 0)) sm.select(sel, QItemSelectionModel.SelectionFlag.ClearAndSelect)
def moveCursor(self, action, modifiers): index = QListView.moveCursor(self, action, modifiers) if action in (QAbstractItemView.CursorAction.MoveLeft, QAbstractItemView.CursorAction.MoveRight) and index.isValid(): ci = self.currentIndex() if ci.isValid() and index.row() == ci.row(): nr = index.row() + (1 if action == QAbstractItemView.CursorAction.MoveRight else -1) if 0 <= nr < self.model().rowCount(QModelIndex()): index = self.model().index(nr, 0) return index
def refresh_after_polish(self): self.refresh_debounce_timer.stop() book_ids = tuple(self.to_be_refreshed) self.to_be_refreshed = set() if self.gui.current_view() is self.gui.library_view: self.gui.library_view.model().refresh_ids(book_ids) current = self.gui.library_view.currentIndex() if current.isValid(): self.gui.library_view.model().current_changed(current, QModelIndex()) self.gui.tags_view.recount()
def dump_tags_model(m): from qt.core import QModelIndex, Qt ans, indent = [], ' ' def dump_node(index, level=-1): if level > -1: ans.append(indent*level + index.data(Qt.ItemDataRole.UserRole).dump_data()) for i in range(m.rowCount(index)): dump_node(m.index(i, 0, index), level + 1) if level == 0: ans.append('') dump_node(QModelIndex()) return '\n'.join(ans)
def search(self, query): cq = self.current_query if cq['items'] and -1 < cq['index'] < len(cq['items']): cq['items'][cq['index']].set_current_search_result(False) if cq['text'] != query: items = tuple(self.find_items(query)) cq.update({'text': query, 'items': items, 'index': -1}) if len(cq['items']) > 0: cq['index'] = (cq['index'] + 1) % len(cq['items']) item = cq['items'][cq['index']] item.set_current_search_result(True) index = self.indexFromItem(item) return index return QModelIndex()
def library_ids_deleted(self, ids_deleted, current_row=None): view = self.gui.library_view for v in (self.gui.memory_view, self.gui.card_a_view, self.gui.card_b_view): if v is None: continue v.model().clear_ondevice(ids_deleted) if current_row is not None: ci = view.model().index(current_row, 0) if not ci.isValid(): # Current row is after the last row, set it to the last row current_row = view.row_count() - 1 view.set_current_row(current_row) if view.model().rowCount(QModelIndex()) < 1: self.gui.book_details.reset_info()
def book_converted(self, job): temp_files, fmt, book_id = self.conversion_jobs.pop(job)[:3] try: if job.failed: self.gui.job_exception(job) return db = self.gui.current_db if not db.new_api.has_id(book_id): return error_dialog( self.gui, _('Book deleted'), _('The book you were trying to convert has been deleted from the calibre library.' ), show=True) same_fmt = getattr(job, 'conversion_of_same_fmt', False) manually_fine_tune_toc = getattr(job, 'manually_fine_tune_toc', False) fmtf = temp_files[-1].name if os.stat(fmtf).st_size < 1: raise Exception( _('Empty output file, ' 'probably the conversion process crashed')) if same_fmt and tweaks['save_original_format']: db.save_original_format(book_id, fmt, notify=False) with open(temp_files[-1].name, 'rb') as data: db.add_format(book_id, fmt, data, index_is_id=True) self.gui.book_converted.emit(book_id, fmt) self.gui.status_bar.show_message( job.description + ' ' + _('completed'), 2000) finally: for f in temp_files: try: if os.path.exists(f.name): os.remove(f.name) except: pass self.gui.tags_view.recount() if self.gui.current_view() is self.gui.library_view: lv = self.gui.library_view lv.model().refresh_ids((book_id, )) current = lv.currentIndex() if current.isValid(): lv.model().current_changed(current, QModelIndex()) if manually_fine_tune_toc: self.gui.iactions['Edit ToC'].do_one(book_id, fmt.upper())
def paint(self, painter, option, index): icon = index.data(Qt.ItemDataRole.DecorationRole) if icon and not icon.isNull(): QStyledItemDelegate.paint(self, painter, option, QModelIndex()) pw, ph = option.rect.width(), option.rect.height() scaled, w, h = fit_image(option.decorationSize.width(), option.decorationSize.height(), pw, ph) r = option.rect if pw > w: x = (pw - w) // 2 r = r.adjusted(x, 0, -x, 0) if ph > h: y = (ph - h) // 2 r = r.adjusted(0, y, 0, -y) painter.drawPixmap(r, icon.pixmap(w, h)) else: QStyledItemDelegate.paint(self, painter, option, index)
def parent(self, index): if not index.isValid() or index.internalId() == 0: return QModelIndex() return self.createIndex(0, 0)
def index(self, row, column, parent=QModelIndex()): return self.createIndex(row, column)
def plugin_to_index(self, display_plugin): for i, p in enumerate(self.display_plugins): if display_plugin == p: return self.index(i, 0, QModelIndex()) return QModelIndex()
def hide_all(self, *args): self.model.hide_jobs(list(range(0, self.model.rowCount(QModelIndex())))) self.proxy_model.beginResetModel(), self.proxy_model.endResetModel()
def columnCount(self, parent=QModelIndex()): return 5
def rowCount(self, parent=QModelIndex()): return len(self.jobs)
def hide(self): self.setCurrentIndex(QModelIndex()) QListView.hide(self)
#!/usr/bin/env python # vim:fileencoding=utf-8 __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' import shutil from qt.core import (QAbstractListModel, Qt, QModelIndex, QApplication, QWidget, QGridLayout, QListView, QStyledItemDelegate, pyqtSignal, QPushButton, QIcon, QItemSelectionModel) from calibre.gui2 import error_dialog ROOT = QModelIndex() MAX_SAVEPOINTS = 100 def cleanup(containers): for container in containers: try: shutil.rmtree(container.root, ignore_errors=True) except: pass class State(object): def __init__(self, container): self.container = container self.message = None