Port use of QVariant in all edit book code. Edit book basically works

This commit is contained in:
Kovid Goyal 2014-04-22 16:54:06 +05:30
parent 69af8a9b65
commit 8ce199cd6b
13 changed files with 127 additions and 116 deletions

View File

@ -19,7 +19,7 @@ from calibre.utils.config import JSONConfig
from calibre.constants import DEBUG from calibre.constants import DEBUG
from calibre import prints from calibre import prints
from calibre.utils.icu import sort_key, lower from calibre.utils.icu import sort_key, lower
from calibre.gui2 import NONE, error_dialog, info_dialog from calibre.gui2 import error_dialog, info_dialog
from calibre.utils.search_query_parser import SearchQueryParser, ParseException from calibre.utils.search_query_parser import SearchQueryParser, ParseException
from calibre.gui2.search_box import SearchBox2 from calibre.gui2.search_box import SearchBox2
@ -224,7 +224,7 @@ class ConfigModel(QAbstractItemModel, SearchQueryParser):
ip = index.internalPointer() ip = index.internalPointer()
if ip is not None and role == Qt.UserRole: if ip is not None and role == Qt.UserRole:
return ip return ip
return NONE return None
def flags(self, index): def flags(self, index):
ans = QAbstractItemModel.flags(self, index) ans = QAbstractItemModel.flags(self, index)
@ -498,7 +498,7 @@ class Delegate(QStyledItemDelegate): # {{{
self.closeEditor.connect(self.editing_done) self.closeEditor.connect(self.editing_done)
def to_doc(self, index): def to_doc(self, index):
data = index.data(Qt.UserRole).toPyObject() data = index.data(Qt.UserRole)
if data is None: if data is None:
html = _('<b>This shortcut no longer exists</b>') html = _('<b>This shortcut no longer exists</b>')
elif data.is_shortcut: elif data.is_shortcut:

View File

@ -12,13 +12,12 @@ from functools import partial
from collections import defaultdict from collections import defaultdict
from PyQt5.Qt import ( from PyQt5.Qt import (
QAbstractItemModel, QModelIndex, Qt, QVariant, pyqtSignal, QApplication, QAbstractItemModel, QModelIndex, Qt, pyqtSignal, QApplication,
QTreeView, QSize, QGridLayout, QAbstractListModel, QListView, QPen, QMenu, QTreeView, QSize, QGridLayout, QAbstractListModel, QListView, QPen, QMenu,
QStyledItemDelegate, QSplitter, QLabel, QSizePolicy, QIcon, QMimeData, QStyledItemDelegate, QSplitter, QLabel, QSizePolicy, QIcon, QMimeData,
QPushButton, QToolButton, QInputMethodEvent) QPushButton, QToolButton, QInputMethodEvent)
from calibre.constants import plugins, cache_dir from calibre.constants import plugins, cache_dir
from calibre.gui2 import NONE
from calibre.gui2.widgets2 import HistoryLineEdit2 from calibre.gui2.widgets2 import HistoryLineEdit2
from calibre.gui2.tweak_book import tprefs from calibre.gui2.tweak_book import tprefs
from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor from calibre.gui2.tweak_book.widgets import Dialog, BusyCursor
@ -435,20 +434,20 @@ class CategoryModel(QAbstractItemModel):
def data(self, index, role=Qt.DisplayRole): def data(self, index, role=Qt.DisplayRole):
if not index.isValid(): if not index.isValid():
return NONE return None
pid = index.internalId() pid = index.internalId()
if pid == 0: if pid == 0:
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
return QVariant(self.categories[index.row()][0]) return self.categories[index.row()][0]
if role == Qt.FontRole: if role == Qt.FontRole:
return QVariant(self.bold_font) return self.bold_font
if role == Qt.DecorationRole and index.row() == 0: if role == Qt.DecorationRole and index.row() == 0:
return QVariant(self.fav_icon) return self.fav_icon
else: else:
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
item = self.categories[pid - 1][1][index.row()] item = self.categories[pid - 1][1][index.row()]
return QVariant(item[0]) return item[0]
return NONE return None
def get_range(self, index): def get_range(self, index):
if index.isValid(): if index.isValid():
@ -532,8 +531,8 @@ class CharModel(QAbstractListModel):
def data(self, index, role): def data(self, index, role):
if role == Qt.UserRole and -1 < index.row() < len(self.chars): if role == Qt.UserRole and -1 < index.row() < len(self.chars):
return QVariant(self.chars[index.row()]) return self.chars[index.row()]
return NONE return None
def flags(self, index): def flags(self, index):
ans = Qt.ItemIsEnabled ans = Qt.ItemIsEnabled
@ -581,8 +580,9 @@ class CharDelegate(QStyledItemDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index) QStyledItemDelegate.paint(self, painter, option, index)
charcode, ok = index.data(Qt.UserRole).toInt() try:
if not ok: charcode = int(index.data(Qt.UserRole))
except (TypeError, ValueError):
return return
painter.save() painter.save()
try: try:
@ -632,8 +632,11 @@ class CharView(QListView):
self.clicked.connect(self.item_activated) self.clicked.connect(self.item_activated)
def item_activated(self, index): def item_activated(self, index):
char_code, ok = self.model().data(index, Qt.UserRole).toInt() try:
if ok: char_code = int(self.model().data(index, Qt.UserRole))
except (TypeError, ValueError):
pass
else:
self.char_selected.emit(chr(char_code)) self.char_selected.emit(chr(char_code))
def set_allow_drag_and_drop(self, enabled): def set_allow_drag_and_drop(self, enabled):
@ -663,8 +666,11 @@ class CharView(QListView):
row = index.row() row = index.row()
if row != self.last_mouse_idx: if row != self.last_mouse_idx:
self.last_mouse_idx = row self.last_mouse_idx = row
char_code, ok = self.model().data(index, Qt.UserRole).toInt() try:
if ok: char_code = int(self.model().data(index, Qt.UserRole))
except (TypeError, ValueError):
pass
else:
self.show_name.emit(char_code) self.show_name.emit(char_code)
self.setCursor(Qt.PointingHandCursor) self.setCursor(Qt.PointingHandCursor)
else: else:
@ -676,8 +682,11 @@ class CharView(QListView):
def context_menu(self, pos): def context_menu(self, pos):
index = self.indexAt(pos) index = self.indexAt(pos)
if index.isValid(): if index.isValid():
char_code, ok = self.model().data(index, Qt.UserRole).toInt() try:
if ok: char_code = int(self.model().data(index, Qt.UserRole))
except (TypeError, ValueError):
pass
else:
m = QMenu(self) m = QMenu(self)
m.addAction(QIcon(I('edit-copy.png')), _('Copy %s to clipboard') % chr(char_code), partial(self.copy_to_clipboard, char_code)) m.addAction(QIcon(I('edit-copy.png')), _('Copy %s to clipboard') % chr(char_code), partial(self.copy_to_clipboard, char_code))
m.addAction(QIcon(I('rating.png')), m.addAction(QIcon(I('rating.png')),

View File

@ -9,7 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import sys import sys
from PyQt5.Qt import ( from PyQt5.Qt import (
QIcon, Qt, QSplitter, QListWidget, QTextBrowser, QPalette, QMenu, QIcon, Qt, QSplitter, QListWidget, QTextBrowser, QPalette, QUrl, QMenu,
QListWidgetItem, pyqtSignal, QApplication, QStyledItemDelegate) QListWidgetItem, pyqtSignal, QApplication, QStyledItemDelegate)
from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL
@ -111,17 +111,17 @@ class Check(QSplitter):
msg, _('Click to run a check on the book'), _('Run check'))) msg, _('Click to run a check on the book'), _('Run check')))
def link_clicked(self, url): def link_clicked(self, url):
url = unicode(url.toString()) url = unicode(url.toString(QUrl.None))
if url == 'activate:item': if url == 'activate:item':
self.current_item_activated() self.current_item_activated()
elif url == 'run:check': elif url == 'run:check':
self.check_requested.emit() self.check_requested.emit()
elif url == 'fix:errors': elif url == 'fix:errors':
errors = [self.items.item(i).data(Qt.UserRole).toPyObject() for i in xrange(self.items.count())] errors = [self.items.item(i).data(Qt.UserRole) for i in xrange(self.items.count())]
self.fix_requested.emit(errors) self.fix_requested.emit(errors)
elif url.startswith('fix:error,'): elif url.startswith('fix:error,'):
num = int(url.rpartition(',')[-1]) num = int(url.rpartition(',')[-1])
errors = [self.items.item(num).data(Qt.UserRole).toPyObject()] errors = [self.items.item(num).data(Qt.UserRole)]
self.fix_requested.emit(errors) self.fix_requested.emit(errors)
elif url.startswith('activate:item:'): elif url.startswith('activate:item:'):
index = int(url.rpartition(':')[-1]) index = int(url.rpartition(':')[-1])
@ -138,7 +138,7 @@ class Check(QSplitter):
def current_item_activated(self, *args): def current_item_activated(self, *args):
i = self.items.currentItem() i = self.items.currentItem()
if i is not None: if i is not None:
err = i.data(Qt.UserRole).toPyObject() err = i.data(Qt.UserRole)
if err.has_multiple_locations: if err.has_multiple_locations:
self.location_activated(0) self.location_activated(0)
else: else:
@ -147,7 +147,7 @@ class Check(QSplitter):
def location_activated(self, index): def location_activated(self, index):
i = self.items.currentItem() i = self.items.currentItem()
if i is not None: if i is not None:
err = i.data(Qt.UserRole).toPyObject() err = i.data(Qt.UserRole)
err.current_location_index = index err.current_location_index = index
self.item_activated.emit(err) self.item_activated.emit(err)
@ -165,7 +165,7 @@ class Check(QSplitter):
return loc return loc
if i is not None: if i is not None:
err = i.data(Qt.UserRole).toPyObject() err = i.data(Qt.UserRole)
header = {DEBUG:_('Debug'), INFO:_('Information'), WARN:_('Warning'), ERROR:_('Error'), CRITICAL:_('Error')}[err.level] header = {DEBUG:_('Debug'), INFO:_('Information'), WARN:_('Warning'), ERROR:_('Error'), CRITICAL:_('Error')}[err.level]
ifix = '' ifix = ''
loc = loc_to_string(err.line, err.col) loc = loc_to_string(err.line, err.col)

View File

@ -11,7 +11,7 @@ from functools import partial
from PyQt5.Qt import ( from PyQt5.Qt import (
QGridLayout, QSize, QListView, QStyledItemDelegate, QLabel, QPixmap, QGridLayout, QSize, QListView, QStyledItemDelegate, QLabel, QPixmap,
QApplication, QSizePolicy, QAbstractListModel, QVariant, Qt, QRect, QApplication, QSizePolicy, QAbstractListModel, Qt, QRect,
QPainter, QModelIndex, QSortFilterProxyModel, QLineEdit, QToolButton, QPainter, QModelIndex, QSortFilterProxyModel, QLineEdit, QToolButton,
QIcon, QFormLayout, pyqtSignal, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QIcon, QFormLayout, pyqtSignal, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QMenu, QInputDialog) QMenu, QInputDialog)
@ -20,7 +20,7 @@ from calibre import fit_image
from calibre.constants import plugins from calibre.constants import plugins
from calibre.ebooks.metadata import string_to_authors from calibre.ebooks.metadata import string_to_authors
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
from calibre.gui2 import NONE, choose_files, error_dialog from calibre.gui2 import choose_files, error_dialog
from calibre.gui2.languages import LanguagesEdit from calibre.gui2.languages import LanguagesEdit
from calibre.gui2.tweak_book import current_container, tprefs from calibre.gui2.tweak_book import current_container, tprefs
from calibre.gui2.tweak_book.widgets import Dialog from calibre.gui2.tweak_book.widgets import Dialog
@ -98,7 +98,7 @@ class ImageDelegate(QStyledItemDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, QModelIndex()) # draw the hover and selection highlights QStyledItemDelegate.paint(self, painter, option, QModelIndex()) # draw the hover and selection highlights
name = unicode(index.data(Qt.DisplayRole).toString()) name = unicode(index.data(Qt.DisplayRole) or '')
cover = self.cover_cache.get(name, None) cover = self.cover_cache.get(name, None)
if cover is None: if cover is None:
cover = self.cover_cache[name] = QPixmap() cover = self.cover_cache[name] = QPixmap()
@ -161,10 +161,10 @@ class Images(QAbstractListModel):
try: try:
name = self.image_names[index.row()] name = self.image_names[index.row()]
except IndexError: except IndexError:
return NONE return None
if role in (Qt.DisplayRole, Qt.ToolTipRole): if role in (Qt.DisplayRole, Qt.ToolTipRole):
return QVariant(name) return name
return NONE return None
class InsertImage(Dialog): class InsertImage(Dialog):
@ -259,12 +259,12 @@ class InsertImage(Dialog):
def activated(self, index): def activated(self, index):
if self.for_browsing: if self.for_browsing:
return self.image_activated.emit(unicode(index.data().toString())) return self.image_activated.emit(unicode(index.data() or ''))
self.chosen_image_is_external = False self.chosen_image_is_external = False
self.accept() self.accept()
def accept(self): def accept(self):
self.chosen_image = unicode(self.view.currentIndex().data().toString()) self.chosen_image = unicode(self.view.currentIndex().data() or '')
super(InsertImage, self).accept() super(InsertImage, self).accept()
def filter_changed(self, *args): def filter_changed(self, *args):

View File

@ -602,7 +602,7 @@ class TextEdit(PlainTextEdit):
return return
pos = cursor.positionInBlock() pos = cursor.positionInBlock()
for r in cursor.block().layout().additionalFormats(): for r in cursor.block().layout().additionalFormats():
if r.start <= pos < r.start + r.length and r.format.property(SYNTAX_PROPERTY).toBool(): if r.start <= pos < r.start + r.length and r.format.property(SYNTAX_PROPERTY):
return r return r
def syntax_format_for_cursor(self, cursor): def syntax_format_for_cursor(self, cursor):
@ -617,6 +617,7 @@ class TextEdit(PlainTextEdit):
QToolTip.setFont(self.tooltip_font) QToolTip.setFont(self.tooltip_font)
QToolTip.setPalette(self.tooltip_palette) QToolTip.setPalette(self.tooltip_palette)
QToolTip.showText(ev.globalPos(), textwrap.fill(tt)) QToolTip.showText(ev.globalPos(), textwrap.fill(tt))
return
QToolTip.hideText() QToolTip.hideText()
ev.ignore() ev.ignore()
# }}} # }}}

View File

@ -87,7 +87,7 @@ class ItemDelegate(QStyledItemDelegate): # {{{
rename_requested = pyqtSignal(object, object) rename_requested = pyqtSignal(object, object)
def setEditorData(self, editor, index): def setEditorData(self, editor, index):
name = unicode(index.data(NAME_ROLE).toString()) name = unicode(index.data(NAME_ROLE) or '')
# We do this because Qt calls selectAll() unconditionally on the # We do this because Qt calls selectAll() unconditionally on the
# editor, and we want only a part of the file name to be selected # editor, and we want only a part of the file name to be selected
QTimer.singleShot(0, partial(self.set_editor_data, name, editor)) QTimer.singleShot(0, partial(self.set_editor_data, name, editor))
@ -105,7 +105,7 @@ class ItemDelegate(QStyledItemDelegate): # {{{
def setModelData(self, editor, model, index): def setModelData(self, editor, model, index):
newname = unicode(editor.text()) newname = unicode(editor.text())
oldname = unicode(index.data(NAME_ROLE).toString()) oldname = unicode(index.data(NAME_ROLE) or '')
if newname != oldname: if newname != oldname:
self.rename_requested.emit(oldname, newname) self.rename_requested.emit(oldname, newname)
@ -123,7 +123,7 @@ class ItemDelegate(QStyledItemDelegate): # {{{
suffix = '%s(%d)' % (NBSP, index.model().rowCount(index)) suffix = '%s(%d)' % (NBSP, index.model().rowCount(index))
else: else:
try: try:
suffix = NBSP + human_readable(current_container().filesize(unicode(index.data(NAME_ROLE).toString()))) suffix = NBSP + human_readable(current_container().filesize(unicode(index.data(NAME_ROLE) or '')))
except EnvironmentError: except EnvironmentError:
suffix = NBSP + human_readable(0) suffix = NBSP + human_readable(0)
br = painter.boundingRect(option.rect, Qt.AlignRight|Qt.AlignVCenter, suffix) br = painter.boundingRect(option.rect, Qt.AlignRight|Qt.AlignVCenter, suffix)
@ -195,7 +195,7 @@ class FileList(QTreeWidget):
def get_state(self): def get_state(self):
s = {'pos':self.verticalScrollBar().value()} s = {'pos':self.verticalScrollBar().value()}
s['expanded'] = {c for c, item in self.categories.iteritems() if item.isExpanded()} s['expanded'] = {c for c, item in self.categories.iteritems() if item.isExpanded()}
s['selected'] = {unicode(i.data(0, NAME_ROLE).toString()) for i in self.selectedItems()} s['selected'] = {unicode(i.data(0, NAME_ROLE) or '') for i in self.selectedItems()}
return s return s
def set_state(self, state): def set_state(self, state):
@ -204,21 +204,21 @@ class FileList(QTreeWidget):
self.verticalScrollBar().setValue(state['pos']) self.verticalScrollBar().setValue(state['pos'])
for parent in self.categories.itervalues(): for parent in self.categories.itervalues():
for c in (parent.child(i) for i in xrange(parent.childCount())): for c in (parent.child(i) for i in xrange(parent.childCount())):
name = unicode(c.data(0, NAME_ROLE).toString()) name = unicode(c.data(0, NAME_ROLE) or '')
if name in state['selected']: if name in state['selected']:
c.setSelected(True) c.setSelected(True)
def item_from_name(self, name): def item_from_name(self, name):
for parent in self.categories.itervalues(): for parent in self.categories.itervalues():
for c in (parent.child(i) for i in xrange(parent.childCount())): for c in (parent.child(i) for i in xrange(parent.childCount())):
q = unicode(c.data(0, NAME_ROLE).toString()) q = unicode(c.data(0, NAME_ROLE) or '')
if q == name: if q == name:
return c return c
def select_name(self, name): def select_name(self, name):
for parent in self.categories.itervalues(): for parent in self.categories.itervalues():
for c in (parent.child(i) for i in xrange(parent.childCount())): for c in (parent.child(i) for i in xrange(parent.childCount())):
q = unicode(c.data(0, NAME_ROLE).toString()) q = unicode(c.data(0, NAME_ROLE) or '')
c.setSelected(q == name) c.setSelected(q == name)
if q == name: if q == name:
self.scrollToItem(c) self.scrollToItem(c)
@ -421,9 +421,9 @@ class FileList(QTreeWidget):
container = current_container() container = current_container()
ci = self.currentItem() ci = self.currentItem()
if ci is not None: if ci is not None:
cn = unicode(ci.data(0, NAME_ROLE).toString()) cn = unicode(ci.data(0, NAME_ROLE) or '')
mt = unicode(ci.data(0, MIME_ROLE).toString()) mt = unicode(ci.data(0, MIME_ROLE) or '')
cat = unicode(ci.data(0, CATEGORY_ROLE).toString()) cat = unicode(ci.data(0, CATEGORY_ROLE) or '')
n = elided_text(cn.rpartition('/')[-1]) n = elided_text(cn.rpartition('/')[-1])
m.addAction(QIcon(I('save.png')), _('Export %s') % n, partial(self.export, cn)) m.addAction(QIcon(I('save.png')), _('Export %s') % n, partial(self.export, cn))
if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS: if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS:
@ -446,7 +446,7 @@ class FileList(QTreeWidget):
selected_map = defaultdict(list) selected_map = defaultdict(list)
for item in sel: for item in sel:
selected_map[unicode(item.data(0, CATEGORY_ROLE).toString())].append(unicode(item.data(0, NAME_ROLE).toString())) selected_map[unicode(item.data(0, CATEGORY_ROLE) or '')].append(unicode(item.data(0, NAME_ROLE) or ''))
for items in selected_map.itervalues(): for items in selected_map.itervalues():
items.sort(key=self.index_of_name) items.sort(key=self.index_of_name)
@ -466,7 +466,7 @@ class FileList(QTreeWidget):
for category, parent in self.categories.iteritems(): for category, parent in self.categories.iteritems():
for i in xrange(parent.childCount()): for i in xrange(parent.childCount()):
item = parent.child(i) item = parent.child(i)
if unicode(item.data(0, NAME_ROLE).toString()) == name: if unicode(item.data(0, NAME_ROLE) or '') == name:
return (category, i) return (category, i)
return (None, -1) return (None, -1)
@ -483,7 +483,7 @@ class FileList(QTreeWidget):
self.mark_requested.emit(name, 'cover') self.mark_requested.emit(name, 'cover')
def mark_as_titlepage(self, name): def mark_as_titlepage(self, name):
first = unicode(self.categories['text'].child(0).data(0, NAME_ROLE).toString()) == name first = unicode(self.categories['text'].child(0).data(0, NAME_ROLE) or '') == name
move_to_start = False move_to_start = False
if not first: if not first:
move_to_start = question_dialog(self, _('Not first item'), _( move_to_start = question_dialog(self, _('Not first item'), _(
@ -500,7 +500,7 @@ class FileList(QTreeWidget):
return QTreeWidget.keyPressEvent(self, ev) return QTreeWidget.keyPressEvent(self, ev)
def request_bulk_rename(self): def request_bulk_rename(self):
names = {unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems()} names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()}
bad = names & current_container().names_that_must_not_be_changed bad = names & current_container().names_that_must_not_be_changed
if bad: if bad:
return error_dialog(self, _('Cannot rename'), return error_dialog(self, _('Cannot rename'),
@ -517,7 +517,7 @@ class FileList(QTreeWidget):
self.bulk_rename_requested.emit(name_map) self.bulk_rename_requested.emit(name_map)
def request_delete(self): def request_delete(self):
names = {unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems()} names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()}
bad = names & current_container().names_that_must_not_be_removed bad = names & current_container().names_that_must_not_be_removed
if bad: if bad:
return error_dialog(self, _('Cannot delete'), return error_dialog(self, _('Cannot delete'),
@ -525,9 +525,9 @@ class FileList(QTreeWidget):
text = self.categories['text'] text = self.categories['text']
children = (text.child(i) for i in xrange(text.childCount())) children = (text.child(i) for i in xrange(text.childCount()))
spine_removals = [(unicode(item.data(0, NAME_ROLE).toString()), item.isSelected()) for item in children] spine_removals = [(unicode(item.data(0, NAME_ROLE) or ''), item.isSelected()) for item in children]
other_removals = {unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems() other_removals = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()
if unicode(item.data(0, CATEGORY_ROLE).toString()) != 'text'} if unicode(item.data(0, CATEGORY_ROLE) or '') != 'text'}
self.delete_requested.emit(spine_removals, other_removals) self.delete_requested.emit(spine_removals, other_removals)
def delete_done(self, spine_removals, other_removals): def delete_done(self, spine_removals, other_removals):
@ -539,7 +539,7 @@ class FileList(QTreeWidget):
if category != 'text': if category != 'text':
for i in xrange(parent.childCount()): for i in xrange(parent.childCount()):
child = parent.child(i) child = parent.child(i)
if unicode(child.data(0, NAME_ROLE).toString()) in other_removals: if unicode(child.data(0, NAME_ROLE) or '') in other_removals:
removals.append(child) removals.append(child)
# The sorting by index is necessary otherwise Qt crashes with recursive # The sorting by index is necessary otherwise Qt crashes with recursive
@ -563,8 +563,8 @@ class FileList(QTreeWidget):
if current_order != pre_drop_order: if current_order != pre_drop_order:
order = [] order = []
for child in (text.child(i) for i in xrange(text.childCount())): for child in (text.child(i) for i in xrange(text.childCount())):
name = unicode(child.data(0, NAME_ROLE).toString()) name = unicode(child.data(0, NAME_ROLE) or '')
linear = child.data(0, LINEAR_ROLE).toBool() linear = bool(child.data(0, LINEAR_ROLE))
order.append([name, linear]) order.append([name, linear])
# Ensure that all non-linear items are at the end, any non-linear # Ensure that all non-linear items are at the end, any non-linear
# items not at the end will be made linear # items not at the end will be made linear
@ -579,9 +579,9 @@ class FileList(QTreeWidget):
self._request_edit(item) self._request_edit(item)
def _request_edit(self, item): def _request_edit(self, item):
category = unicode(item.data(0, CATEGORY_ROLE).toString()) category = unicode(item.data(0, CATEGORY_ROLE) or '')
mime = unicode(item.data(0, MIME_ROLE).toString()) mime = unicode(item.data(0, MIME_ROLE) or '')
name = unicode(item.data(0, NAME_ROLE).toString()) name = unicode(item.data(0, NAME_ROLE) or '')
syntax = {'text':'html', 'styles':'css'}.get(category, None) syntax = {'text':'html', 'styles':'css'}.get(category, None)
self.edit_file.emit(name, syntax, mime) self.edit_file.emit(name, syntax, mime)
@ -601,9 +601,9 @@ class FileList(QTreeWidget):
def searchable_names(self): def searchable_names(self):
ans = {'text':OrderedDict(), 'styles':OrderedDict(), 'selected':OrderedDict()} ans = {'text':OrderedDict(), 'styles':OrderedDict(), 'selected':OrderedDict()}
for item in self.all_files: for item in self.all_files:
category = unicode(item.data(0, CATEGORY_ROLE).toString()) category = unicode(item.data(0, CATEGORY_ROLE) or '')
mime = unicode(item.data(0, MIME_ROLE).toString()) mime = unicode(item.data(0, MIME_ROLE) or '')
name = unicode(item.data(0, NAME_ROLE).toString()) name = unicode(item.data(0, NAME_ROLE) or '')
ok = category in {'text', 'styles'} ok = category in {'text', 'styles'}
if ok: if ok:
ans[category][name] = syntax_from_mime(name, mime) ans[category][name] = syntax_from_mime(name, mime)
@ -646,7 +646,7 @@ class FileList(QTreeWidget):
def link_stylesheets(self, names): def link_stylesheets(self, names):
s = self.categories['styles'] s = self.categories['styles']
sheets = [unicode(s.child(i).data(0, NAME_ROLE).toString()) for i in xrange(s.childCount())] sheets = [unicode(s.child(i).data(0, NAME_ROLE) or '') for i in xrange(s.childCount())]
if not sheets: if not sheets:
return error_dialog(self, _('No stylesheets'), _( return error_dialog(self, _('No stylesheets'), _(
'This book currently has no stylesheets. You must first create a stylesheet' 'This book currently has no stylesheets. You must first create a stylesheet'

View File

@ -72,7 +72,7 @@ class BasicSettings(QWidget): # {{{
widget.addItem(human or key, key) widget.addItem(human or key, key)
def getter(w): def getter(w):
ans = unicode(w.itemData(w.currentIndex()).toString()) ans = unicode(w.itemData(w.currentIndex()) or '')
return {none_val:None}.get(ans, ans) return {none_val:None}.get(ans, ans)
def setter(w, val): def setter(w, val):

View File

@ -212,7 +212,7 @@ class NetworkAccessManager(QNetworkAccessManager):
self.cache.setMaximumCacheSize(0) self.cache.setMaximumCacheSize(0)
def createRequest(self, operation, request, data): def createRequest(self, operation, request, data):
url = unicode(request.url().toString()) url = unicode(request.url().toString(QUrl.None))
if operation == self.GetOperation and url.startswith('file://'): if operation == self.GetOperation and url.startswith('file://'):
path = url[7:] path = url[7:]
if iswindows and path.startswith('/'): if iswindows and path.startswith('/'):
@ -320,12 +320,13 @@ class WebPage(QWebPage):
def line_numbers(self): def line_numbers(self):
if self._line_numbers is None: if self._line_numbers is None:
def atoi(x): def atoi(x):
ans, ok = x.toUInt() try:
if not ok: ans = int(x)
except (TypeError, ValueError):
ans = None ans = None
return ans return ans
self._line_numbers = sorted(uniq(filter(lambda x:x is not None, map(atoi, self.mainFrame().evaluateJavaScript( val = self.mainFrame().evaluateJavaScript('window.calibre_preview_integration.line_numbers()')
'window.calibre_preview_integration.line_numbers()').toStringList())))) self._line_numbers = sorted(uniq(filter(lambda x:x is not None, map(atoi, val))))
return self._line_numbers return self._line_numbers
def go_to_line(self, lnum): def go_to_line(self, lnum):

View File

@ -14,12 +14,12 @@ from PyQt5.Qt import (
QWidget, QToolBar, Qt, QHBoxLayout, QSize, QIcon, QGridLayout, QLabel, QTimer, QWidget, QToolBar, Qt, QHBoxLayout, QSize, QIcon, QGridLayout, QLabel, QTimer,
QPushButton, pyqtSignal, QComboBox, QCheckBox, QSizePolicy, QVBoxLayout, QFont, QPushButton, pyqtSignal, QComboBox, QCheckBox, QSizePolicy, QVBoxLayout, QFont,
QLineEdit, QToolButton, QListView, QFrame, QApplication, QStyledItemDelegate, QLineEdit, QToolButton, QListView, QFrame, QApplication, QStyledItemDelegate,
QAbstractListModel, QVariant, QFormLayout, QModelIndex, QMenu, QItemSelection) QAbstractListModel, QFormLayout, QModelIndex, QMenu, QItemSelection)
import regex import regex
from calibre import prepare_string_for_xml from calibre import prepare_string_for_xml
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, choose_save_file from calibre.gui2 import error_dialog, info_dialog, choose_files, choose_save_file
from calibre.gui2.dialogs.message_box import MessageBox from calibre.gui2.dialogs.message_box import MessageBox
from calibre.gui2.widgets2 import HistoryComboBox from calibre.gui2.widgets2 import HistoryComboBox
from calibre.gui2.tweak_book import tprefs, editors, current_container from calibre.gui2.tweak_book import tprefs, editors, current_container
@ -403,17 +403,17 @@ class SearchesModel(QAbstractListModel):
try: try:
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
search = self.searches[self.filtered_searches[index.row()]] search = self.searches[self.filtered_searches[index.row()]]
return QVariant(search['name']) return search['name']
if role == Qt.ToolTipRole: if role == Qt.ToolTipRole:
search = self.searches[self.filtered_searches[index.row()]] search = self.searches[self.filtered_searches[index.row()]]
tt = '\n'.join((search['find'], search['replace'])) tt = '\n'.join((search['find'], search['replace']))
return QVariant(tt) return tt
if role == Qt.UserRole: if role == Qt.UserRole:
search = self.searches[self.filtered_searches[index.row()]] search = self.searches[self.filtered_searches[index.row()]]
return QVariant((self.filtered_searches[index.row()], search)) return (self.filtered_searches[index.row()], search)
except IndexError: except IndexError:
pass pass
return NONE return None
def do_filter(self, text): def do_filter(self, text):
text = unicode(text) text = unicode(text)
@ -696,7 +696,7 @@ class SavedSearches(Dialog):
seen.add(index.row()) seen.add(index.row())
search = SearchWidget.DEFAULT_STATE.copy() search = SearchWidget.DEFAULT_STATE.copy()
del search['mode'] del search['mode']
search_index, s = index.data(Qt.UserRole).toPyObject() search_index, s = index.data(Qt.UserRole)
search.update(s) search.update(s)
search['wrap'] = self.wrap search['wrap'] = self.wrap
search['direction'] = self.direction search['direction'] = self.direction
@ -722,7 +722,7 @@ class SavedSearches(Dialog):
def edit_search(self): def edit_search(self):
index = self.searches.currentIndex() index = self.searches.currentIndex()
if index.isValid(): if index.isValid():
search_index, search = index.data(Qt.UserRole).toPyObject() search_index, search = index.data(Qt.UserRole)
d = EditSearch(search=search, search_index=search_index, parent=self) d = EditSearch(search=search, search_index=search_index, parent=self)
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
self.model.dataChanged.emit(index, index) self.model.dataChanged.emit(index, index)
@ -753,7 +753,7 @@ class SavedSearches(Dialog):
self.description.setText(' \n \n ') self.description.setText(' \n \n ')
i = self.searches.currentIndex() i = self.searches.currentIndex()
if i.isValid(): if i.isValid():
search_index, search = i.data(Qt.UserRole).toPyObject() search_index, search = i.data(Qt.UserRole)
cs = '' if search.get('case_sensitive', SearchWidget.DEFAULT_STATE['case_sensitive']) else '' cs = '' if search.get('case_sensitive', SearchWidget.DEFAULT_STATE['case_sensitive']) else ''
da = '' if search.get('dot_all', SearchWidget.DEFAULT_STATE['dot_all']) else '' da = '' if search.get('dot_all', SearchWidget.DEFAULT_STATE['dot_all']) else ''
if search.get('mode', SearchWidget.DEFAULT_STATE['mode']) == 'regex': if search.get('mode', SearchWidget.DEFAULT_STATE['mode']) == 'regex':
@ -799,7 +799,7 @@ class SavedSearches(Dialog):
else: else:
searches = [] searches = []
for index in self.searches.selectionModel().selectedIndexes(): for index in self.searches.selectionModel().selectedIndexes():
search = index.data(Qt.UserRole).toPyObject()[-1] search = index.data(Qt.UserRole)[-1]
searches.append(search.copy()) searches.append(search.copy())
if not searches: if not searches:
return error_dialog(self, _('No searches'), _( return error_dialog(self, _('No searches'), _(

View File

@ -275,7 +275,7 @@ class ManageUserDictionaries(Dialog):
d = self.dictionaries.currentItem() d = self.dictionaries.currentItem()
if d is None: if d is None:
return return
return d.data(Qt.UserRole).toPyObject() return d.data(Qt.UserRole)
def active_toggled(self): def active_toggled(self):
d = self.current_dictionary d = self.current_dictionary
@ -283,7 +283,7 @@ class ManageUserDictionaries(Dialog):
dictionaries.mark_user_dictionary_as_active(d.name, self.is_active.isChecked()) dictionaries.mark_user_dictionary_as_active(d.name, self.is_active.isChecked())
self.dictionaries_changed = True self.dictionaries_changed = True
for item in (self.dictionaries.item(i) for i in xrange(self.dictionaries.count())): for item in (self.dictionaries.item(i) for i in xrange(self.dictionaries.count())):
d = item.data(Qt.UserRole).toPyObject() d = item.data(Qt.UserRole)
item.setData(Qt.FontRole, self.emph_font if d.is_active else None) item.setData(Qt.FontRole, self.emph_font if d.is_active else None)
def show_current_dictionary(self, *args): def show_current_dictionary(self, *args):
@ -364,7 +364,7 @@ class ManageUserDictionaries(Dialog):
self.dictionaries_changed = True self.dictionaries_changed = True
def remove_word(self): def remove_word(self):
words = {i.data(Qt.UserRole).toPyObject() for i in self.words.selectedItems()} words = {i.data(Qt.UserRole) for i in self.words.selectedItems()}
if words: if words:
kwords = [(w, DictionaryLocale(l, None)) for w, l in words] kwords = [(w, DictionaryLocale(l, None)) for w, l in words]
d = self.current_dictionary d = self.current_dictionary
@ -376,7 +376,7 @@ class ManageUserDictionaries(Dialog):
def find_word(self, word, lang): def find_word(self, word, lang):
key = (word, lang) key = (word, lang)
for i in xrange(self.words.count()): for i in xrange(self.words.count()):
if self.words.item(i).data(Qt.UserRole).toPyObject() == key: if self.words.item(i).data(Qt.UserRole) == key:
return i return i
return -1 return -1
@ -454,7 +454,7 @@ class ManageDictionaries(Dialog): # {{{
def data_changed(self, item, column): def data_changed(self, item, column):
if column == 0 and item.type() == DICTIONARY: if column == 0 and item.type() == DICTIONARY:
d = item.data(0, Qt.UserRole).toPyObject() d = item.data(0, Qt.UserRole)
if not d.builtin and unicode(item.text(0)) != d.name: if not d.builtin and unicode(item.text(0)) != d.name:
rename_dictionary(d, unicode(item.text(0))) rename_dictionary(d, unicode(item.text(0)))
@ -504,7 +504,7 @@ class ManageDictionaries(Dialog): # {{{
def remove_dictionary(self): def remove_dictionary(self):
item = self.dictionaries.currentItem() item = self.dictionaries.currentItem()
if item is not None and item.type() == DICTIONARY: if item is not None and item.type() == DICTIONARY:
dic = item.data(0, Qt.UserRole).toPyObject() dic = item.data(0, Qt.UserRole)
if not dic.builtin: if not dic.builtin:
remove_dictionary(dic) remove_dictionary(dic)
self.build_dictionaries(reread=True) self.build_dictionaries(reread=True)
@ -534,7 +534,7 @@ class ManageDictionaries(Dialog): # {{{
def init_country(self, item): def init_country(self, item):
pc = self.pcb pc = self.pcb
font = item.data(0, Qt.FontRole).toPyObject() font = item.data(0, Qt.FontRole)
preferred = bool(font and font.bold()) preferred = bool(font and font.bold())
pc.setText((_( pc.setText((_(
'This is already the preferred variant for the {1} language') if preferred else _( 'This is already the preferred variant for the {1} language') if preferred else _(
@ -548,20 +548,20 @@ class ManageDictionaries(Dialog): # {{{
bf.setBold(True) bf.setBold(True)
for x in (item.parent().child(i) for i in xrange(item.parent().childCount())): for x in (item.parent().child(i) for i in xrange(item.parent().childCount())):
x.setData(0, Qt.FontRole, bf if x is item else None) x.setData(0, Qt.FontRole, bf if x is item else None)
lc = unicode(item.parent().data(0, Qt.UserRole).toPyObject()) lc = unicode(item.parent().data(0, Qt.UserRole))
pl = dprefs['preferred_locales'] pl = dprefs['preferred_locales']
pl[lc] = '%s-%s' % (lc, unicode(item.data(0, Qt.UserRole).toPyObject())) pl[lc] = '%s-%s' % (lc, unicode(item.data(0, Qt.UserRole)))
dprefs['preferred_locales'] = pl dprefs['preferred_locales'] = pl
def init_dictionary(self, item): def init_dictionary(self, item):
saf = self.fb saf = self.fb
font = item.data(0, Qt.FontRole).toPyObject() font = item.data(0, Qt.FontRole)
preferred = bool(font and font.italic()) preferred = bool(font and font.italic())
saf.setText((_( saf.setText((_(
'This is already the preferred dictionary') if preferred else 'This is already the preferred dictionary') if preferred else
_('Use this as the preferred dictionary'))) _('Use this as the preferred dictionary')))
saf.setEnabled(not preferred) saf.setEnabled(not preferred)
self.remove_dictionary_button.setEnabled(not item.data(0, Qt.UserRole).toPyObject().builtin) self.remove_dictionary_button.setEnabled(not item.data(0, Qt.UserRole).builtin)
def set_favorite(self): def set_favorite(self):
item = self.dictionaries.currentItem() item = self.dictionaries.currentItem()
@ -569,9 +569,9 @@ class ManageDictionaries(Dialog): # {{{
bf.setItalic(True) bf.setItalic(True)
for x in (item.parent().child(i) for i in xrange(item.parent().childCount())): for x in (item.parent().child(i) for i in xrange(item.parent().childCount())):
x.setData(0, Qt.FontRole, bf if x is item else None) x.setData(0, Qt.FontRole, bf if x is item else None)
cc = unicode(item.parent().data(0, Qt.UserRole).toPyObject()) cc = unicode(item.parent().data(0, Qt.UserRole))
lc = unicode(item.parent().parent().data(0, Qt.UserRole).toPyObject()) lc = unicode(item.parent().parent().data(0, Qt.UserRole))
d = item.data(0, Qt.UserRole).toPyObject() d = item.data(0, Qt.UserRole)
locale = '%s-%s' % (lc, cc) locale = '%s-%s' % (lc, cc)
pl = dprefs['preferred_dictionaries'] pl = dprefs['preferred_dictionaries']
pl[locale] = d.id pl[locale] = d.id

View File

@ -182,8 +182,8 @@ class TOCViewer(QWidget):
def emit_navigate(self, *args): def emit_navigate(self, *args):
item = self.view.currentItem() item = self.view.currentItem()
if item is not None: if item is not None:
dest = unicode(item.data(0, DEST_ROLE).toString()) dest = unicode(item.data(0, DEST_ROLE) or '')
frag = unicode(item.data(0, FRAG_ROLE).toString()) frag = unicode(item.data(0, FRAG_ROLE) or '')
if not frag: if not frag:
frag = TOP frag = TOP
self.navigate_requested.emit(dest, frag) self.navigate_requested.emit(dest, frag)

View File

@ -9,10 +9,10 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import shutil import shutil
from PyQt5.Qt import ( from PyQt5.Qt import (
QAbstractListModel, Qt, QModelIndex, QVariant, QApplication, QWidget, QAbstractListModel, Qt, QModelIndex, QApplication, QWidget,
QGridLayout, QListView, QStyledItemDelegate, pyqtSignal, QPushButton, QIcon) QGridLayout, QListView, QStyledItemDelegate, pyqtSignal, QPushButton, QIcon)
from calibre.gui2 import NONE, error_dialog from calibre.gui2 import error_dialog
ROOT = QModelIndex() ROOT = QModelIndex()
@ -44,14 +44,14 @@ class GlobalUndoHistory(QAbstractListModel):
def data(self, index, role=Qt.DisplayRole): def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
return QVariant(self.label_for_row(index.row())) return self.label_for_row(index.row())
if role == Qt.FontRole and index.row() == self.pos: if role == Qt.FontRole and index.row() == self.pos:
f = QApplication.instance().font() f = QApplication.instance().font()
f.setBold(True) f.setBold(True)
return QVariant(f) return f
if role == Qt.UserRole: if role == Qt.UserRole:
return QVariant(self.states[index.row()]) return self.states[index.row()]
return NONE return None
def label_for_row(self, row): def label_for_row(self, row):
msg = self.states[row].message msg = self.states[row].message

View File

@ -14,9 +14,9 @@ from PyQt5.Qt import (
QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QVBoxLayout, QDialog, QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QVBoxLayout,
QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt, QWidget, QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt, QWidget,
QPoint, QSizePolicy, QPainter, QStaticText, pyqtSignal, QTextOption, QPoint, QSizePolicy, QPainter, QStaticText, pyqtSignal, QTextOption,
QAbstractListModel, QModelIndex, QVariant, QStyledItemDelegate, QStyle, QAbstractListModel, QModelIndex, QStyledItemDelegate, QStyle, QCheckBox,
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QCheckBox, QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QGroupBox,
QSplitter, QPixmap, QRect, QGroupBox) QSplitter, QPixmap, QRect)
from calibre import prepare_string_for_xml, human_readable from calibre import prepare_string_for_xml, human_readable
from calibre.ebooks.oeb.polish.utils import lead_text, guess_type from calibre.ebooks.oeb.polish.utils import lead_text, guess_type
@ -502,7 +502,7 @@ class NamesDelegate(QStyledItemDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index) QStyledItemDelegate.paint(self, painter, option, index)
text, positions = index.data(Qt.UserRole).toPyObject() text, positions = index.data(Qt.UserRole)
self.initStyleOption(option, index) self.initStyleOption(option, index)
painter.save() painter.save()
painter.setFont(option.font) painter.setFont(option.font)
@ -551,9 +551,9 @@ class NamesModel(QAbstractListModel):
def data(self, index, role): def data(self, index, role):
if role == Qt.UserRole: if role == Qt.UserRole:
return QVariant(self.items[index.row()]) return self.items[index.row()]
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
return QVariant('\xa0' * 20) return '\xa0' * 20
def filter(self, query): def filter(self, query):
query = unicode(query or '') query = unicode(query or '')
@ -684,7 +684,7 @@ class InsertLink(Dialog):
if not rows: if not rows:
self.anchor_names.model().set_names([]) self.anchor_names.model().set_names([])
else: else:
name, positions = self.file_names.model().data(rows[0], Qt.UserRole).toPyObject() name, positions = self.file_names.model().data(rows[0], Qt.UserRole)
self.populate_anchors(name) self.populate_anchors(name)
def populate_anchors(self, name): def populate_anchors(self, name):
@ -704,7 +704,7 @@ class InsertLink(Dialog):
rows = list(self.file_names.selectionModel().selectedRows()) rows = list(self.file_names.selectionModel().selectedRows())
if not rows: if not rows:
return return
name = self.file_names.model().data(rows[0], Qt.UserRole).toPyObject()[0] name = self.file_names.model().data(rows[0], Qt.UserRole)[0]
if name == self.source_name: if name == self.source_name:
href = '' href = ''
else: else:
@ -843,7 +843,7 @@ class InsertSemantics(Dialog):
d.exec_() d.exec_()
def semantic_type_changed(self): def semantic_type_changed(self):
item_type = unicode(self.semantic_type.itemData(self.semantic_type.currentIndex()).toString()) item_type = unicode(self.semantic_type.itemData(self.semantic_type.currentIndex()) or '')
name, frag = self.final_type_map.get(item_type, (None, None)) name, frag = self.final_type_map.get(item_type, (None, None))
self.show_type(name, frag) self.show_type(name, frag)
@ -869,7 +869,7 @@ class InsertSemantics(Dialog):
def target_text_changed(self): def target_text_changed(self):
name, frag = unicode(self.target.text()).partition('#')[::2] name, frag = unicode(self.target.text()).partition('#')[::2]
item_type = unicode(self.semantic_type.itemData(self.semantic_type.currentIndex()).toString()) item_type = unicode(self.semantic_type.itemData(self.semantic_type.currentIndex()) or '')
self.final_type_map[item_type] = (name, frag or None) self.final_type_map[item_type] = (name, frag or None)
def selected_file_changed(self, *args): def selected_file_changed(self, *args):
@ -877,7 +877,7 @@ class InsertSemantics(Dialog):
if not rows: if not rows:
self.anchor_names.model().set_names([]) self.anchor_names.model().set_names([])
else: else:
name, positions = self.file_names.model().data(rows[0], Qt.UserRole).toPyObject() name, positions = self.file_names.model().data(rows[0], Qt.UserRole)
self.populate_anchors(name) self.populate_anchors(name)
def populate_anchors(self, name): def populate_anchors(self, name):
@ -893,12 +893,12 @@ class InsertSemantics(Dialog):
rows = list(self.file_names.selectionModel().selectedRows()) rows = list(self.file_names.selectionModel().selectedRows())
if not rows: if not rows:
return return
name = self.file_names.model().data(rows[0], Qt.UserRole).toPyObject()[0] name = self.file_names.model().data(rows[0], Qt.UserRole)[0]
href = name href = name
frag = '' frag = ''
rows = list(self.anchor_names.selectionModel().selectedRows()) rows = list(self.anchor_names.selectionModel().selectedRows())
if rows: if rows:
anchor = self.anchor_names.model().data(rows[0], Qt.UserRole).toPyObject()[0] anchor = self.anchor_names.model().data(rows[0], Qt.UserRole)[0]
if anchor: if anchor:
frag = '#' + anchor frag = '#' + anchor
href += frag href += frag