1
0
mirror of https://github.com/KanjiVG/kanjivg.git synced 2026-01-25 07:33:12 +01:00
Files
kanjivg/viewer.py
2010-12-31 19:01:36 +09:00

449 lines
23 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 Alexandre Courbot
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys, os, xml.sax, re, codecs, datetime
from PyQt4 import QtGui, QtCore
from kanjivg import *
def loadKanji(code):
f = str(code)
svgHandler = SVGHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(svgHandler)
parser.setFeature(xml.sax.handler.feature_external_ges, False)
parser.setFeature(xml.sax.handler.feature_external_pes, False)
parser.parse(os.path.join("data", f + ".svg"))
kanji = svgHandler.kanjis.values()[0]
return kanji
from PyQt4.QtCore import QPointF
def svg2Path(svg):
'''Converts a SVG textual path into a QPainterPath'''
retPath = QtGui.QPainterPath()
# Add spaces between unseparated tokens
t = svg
t = re.sub(r"[a-zA-Z]\d|\d[a-zA-Z]", lambda(m): m.group(0)[0] + " " + m.group(0)[1], t)
t = re.sub(r"[a-zA-Z]\d|\d[a-zA-Z]", lambda(m): m.group(0)[0] + " " + m.group(0)[1], t)
t = re.sub(r"\-\d", lambda(m): " " + m.group(0), t)
tokens = re.split("[ ,]+", t)
# Convert to Qt path
i = 0
curAction = ''
while i < len(tokens):
if tokens[i] in ( "M", "m", "L", "l", "C", "c", "S", "s", "z", "Z" ):
curAction = tokens[i]
i += 1
if curAction in ( "M", "m" ):
dest = QPointF(float(tokens[i]), float(tokens[i + 1]))
if curAction == "m": dest += retPath.currentPosition()
retPath.moveTo(dest)
i += 2
lastControl = retPath.currentPosition()
elif curAction in ( "L", "l" ):
dest = QPointF(float(tokens[i]), float(tokens[i + 1]))
if curAction == "l": dest += retPath.currentPosition()
retPath.lineTo(dest)
i += 2
lastControl = retPath.currentPosition()
elif curAction in ( "C", "c" ):
p1 = QPointF(float(tokens[i]), float(tokens[i + 1]))
p2 = QPointF(float(tokens[i + 2]), float(tokens[i + 3]))
dest = QPointF(float(tokens[i + 4]), float(tokens[i + 5]))
if curAction == "c":
p1 += retPath.currentPosition()
p2 += retPath.currentPosition()
dest += retPath.currentPosition()
retPath.cubicTo(p1, p2, dest)
i += 6
lastControl = p2
elif curAction in ( "S", "s" ):
p1 = retPath.currentPosition() * 2 - lastControl
p2 = QPointF(float(tokens[i]), float(tokens[i + 1]))
dest = QPointF(float(tokens[i + 2]), float(tokens[i + 3]))
if curAction == "s":
p2 += retPath.currentPosition()
dest += retPath.currentPosition()
retPath.cubicTo(p1, p2, dest)
i += 4
lastControl = p2
elif curAction in ( "Z", "z" ):
retPath.closeSubPath()
lastControl = retPath.currentPosition()
else:
print "Unknown command %s while computing kanji path!" % ( tokens[i], )
i += 1
return retPath;
class SimpleStrokeRenderer:
def __init__(self):
self.strokesPen = QtGui.QPen()
self.strokesPen.setWidth(2)
def renderStroke(self, painter, stroke):
painter.setPen(self.strokesPen)
painter.drawPath(stroke.qPath)
class FancyStrokeRenderer:
basicWeight = 1
weightFactor = 1
weights = {
u'' : (19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6),
u'a' : (17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 8, 10, 12, 14),
u'' : (9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 15, 15, 14, 13, 12, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8),
u'㇐a' : (11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 14, 14, 13, 12, 11, 10),
u'㇐b' : (10, 10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 13, 12, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8),
u'㇐c' : (9, 9, 9, 9, 9, 9, 10, 10,10, 10, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 15, 14, 13, 12, 11, 10, 9),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9),
u'㇑a' : (9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 13, 13, 13, 12, 12, 12, 12),
u'' : (8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26),
u'' : (19, 19, 18, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1),
u'㇏a' : (20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1),
u'' : (8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18),
u'㇖a' : (10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19),
u'㇖b' : (12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 14, 14, 13, 12, 11, 10, 9, 8, 7, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 20),
u'㇚a' : (18, 18, 17, 17, 16, 16, 15, 15, 14, 14, 13, 12, 11, 10, 9, 8, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 20),
u'㇚b' : (17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 8, 10, 12, 14),
u'' : (9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 13, 14, 14, 15, 15, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 5, 7, 9, 13, 15, 17, 19, 21, 23),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20, 20),
u'' : (9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 15, 14, 13, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9),
u'㇕a' : (10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 8, 8, 9, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 15, 14, 13, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9),
u'㇕b' : (10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 8, 8, 9, 10, 11, 12, 13, 13, 14, 14, 15, 15, 14, 14, 13, 12, 11, 10),
u'㇕c' : (9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 12, 13, 13, 14, 14, 15, 15, 14, 13, 12, 11, 10, 10),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8),
u'㇗a' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 12, 12, 12, 11, 11, 11),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 9, 9, 9, 10, 11, 12, 13, 14, 15, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18),
u'' : (8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26),
u'㇇a' : (10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8),
u'㇄a' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 13, 12, 11, 10),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21),
u'㇆a' : (10, 10, 11, 11, 12, 13, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21),
u'㇆b' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 11, 11, 11, 10, 10, 9, 9, 8, 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23),
u'㇟a' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9),
u'㇟b' : (19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23),
u'' : (),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 10, 9, 10, 9, 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 9, 10, 10, 11, 11, 12, 12, 14, 14, 13, 13, 12, 12, 11, 13, 11, 10, 10, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, 18, 19, 19),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 13, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 13, 12, 11, 11, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23),
u'㇈a' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 5, 7, 9, 13, 15, 17, 19, 21, 23),
u'㇈b' : (9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 11, 11, 11, 10, 9, 8, 7, 6, 5, 4, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23),
u'' : (8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 11, 11, 10, 9, 10, 9, 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 15, 16, 16, 16, 16, 15, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 9, 10, 10, 11, 11, 12, 12, 14, 14, 13, 13, 12, 12, 11, 13, 11, 10, 10),
}
def __init__(self):
self.fallback = SimpleStrokeRenderer()
self.strokesPen = QtGui.QPen()
self.strokesPen.setWidth(2)
def renderStroke(self, painter, stroke):
if stroke.stype in self.weights: layout = self.weights[stroke.stype]
elif stroke.stype[0] in self.weights: layout = self.weights[stroke.stype[0]]
else:
self.fallback.renderStroke(painter, stroke)
return
ppen = QtGui.QPen(self.strokesPen)
ppen.setCapStyle(QtCore.Qt.RoundCap)
segmentLength = stroke.qPath.length() / len(layout)
startPoint = 0.0
for i in range(len(layout)):
ppen.setWidthF(self.basicWeight + (3.8 - 0.1 * layout[i]) * self.weightFactor)
self.renderPartialStroke(painter, ppen, stroke.qPath, startPoint, segmentLength)
startPoint += segmentLength
def renderPartialStroke(self, painter, pen, stroke, startPoint, segmentLength):
painter.save()
ppen = QtGui.QPen(pen)
dashes = [ 0.0, startPoint / ppen.widthF(), segmentLength / ppen.widthF(), stroke.length() / ppen.widthF() ]
ppen.setDashPattern(dashes)
painter.setPen(ppen)
painter.drawPath(stroke)
painter.restore()
class StrokesWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.strokesRenderer = FancyStrokeRenderer()
self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding);
self.selectedStrokesPen = QtGui.QPen()
self.selectedStrokesPen.setWidth(2)
self.selectedStrokesPen.setColor(QtCore.Qt.red)
self.boundingPen = QtGui.QPen()
self.boundingPen.setStyle(QtCore.Qt.DotLine)
def setKanji(self, kanji):
self.kanji = kanji
self.__loadGroup(self.kanji.root)
def __loadGroup(self, group):
rect = QtCore.QRectF()
pwidth = 1.0
for child in group.childs:
if isinstance(child, StrokeGr):
self.__loadGroup(child)
else:
child.qPath = svg2Path(child.svg)
child.boundingRect = child.qPath.controlPointRect().adjusted(-pwidth, -pwidth, pwidth, pwidth)
rect |= child.boundingRect
group.boundingRect = rect
def paintEvent(self, event):
if not self.kanji: return
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
size = self.size()
painter.scale(size.width() / 109.0, size.height() / 109.0)
drawLater = []
self.__renderGroup(self.kanji.root, painter, drawLater)
painter.setPen(self.selectedStrokesPen)
for child in drawLater:
painter.drawPath(child.qPath)
# Also draw a tip to indicate the direction
lastPoint = child.qPath.pointAtPercent(1)
lastAngle = child.qPath.angleAtPercent(0.95)
line = QtCore.QLineF(0, 0, 4, 4)
line.translate(lastPoint)
line.setAngle(lastAngle + 150)
painter.drawLine(line)
line.setAngle(lastAngle - 150)
painter.drawLine(line)
def __renderGroup(self, group, painter, drawLater):
for child in group.childs:
if isinstance(child, StrokeGr):
self.__renderGroup(child, painter, drawLater)
if child in self.selection:
painter.setPen(self.boundingPen)
painter.drawRect(child.boundingRect)
else:
if child in self.selection: drawLater.append(child)
else:
self.strokesRenderer.renderStroke(painter, child)
class KanjiStructModel(QtCore.QAbstractItemModel):
columns = [ 'Element', 'Original', 'Position', 'Phon', 'Part' ]
StrokeRole = QtCore.Qt.UserRole
def __init__(self, kanji = None, parent = None):
QtCore.QAbstractItemModel.__init__(self, parent)
self.setKanji(kanji)
def setKanji(self, kanji):
self.kanji = kanji
def index(self, row, column, parent):
if not self.kanji: return QtCore.QModelIndex()
if not parent.isValid(): return self.createIndex(row, column, kanji.root)
group = parent.internalPointer().childs[parent.row()]
if not isinstance(group, StrokeGr) or row >= len(group.childs) or column >= len(KanjiStructModel.columns):
return QtCore.QModelIndex()
return self.createIndex(row, column, group)
def data(self, index, role):
if not self.kanji or not index.isValid(): return QtCore.QVariant()
item = index.internalPointer().childs[index.row()]
if role == KanjiStructModel.StrokeRole:
return item
column = index.column()
if isinstance(item, StrokeGr) and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if column == 0: return item.element
elif column == 1: return item.original
elif column == 2: return item.position
elif column == 3: return item.phon
elif column == 4: return item.part
elif isinstance(item, Stroke) and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
if column == 0: return item.stype
return QtCore.QVariant()
def setData(self, index, value, role):
if not self.kanji or not index.isValid(): return QtCore.QVariant()
item = index.internalPointer().childs[index.row()]
column = index.column()
if isinstance(item, StrokeGr) and role == QtCore.Qt.EditRole:
if column == 0: item.element = value
elif column == 1: item.original = value
elif column == 2: item.position = value
elif column == 3: item.phon = value
elif column == 4: item.part = value
else: return False
return True
elif isinstance(item, Stroke) and role == QtCore.Qt.EditRole:
if column == 0: item.type = value
else: return False
return True
return False
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole and section < len(KanjiStructModel.columns):
return KanjiStructModel.columns[section]
else: return QtCore.QVariant()
def flags(self, index):
if not index.isValid(): return 0
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def parent(self, index):
if not self.kanji or not index.isValid(): return QtCore.QModelIndex()
p = index.internalPointer()
if p == self.kanji.root: return QtCore.QModelIndex()
else: return self.createIndex(p.parent.childs.index(p), 0, p.parent)
def rowCount(self, parent):
if not self.kanji: return 0
if not parent.isValid(): return len(kanji.root.childs)
group = parent.internalPointer().childs[parent.row()]
if not isinstance(group, StrokeGr): return 0
return len(group.childs)
def columnCount(self, parent):
return len(KanjiStructModel.columns)
class KanjiStructDelegate(QtGui.QStyledItemDelegate):
positions = [ "", "top", "bottom", "left", "right", "tare" ]
def __init__(self, parent=None):
QtGui.QStyledItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
item = index.internalPointer().childs[index.row()]
if isinstance(item, StrokeGr):
if index.column() in (0, 1, 3):
ret = QtGui.QStyledItemDelegate.createEditor(self, parent, option, index)
elif index.column() == 2:
ret = QtGui.QComboBox(parent)
ret.addItems(KanjiStructDelegate.positions)
elif index.column() == 4:
ret = QtGui.QSpinBox(parent)
ret.setSpecialValueText("None")
ret.setMinimum(0)
ret.setMaximum(10)
elif isinstance(item, Stroke):
ret = QtGui.QStyledItemDelegate.createEditor(self, parent, option, index)
return ret
def setEditorData(self, editor, index):
item = index.internalPointer().childs[index.row()]
if isinstance(item, StrokeGr):
if index.column() in (0, 1, 3):
QtGui.QStyledItemDelegate.setEditorData(self, editor, index)
elif index.column() == 2:
pos = index.model().data(index, QtCore.Qt.EditRole)
if pos in KanjiStructDelegate.positions:
editor.setCurrentIndex(KanjiStructDelegate.positions.index(pos))
elif index.column() == 4:
val = index.model().data(index, QtCore.Qt.EditRole)
editor.setValue(val)
elif isinstance(item, Stroke):
QtGui.QStyledItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
if index.column() in (0, 1, 3):
QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
elif index.column() == 2:
model.setData(index, editor.currentText(), QtCore.Qt.EditRole)
elif index.column() == 4:
val = editor.value()
if val == 0: val = None
model.setData(index, val, QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class KanjiStructView(QtGui.QTreeView):
__pyqtSignals__ = ("selectionChanged()")
def __init__(self, parent=None):
QtGui.QTreeView.__init__(self, parent)
def selectionChanged(self, selected, deselected):
QtGui.QTreeView.selectionChanged(self, selected, deselected)
self.emit(QtCore.SIGNAL("selectionChanged()"))
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowTitle('KanjiVG viewer')
self.canvas = StrokesWidget(self)
self.canvas.selection = []
self.kanjiModel = KanjiStructModel(self)
self.structure = KanjiStructView(self)
self.structure.setItemDelegate(KanjiStructDelegate())
self.structure.setModel(self.kanjiModel)
hLayout = QtGui.QHBoxLayout()
hLayout.addWidget(self.canvas)
hLayout.addWidget(self.structure)
self.setLayout(hLayout)
self.connect(self.structure, QtCore.SIGNAL('selectionChanged()'), self.onSelectionChanged)
def setKanji(self, kanji):
self.canvas.setKanji(kanji)
self.kanjiModel.setKanji(kanji)
def onSelectionChanged(self):
self.canvas.selection = []
for index in self.structure.selectedIndexes():
self.canvas.selection.append(index.model().data(index, KanjiStructModel.StrokeRole))
self.canvas.update()
from createsvgfiles import createSVG
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit(0)
kanji = loadKanji(sys.argv[1])
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.resize(500, 400)
mw.setKanji(kanji)
mw.show()
ret = app.exec_()
createSVG(codecs.open('out.svg', 'w', 'utf-8'), kanji)
sys.exit(ret)