mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Displaying characters works (apart from non-printable chars)
This commit is contained in:
parent
a06038f078
commit
fb7118814f
@ -6,14 +6,18 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import unicodedata
|
||||||
|
from bisect import bisect
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QAbstractItemModel, QModelIndex, Qt, QVariant, pyqtSignal, QApplication,
|
QAbstractItemModel, QModelIndex, Qt, QVariant, pyqtSignal, QApplication,
|
||||||
QTreeView, QSize, QGridLayout)
|
QTreeView, QSize, QGridLayout, QAbstractListModel, QListView,
|
||||||
|
QStyledItemDelegate, QSplitter, QLabel, QSizePolicy, QIcon)
|
||||||
|
|
||||||
from calibre.constants import ispy3, plugins
|
from calibre.constants import ispy3, plugins
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.tweak_book import tprefs
|
from calibre.gui2.tweak_book import tprefs
|
||||||
from calibre.gui2.tweak_book.insert_resource import Dialog
|
from calibre.gui2.tweak_book.editor.insert_resource import Dialog
|
||||||
|
|
||||||
if not ispy3:
|
if not ispy3:
|
||||||
chr = unichr
|
chr = unichr
|
||||||
@ -184,10 +188,10 @@ class CategoryModel(QAbstractItemModel):
|
|||||||
(_('East Asian Scripts'), (
|
(_('East Asian Scripts'), (
|
||||||
(_('Bopomofo'), (0x3100, 0x312F)),
|
(_('Bopomofo'), (0x3100, 0x312F)),
|
||||||
(_('Bopomofo Extended'), (0x31A0, 0x31BF)),
|
(_('Bopomofo Extended'), (0x31A0, 0x31BF)),
|
||||||
(_('CJK Unified Ideographs (big!)'), (0x4E00, 0x9FFF)),
|
(_('CJK Unified Ideographs'), (0x4E00, 0x9FFF)),
|
||||||
(_('CJK Unified Ideographs Extension-A (big!)'), (0x3400, 0x4DBF)),
|
(_('CJK Unified Ideographs Extension-A'), (0x3400, 0x4DBF)),
|
||||||
(_('CJK Unified Ideographs Extension B (big!)'), (0x20000, 0x2A6DF)),
|
(_('CJK Unified Ideographs Extension B'), (0x20000, 0x2A6DF)),
|
||||||
(_('CJK Unified Ideographs Extension C (big!)'), (0x2A700, 0x2B73F)),
|
(_('CJK Unified Ideographs Extension C'), (0x2A700, 0x2B73F)),
|
||||||
(_('CJK Unified Ideographs Extension D'), (0x2B740, 0x2B81F)),
|
(_('CJK Unified Ideographs Extension D'), (0x2B740, 0x2B81F)),
|
||||||
(_('CJK Compatibility Ideographs'), (0xF900, 0xFAFF)),
|
(_('CJK Compatibility Ideographs'), (0xF900, 0xFAFF)),
|
||||||
(_('CJK Compatibility Ideographs Supplement'), (0x2F800, 0x2FA1F)),
|
(_('CJK Compatibility Ideographs Supplement'), (0x2F800, 0x2FA1F)),
|
||||||
@ -201,7 +205,7 @@ class CategoryModel(QAbstractItemModel):
|
|||||||
(_('Kana Supplement'), (0x1B000, 0x1B0FF)),
|
(_('Kana Supplement'), (0x1B000, 0x1B0FF)),
|
||||||
(_('Halfwidth Katakana'), (0xFF65, 0xFF9F)),
|
(_('Halfwidth Katakana'), (0xFF65, 0xFF9F)),
|
||||||
(_('Kanbun'), (0x3190, 0x319F)),
|
(_('Kanbun'), (0x3190, 0x319F)),
|
||||||
(_('Hangul Syllables (big!)'), (0xAC00, 0xD7AF)),
|
(_('Hangul Syllables'), (0xAC00, 0xD7AF)),
|
||||||
(_('Hangul Jamo'), (0x1100, 0x11FF)),
|
(_('Hangul Jamo'), (0x1100, 0x11FF)),
|
||||||
(_('Hangul Jamo Extended-A'), (0xA960, 0xA97F)),
|
(_('Hangul Jamo Extended-A'), (0xA960, 0xA97F)),
|
||||||
(_('Hangul Jamo Extended-B'), (0xD7B0, 0xD7FF)),
|
(_('Hangul Jamo Extended-B'), (0xD7B0, 0xD7FF)),
|
||||||
@ -328,6 +332,17 @@ class CategoryModel(QAbstractItemModel):
|
|||||||
)),
|
)),
|
||||||
) # }}}
|
) # }}}
|
||||||
|
|
||||||
|
self.category_map = {}
|
||||||
|
self.starts = []
|
||||||
|
for tlname, items in self.categories[1:]:
|
||||||
|
for name, (start, end) in items:
|
||||||
|
self.category_map[start] = (tlname, name)
|
||||||
|
self.starts.append(start)
|
||||||
|
self.starts.sort()
|
||||||
|
self.bold_font = f = QApplication.font()
|
||||||
|
f.setBold(True)
|
||||||
|
self.fav_icon = QIcon(I('rating.png'))
|
||||||
|
|
||||||
def columnCount(self, parent=ROOT):
|
def columnCount(self, parent=ROOT):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@ -363,6 +378,10 @@ class CategoryModel(QAbstractItemModel):
|
|||||||
if pid == 0:
|
if pid == 0:
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
return QVariant(self.categories[index.row()][0])
|
return QVariant(self.categories[index.row()][0])
|
||||||
|
if role == Qt.FontRole:
|
||||||
|
return QVariant(self.bold_font)
|
||||||
|
if role == Qt.DecorationRole and index.row() == 0:
|
||||||
|
return QVariant(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()]
|
||||||
@ -379,6 +398,25 @@ class CategoryModel(QAbstractItemModel):
|
|||||||
item = self.categories[pid - 1][1][index.row()]
|
item = self.categories[pid - 1][1][index.row()]
|
||||||
return (item[0], list(xrange(item[1][0], item[1][1] + 1)))
|
return (item[0], list(xrange(item[1][0], item[1][1] + 1)))
|
||||||
|
|
||||||
|
def get_char_info(self, char_code):
|
||||||
|
ipos = bisect(self.starts, char_code) - 1
|
||||||
|
try:
|
||||||
|
category, subcategory = self.category_map[self.starts[ipos]]
|
||||||
|
except IndexError:
|
||||||
|
category = subcategory = _('Unknown')
|
||||||
|
return category, subcategory, unicodedata.name(chr(char_code), _('Unknown'))
|
||||||
|
|
||||||
|
class CategoryDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QStyledItemDelegate.__init__(self, parent)
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
ans = QStyledItemDelegate.sizeHint(self, option, index)
|
||||||
|
if not index.parent().isValid():
|
||||||
|
ans += QSize(0, 6)
|
||||||
|
return ans
|
||||||
|
|
||||||
class CategoryView(QTreeView):
|
class CategoryView(QTreeView):
|
||||||
|
|
||||||
category_selected = pyqtSignal(object, object)
|
category_selected = pyqtSignal(object, object)
|
||||||
@ -386,6 +424,7 @@ class CategoryView(QTreeView):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTreeView.__init__(self, parent)
|
QTreeView.__init__(self, parent)
|
||||||
self.setHeaderHidden(True)
|
self.setHeaderHidden(True)
|
||||||
|
self.setAnimated(True)
|
||||||
self.activated.connect(self.item_activated)
|
self.activated.connect(self.item_activated)
|
||||||
self.clicked.connect(self.item_activated)
|
self.clicked.connect(self.item_activated)
|
||||||
pi = plugins['progress_indicator'][0]
|
pi = plugins['progress_indicator'][0]
|
||||||
@ -402,8 +441,86 @@ class CategoryView(QTreeView):
|
|||||||
if not self.initialized:
|
if not self.initialized:
|
||||||
self._model = m = CategoryModel(self)
|
self._model = m = CategoryModel(self)
|
||||||
self.setModel(m)
|
self.setModel(m)
|
||||||
|
self.item_activated(m.index(0, 0))
|
||||||
|
self._delegate = CategoryDelegate(self)
|
||||||
|
self.setItemDelegate(self._delegate)
|
||||||
self.initialized = True
|
self.initialized = True
|
||||||
|
|
||||||
|
class CharModel(QAbstractListModel):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QAbstractListModel.__init__(self, parent)
|
||||||
|
self.chars = []
|
||||||
|
|
||||||
|
def rowCount(self, parent=ROOT):
|
||||||
|
return len(self.chars)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.UserRole and -1 < index.row() < len(self.chars):
|
||||||
|
return QVariant(self.chars[index.row()])
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
class CharDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QStyledItemDelegate.__init__(self, parent)
|
||||||
|
self.item_size = QSize(32, 32)
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
return self.item_size
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
QStyledItemDelegate.paint(self, painter, option, index)
|
||||||
|
charcode, ok = index.data(Qt.UserRole).toInt()
|
||||||
|
if not ok:
|
||||||
|
return
|
||||||
|
char = chr(charcode)
|
||||||
|
painter.save()
|
||||||
|
try:
|
||||||
|
f = option.font
|
||||||
|
f.setPixelSize(self.item_size.height() - 8)
|
||||||
|
painter.setFont(f)
|
||||||
|
painter.drawText(option.rect, Qt.AlignHCenter | Qt.AlignBottom | Qt.TextSingleLine, char)
|
||||||
|
finally:
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
class CharView(QListView):
|
||||||
|
|
||||||
|
show_name = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
self.last_mouse_idx = -1
|
||||||
|
QListView.__init__(self, parent)
|
||||||
|
self._model = CharModel(self)
|
||||||
|
self.setModel(self._model)
|
||||||
|
self.delegate = CharDelegate(self)
|
||||||
|
self.setItemDelegate(self.delegate)
|
||||||
|
self.setFlow(self.LeftToRight)
|
||||||
|
self.setWrapping(True)
|
||||||
|
self.setMouseTracking(True)
|
||||||
|
self.setSpacing(2)
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
|
||||||
|
def show_chars(self, name, codes):
|
||||||
|
self._model.chars = codes
|
||||||
|
self._model.reset()
|
||||||
|
self.scrollToTop()
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, ev):
|
||||||
|
index = self.indexAt(ev.pos())
|
||||||
|
if index.isValid():
|
||||||
|
row = index.row()
|
||||||
|
if row != self.last_mouse_idx:
|
||||||
|
self.last_mouse_idx = row
|
||||||
|
char_code, ok = self.model().data(index, Qt.UserRole).toInt()
|
||||||
|
if ok:
|
||||||
|
self.show_name.emit(char_code)
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
else:
|
||||||
|
self.setCursor(Qt.ArrowCursor)
|
||||||
|
self.show_name.emit(-1)
|
||||||
|
self.last_mouse_idx = -1
|
||||||
|
|
||||||
class CharSelect(Dialog):
|
class CharSelect(Dialog):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -415,8 +532,19 @@ class CharSelect(Dialog):
|
|||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
self.bb.setStandardButtons(self.bb.Close)
|
self.bb.setStandardButtons(self.bb.Close)
|
||||||
|
|
||||||
|
self.splitter = s = QSplitter(self)
|
||||||
|
s.setChildrenCollapsible(False)
|
||||||
|
|
||||||
self.category_view = CategoryView(self)
|
self.category_view = CategoryView(self)
|
||||||
l.addWidget(self.category_view)
|
l.addWidget(s)
|
||||||
|
self.char_view = CharView(self)
|
||||||
|
self.category_view.category_selected.connect(self.char_view.show_chars)
|
||||||
|
self.char_view.show_name.connect(self.show_char_info)
|
||||||
|
s.addWidget(self.category_view), s.addWidget(self.char_view)
|
||||||
|
|
||||||
|
self.char_info = la = QLabel('\xa0')
|
||||||
|
la.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
l.addWidget(la)
|
||||||
l.addWidget(self.bb)
|
l.addWidget(self.bb)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@ -426,6 +554,13 @@ class CharSelect(Dialog):
|
|||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return QSize(800, 600)
|
return QSize(800, 600)
|
||||||
|
|
||||||
|
def show_char_info(self, char_code):
|
||||||
|
if char_code > 0:
|
||||||
|
category_name, subcategory_name, character_name = self.category_view.model().get_char_info(char_code)
|
||||||
|
self.char_info.setText('%s - %s - %s (U+%s)' % (category_name, subcategory_name, character_name, hex(char_code)))
|
||||||
|
else:
|
||||||
|
self.char_info.clear()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
self.initialize()
|
self.initialize()
|
||||||
Dialog.show(self)
|
Dialog.show(self)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user