Checkpoint, not working
This commit is contained in:
671
lib/definition.py
Normal file
671
lib/definition.py
Normal file
@@ -0,0 +1,671 @@
|
||||
import re
|
||||
import copy
|
||||
from typing import Any, Optional, cast
|
||||
import re
|
||||
from PyQt6.QtCore import QMargins, QPoint, QRect, QSize, QUrl, Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QColor, QFont, QFontMetrics, QMouseEvent, QPaintEvent, QPainter, QResizeEvent, QTextOption, QTransform, QBrush
|
||||
from PyQt6.QtWidgets import QWidget
|
||||
|
||||
class Fragment:
|
||||
"""A fragment of text to be displayed"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: str,
|
||||
font: QFont,
|
||||
audio: str = "",
|
||||
color: Optional[QColor] = None,
|
||||
asis: bool = False,
|
||||
) -> None:
|
||||
self._text = text
|
||||
self._font = font
|
||||
self._audio: QUrl = QUrl(audio)
|
||||
self._align = QTextOption(
|
||||
Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBaseline
|
||||
)
|
||||
self._padding = QMargins()
|
||||
self._border = QMargins()
|
||||
self._margin = QMargins()
|
||||
self._wref = ""
|
||||
self._position = QPoint()
|
||||
self._rect = QRect()
|
||||
self._borderRect = QRect()
|
||||
self._clickRect = QRect()
|
||||
if color:
|
||||
self._color = color
|
||||
else:
|
||||
self._color = QColor()
|
||||
self._background = QColor()
|
||||
self._asis = asis
|
||||
self._left = 0
|
||||
self._target = "word"
|
||||
return
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
def size(self, width: int) -> QSize:
|
||||
rect = QRect(self._position, QSize(width - self._position.x(), 2000))
|
||||
flags = (
|
||||
Qt.AlignmentFlag.AlignLeft
|
||||
| Qt.AlignmentFlag.AlignBaseline
|
||||
| Qt.TextFlag.TextWordWrap
|
||||
)
|
||||
fm = QFontMetrics(self._font)
|
||||
bounding = fm.boundingRect(rect, flags, self._text)
|
||||
size = bounding.size()
|
||||
size = size.grownBy(self._padding)
|
||||
size = size.grownBy(self._border)
|
||||
size = size.grownBy(self._margin)
|
||||
return size
|
||||
|
||||
def height(self, width: int) -> int:
|
||||
return self.size(width).height()
|
||||
|
||||
def width(self, width: int) -> int:
|
||||
return self.size(width).width()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"({self._position.x()}, {self._position.y()}): {self._text}"
|
||||
|
||||
def repaintEvent(self, painter: QPainter) -> int:
|
||||
painter.save()
|
||||
painter.setFont(self._font)
|
||||
painter.setPen(self._color)
|
||||
rect = QRect()
|
||||
rect.setLeft(self._position.x())
|
||||
rect.setTop(
|
||||
self._position.y()
|
||||
+ painter.fontMetrics().descent()
|
||||
- painter.fontMetrics().height()
|
||||
)
|
||||
rect.setWidth(painter.viewport().width() - self._position.x())
|
||||
rect.setHeight(2000)
|
||||
flags = (
|
||||
Qt.AlignmentFlag.AlignLeft
|
||||
| Qt.AlignmentFlag.AlignBaseline
|
||||
| Qt.TextFlag.TextWordWrap
|
||||
)
|
||||
bounding = painter.boundingRect(rect, flags, self._text)
|
||||
size = bounding.size()
|
||||
|
||||
painter.setPen(QColor("#f00"))
|
||||
if self._audio.isValid():
|
||||
radius = self._borderRect.height() / 2
|
||||
painter.drawRoundedRect(self._borderRect, radius, radius)
|
||||
if self._wref:
|
||||
start = bounding.bottomLeft()
|
||||
end = bounding.bottomRight()
|
||||
painter.drawLine(start, end)
|
||||
painter.setPen(self._color)
|
||||
painter.drawText(rect, flags, self._text)
|
||||
painter.restore()
|
||||
size = size.grownBy(self._margin)
|
||||
size = size.grownBy(self._border)
|
||||
size = size.grownBy(self._padding)
|
||||
return size.height()
|
||||
|
||||
#
|
||||
# Setters
|
||||
#
|
||||
def setText(self, text: str) -> None:
|
||||
self._text = text
|
||||
return
|
||||
|
||||
def setTarget(self, target: str) -> None:
|
||||
self._target = target
|
||||
return
|
||||
|
||||
def setFont(self, font: QFont) -> None:
|
||||
self._font = font
|
||||
return
|
||||
|
||||
def setAudio(self, audio: str | QUrl) -> None:
|
||||
if type(audio) is str:
|
||||
self._audio = QUrl(audio)
|
||||
else:
|
||||
self._audio = cast(QUrl, audio)
|
||||
return
|
||||
|
||||
def setAlign(self, align: QTextOption) -> None:
|
||||
self._align = align
|
||||
return
|
||||
|
||||
def setRect(self, rect: QRect) -> None:
|
||||
self._rect = rect
|
||||
return
|
||||
|
||||
def setPadding(self, *args: int, **kwargs: int) -> None:
|
||||
top = kwargs.get("top", -1)
|
||||
right = kwargs.get("right", -1)
|
||||
bottom = kwargs.get("bottom", -1)
|
||||
left = kwargs.get("left", -1)
|
||||
if top > -1 or right > -1 or bottom > -1 or left > -1:
|
||||
if top >= 0:
|
||||
self._padding.setTop(top)
|
||||
if right >= 0:
|
||||
self._padding.setRight(right)
|
||||
if bottom >= 0:
|
||||
self._padding.setBottom(bottom)
|
||||
if left >= 0:
|
||||
self._padding.setLeft(left)
|
||||
return
|
||||
if len(args) == 4:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[3]]
|
||||
elif len(args) == 3:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[1]]
|
||||
elif len(args) == 2:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[0], args[1]]
|
||||
elif len(args) == 1:
|
||||
(top, right, bottom, left) = [args[0], args[0], args[0], args[0]]
|
||||
else:
|
||||
raise Exception("argument error")
|
||||
self._padding = QMargins(left, top, right, bottom)
|
||||
return
|
||||
|
||||
def setBorder(self, *args: int, **kwargs: int) -> None:
|
||||
top = kwargs.get("top", -1)
|
||||
right = kwargs.get("right", -1)
|
||||
bottom = kwargs.get("bottom", -1)
|
||||
left = kwargs.get("left", -1)
|
||||
if top > -1 or right > -1 or bottom > -1 or left > -1:
|
||||
if top >= 0:
|
||||
self._border.setTop(top)
|
||||
if right >= 0:
|
||||
self._border.setRight(right)
|
||||
if bottom >= 0:
|
||||
self._border.setBottom(bottom)
|
||||
if left >= 0:
|
||||
self._border.setLeft(left)
|
||||
return
|
||||
if len(args) == 4:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[3]]
|
||||
elif len(args) == 3:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[1]]
|
||||
elif len(args) == 2:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[0], args[1]]
|
||||
elif len(args) == 1:
|
||||
(top, right, bottom, left) = [args[0], args[0], args[0], args[0]]
|
||||
else:
|
||||
raise Exception("argument error")
|
||||
self._border = QMargins(left, top, right, bottom)
|
||||
return
|
||||
|
||||
def setMargin(self, *args: int, **kwargs: int) -> None:
|
||||
top = kwargs.get("top", -1)
|
||||
right = kwargs.get("right", -1)
|
||||
bottom = kwargs.get("bottom", -1)
|
||||
left = kwargs.get("left", -1)
|
||||
|
||||
if top > -1 or right > -1 or bottom > -1 or left > -1:
|
||||
if top >= 0:
|
||||
self._margin.setTop(top)
|
||||
if right >= 0:
|
||||
self._margin.setRight(right)
|
||||
if bottom >= 0:
|
||||
self._margin.setBottom(bottom)
|
||||
if left >= 0:
|
||||
self._margin.setLeft(left)
|
||||
return
|
||||
if len(args) == 4:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[3]]
|
||||
elif len(args) == 3:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[2], args[1]]
|
||||
elif len(args) == 2:
|
||||
(top, right, bottom, left) = [args[0], args[1], args[0], args[1]]
|
||||
elif len(args) == 1:
|
||||
(top, right, bottom, left) = [args[0], args[0], args[0], args[0]]
|
||||
else:
|
||||
raise Exception("argument error")
|
||||
self._margin = QMargins(left, top, right, bottom)
|
||||
return
|
||||
|
||||
def setWRef(self, ref: str) -> None:
|
||||
self._wref = ref
|
||||
return
|
||||
|
||||
def setPosition(self, pnt: QPoint) -> None:
|
||||
self._position = pnt
|
||||
return
|
||||
|
||||
def setBorderRect(self, rect: QRect) -> None:
|
||||
self._borderRect = rect
|
||||
return
|
||||
|
||||
def setClickRect(self, rect: QRect) -> None:
|
||||
self._clickRect = rect
|
||||
return
|
||||
|
||||
def setColor(self, color: QColor) -> None:
|
||||
self._color = color
|
||||
return
|
||||
|
||||
def setBackground(self, color: QColor) -> None:
|
||||
self._background = color
|
||||
return
|
||||
def setLeft(self, left: int) -> None:
|
||||
self._left = left
|
||||
return
|
||||
|
||||
#
|
||||
# Getters
|
||||
#
|
||||
def wRef(self) -> str:
|
||||
return self._wref
|
||||
|
||||
def text(self) -> str:
|
||||
return self._text
|
||||
|
||||
def font(self) -> QFont:
|
||||
return self._font
|
||||
|
||||
def audio(self) -> QUrl:
|
||||
return self._audio
|
||||
|
||||
def align(self) -> QTextOption:
|
||||
return self._align
|
||||
|
||||
def rect(self) -> QRect:
|
||||
return self._rect
|
||||
|
||||
def padding(self) -> QMargins:
|
||||
return self._padding
|
||||
|
||||
def border(self) -> QMargins:
|
||||
return self._border
|
||||
|
||||
def margin(self) -> QMargins:
|
||||
return self._margin
|
||||
|
||||
def position(self) -> QPoint:
|
||||
return self._position
|
||||
|
||||
def borderRect(self) -> QRect:
|
||||
return self._borderRect
|
||||
|
||||
def clickRect(self) -> QRect:
|
||||
return self._clickRect
|
||||
|
||||
def color(self) -> QColor:
|
||||
return self._color
|
||||
|
||||
def asis(self) -> bool:
|
||||
return self._asis
|
||||
|
||||
def left(self) -> int:
|
||||
return self._left
|
||||
|
||||
class Line:
|
||||
def __init__(self) -> None:
|
||||
self._maxHeight = -1
|
||||
self._baseLine = -1
|
||||
self._leading = -1
|
||||
self._fragments: list[Fragment] = []
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
"|".join([x.text() for x in self._fragments])
|
||||
+ f"|{self._maxHeight}"
|
||||
)
|
||||
|
||||
def repaintEvent(self, painter: QPainter) -> int:
|
||||
#
|
||||
# we do not have an event field because we are not a true widget
|
||||
#
|
||||
lineSpacing = 0
|
||||
for frag in self._fragments:
|
||||
ls = frag.repaintEvent(painter)
|
||||
if ls > lineSpacing:
|
||||
lineSpacing = ls
|
||||
return lineSpacing
|
||||
|
||||
def parseText(self, frag: Fragment) -> list[Fragment]:
|
||||
org = frag.text()
|
||||
if frag.asis():
|
||||
return [frag]
|
||||
#
|
||||
# Needed Fonts
|
||||
# We can't use _resources because that might not be the font
|
||||
# for this piece of text
|
||||
#
|
||||
bold = QFont(frag.font())
|
||||
bold.setWeight(QFont.Weight.Bold)
|
||||
italic = QFont(frag.font())
|
||||
italic.setItalic(True)
|
||||
smallCaps = QFont(frag.font())
|
||||
smallCaps.setCapitalization(QFont.Capitalization.SmallCaps)
|
||||
script = QFont(frag.font())
|
||||
script.setPixelSize(int(script.pixelSize() / 4))
|
||||
caps = QFont(frag.font())
|
||||
caps.setCapitalization(QFont.Capitalization.AllUppercase)
|
||||
|
||||
results: list[Fragment] = []
|
||||
while True:
|
||||
text = frag.text()
|
||||
start = text.find("{")
|
||||
if start < 0:
|
||||
results.append(frag)
|
||||
return results
|
||||
if start > 0:
|
||||
newFrag = copy.copy(frag)
|
||||
newFrag.setText(text[:start])
|
||||
results.append(newFrag)
|
||||
frag.setText(text[start:])
|
||||
continue
|
||||
#
|
||||
# Start == 0
|
||||
#
|
||||
|
||||
#
|
||||
# If the token is an end-token, return now.
|
||||
#
|
||||
if text.startswith("{/"):
|
||||
results.append(frag)
|
||||
return results
|
||||
|
||||
#
|
||||
# extract this token
|
||||
#
|
||||
end = text.find("}")
|
||||
token = text[1:end]
|
||||
frag.setText(text[end + 1 :])
|
||||
newFrag = copy.copy(frag)
|
||||
oldFont = QFont(frag.font())
|
||||
if token == "bc":
|
||||
results.append(Fragment(": ", bold, color=QColor("#fff")))
|
||||
continue
|
||||
if token in [
|
||||
"b",
|
||||
"inf",
|
||||
"it",
|
||||
"sc",
|
||||
"sup",
|
||||
"phrase",
|
||||
"parahw",
|
||||
"gloss",
|
||||
"qword",
|
||||
"wi",
|
||||
"dx",
|
||||
"dx_def",
|
||||
"dx_ety",
|
||||
"ma",
|
||||
]:
|
||||
if token == "b":
|
||||
frag.setFont(bold)
|
||||
elif token in ["it", "qword", "wi"]:
|
||||
frag.setFont(italic)
|
||||
elif token == "sc":
|
||||
frag.setFont(smallCaps)
|
||||
elif token in ["inf", "sup"]:
|
||||
frag.setFont(script)
|
||||
elif token == "phrase":
|
||||
font = QFont(bold)
|
||||
font.setItalic(True)
|
||||
frag.setFont(font)
|
||||
elif token == "parahw":
|
||||
font = QFont(smallCaps)
|
||||
font.setWeight(QFont.Weight.Bold)
|
||||
frag.setFont(font)
|
||||
elif token == "gloss":
|
||||
frag.setText("[" + frag.text())
|
||||
elif token in ["dx", "dx_ety"]:
|
||||
frag.setText("\u2014" + frag.text())
|
||||
elif token == "ma":
|
||||
frag.setText("\u2014 more at " + frag.text())
|
||||
elif token == "dx_def":
|
||||
frag.setText("(" + frag.text())
|
||||
else:
|
||||
raise Exception(f"Unknown block marker: {token}")
|
||||
results += self.parseText(frag)
|
||||
frag = results.pop()
|
||||
frag.setFont(oldFont)
|
||||
text = frag.text()
|
||||
if not text.startswith("{/" + token + "}"):
|
||||
raise Exception(
|
||||
f"No matching close for {token} in {org}"
|
||||
)
|
||||
if token == "gloss":
|
||||
results[-1].setText(results[-1].text() + "]")
|
||||
elif token == "dx_def":
|
||||
results[-1].setText(results[-1].text() + ")")
|
||||
end = text.find("}")
|
||||
text = text[end + 1 :]
|
||||
frag.setText(text)
|
||||
continue
|
||||
#
|
||||
# These are codes that include all information within the token
|
||||
#
|
||||
fields = token.split("|")
|
||||
token = fields[0]
|
||||
if token in [
|
||||
"a_link",
|
||||
"d_link",
|
||||
"dxt",
|
||||
"et_link",
|
||||
"i_link",
|
||||
"mat",
|
||||
"sx",
|
||||
]:
|
||||
wref = ""
|
||||
htext = fields[1]
|
||||
oldFont = QFont(frag.font())
|
||||
target = "word"
|
||||
if token == "a_link":
|
||||
wref = fields[1]
|
||||
elif token in ["d_link", "et_link", "mat", "sx", "i_link"]:
|
||||
if fields[2] == "":
|
||||
wref = fields[1]
|
||||
else:
|
||||
wref = fields[2]
|
||||
if token == "i_link":
|
||||
frag.setFont(italic)
|
||||
elif token == "sx":
|
||||
frag.setFont(caps)
|
||||
elif token == "dxt":
|
||||
if fields[3] == "illustration":
|
||||
wref = fields[2]
|
||||
target = "article"
|
||||
elif fields[3] == "table":
|
||||
wref = fields[2]
|
||||
target = "table"
|
||||
elif fields[3] != "":
|
||||
wref = fields[3]
|
||||
target = "sense"
|
||||
else:
|
||||
wref = fields[1]
|
||||
target = "word"
|
||||
elif token == "a_link":
|
||||
target = "word"
|
||||
wref = fields[1]
|
||||
else:
|
||||
raise Exception(f"Unknown code: {token} in {org}")
|
||||
newFrag = copy.copy(frag)
|
||||
newFrag.setText(htext)
|
||||
newFrag.setWRef(wref)
|
||||
newFrag.setTarget(target)
|
||||
results.append(newFrag)
|
||||
frag.setFont(oldFont)
|
||||
text = frag.text()
|
||||
continue
|
||||
raise Exception(
|
||||
f"Unable to locate a known token {token} in {org}"
|
||||
)
|
||||
|
||||
def addFragment(
|
||||
self,
|
||||
frag: Fragment,
|
||||
) -> None:
|
||||
SPEAKER = "\U0001F508"
|
||||
|
||||
if frag.audio().isValid():
|
||||
frag.setText(frag.text() + " " + SPEAKER)
|
||||
|
||||
text = frag.text()
|
||||
text = re.sub(r"\*", "\u2022", text)
|
||||
text = re.sub(r"\{ldquo\}", "\u201c", text)
|
||||
text = re.sub(r"\{rdquo\}", "\u201d", text)
|
||||
frag.setText(text)
|
||||
if frag.audio().isValid():
|
||||
frag.setPadding(3, 0, 0, 5)
|
||||
frag.setBorder(1)
|
||||
frag.setMargin(0, 0, 0, 0)
|
||||
items = self.parseText(frag)
|
||||
self._fragments += items
|
||||
return
|
||||
|
||||
def finalizeLine(self, width: int, base: int) -> None:
|
||||
"""Create all of the positions for all the fragments."""
|
||||
#
|
||||
# Find the maximum hight and max baseline
|
||||
#
|
||||
maxHeight = -1
|
||||
baseLine = -1
|
||||
leading = -1
|
||||
for frag in self._fragments:
|
||||
fm = QFontMetrics(frag.font())
|
||||
height = frag.height(width)
|
||||
bl = fm.height() - fm.descent()
|
||||
if fm.leading() > leading:
|
||||
leading = fm.leading()
|
||||
if height > maxHeight:
|
||||
maxHeight = height
|
||||
if bl > baseLine:
|
||||
baseLine = bl
|
||||
self._baseLine = baseLine
|
||||
self._maxHeight = maxHeight
|
||||
self._leading = leading
|
||||
x = 0
|
||||
for frag in self._fragments:
|
||||
if x < frag.left():
|
||||
x = frag.left()
|
||||
#
|
||||
# We need to calculate the location to draw the
|
||||
# text. We also need to calculate the bounding Rectangle
|
||||
# for this fragment
|
||||
#
|
||||
size = frag.size(width)
|
||||
fm = QFontMetrics(frag.font())
|
||||
offset = (
|
||||
frag.margin().left()
|
||||
+ frag.border().left()
|
||||
+ frag.padding().left()
|
||||
)
|
||||
frag.setPosition(QPoint(x + offset, self._baseLine))
|
||||
if not frag.border().isNull() or not frag.wRef():
|
||||
#
|
||||
# self._baseLine is where the text will be drawn
|
||||
# fm.descent is the distance from the baseline of the
|
||||
# text to the bottom of the rect
|
||||
# The top of the bounding rect is at self._baseLine
|
||||
# + fm.descent - rect.height
|
||||
# The border is drawn at top-padding-border-margin+marin
|
||||
#
|
||||
top = self._baseLine + fm.descent() - fm.height()
|
||||
y = top - frag.padding().top() - frag.border().top()
|
||||
pos = QPoint(x, y)
|
||||
rect = QRect(pos, size.shrunkBy(frag.margin()))
|
||||
frag.setBorderRect(rect)
|
||||
pos.setY(pos.y() + base)
|
||||
frag.setClickRect(QRect(pos, size.shrunkBy(frag.margin())))
|
||||
x += size.width()
|
||||
return
|
||||
|
||||
def getLine(self) -> list[Fragment]:
|
||||
return self._fragments
|
||||
|
||||
def getLineSpacing(self) -> int:
|
||||
return self._leading + self._maxHeight
|
||||
|
||||
class Definition(QWidget):
|
||||
pronounce = pyqtSignal(str)
|
||||
|
||||
def __init__(
|
||||
self, word: Optional[Any] = None, *args: Any, **kwargs: Any
|
||||
) -> None:
|
||||
super(Definition, self).__init__(*args, **kwargs)
|
||||
self._word = word
|
||||
if word is not None:
|
||||
self.setWord(word)
|
||||
return
|
||||
|
||||
def setWord(self, word: Any) -> None:
|
||||
self._word = word
|
||||
lines = word.get_def()
|
||||
assert lines is not None
|
||||
self._lines = lines
|
||||
self._buttons: list[Fragment] = []
|
||||
base = 0
|
||||
|
||||
for line in self._lines:
|
||||
line.finalizeLine(self.width(), base)
|
||||
for frag in line.getLine():
|
||||
if frag.audio().isValid():
|
||||
self._buttons.append(frag)
|
||||
if frag.wRef():
|
||||
print(f"Adding {frag} as an anchor")
|
||||
self._buttons.append(frag)
|
||||
base += line.getLineSpacing()
|
||||
self.setFixedHeight(base)
|
||||
return
|
||||
|
||||
def resizeEvent(self, event: Optional[QResizeEvent] = None) -> None:
|
||||
base = 0
|
||||
for line in self._lines:
|
||||
line.finalizeLine(self.width(), base)
|
||||
base += line.getLineSpacing()
|
||||
self.setFixedHeight(base)
|
||||
super(Definition, self).resizeEvent(event)
|
||||
return
|
||||
|
||||
_downFrag: Optional[Fragment | None] = None
|
||||
|
||||
def mousePressEvent(self, event: Optional[QMouseEvent]) -> None:
|
||||
if not event:
|
||||
return super().mousePressEvent(event)
|
||||
print(f"mousePressEvent: {event.pos()}")
|
||||
for frag in self._buttons:
|
||||
rect = frag.clickRect()
|
||||
if rect.contains(event.pos()):
|
||||
self._downFrag = frag
|
||||
return
|
||||
return super().mousePressEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event: Optional[QMouseEvent]) -> None:
|
||||
if not event:
|
||||
return super().mouseReleaseEvent(event)
|
||||
if self._downFrag is not None and self._downFrag.clickRect().contains(
|
||||
event.pos()
|
||||
):
|
||||
audio = self._downFrag.audio().url()
|
||||
print(audio)
|
||||
self.pronounce.emit(audio)
|
||||
print("emit done")
|
||||
self._downFrag = None
|
||||
return
|
||||
self._downFrag = None
|
||||
return super().mouseReleaseEvent(event)
|
||||
|
||||
def paintEvent(self, _: Optional[QPaintEvent]) -> None: # noqa
|
||||
painter = QPainter(self)
|
||||
painter.save()
|
||||
painter.setBrush(QBrush())
|
||||
painter.setPen(QColor("white"))
|
||||
|
||||
#
|
||||
# Each line needs a base calculated. To do that, we need to find the
|
||||
# bounding rectangle of the text. Once we have the bounding rectangle,
|
||||
# we can use the descendant to calculate the baseline within that
|
||||
# bounding box.
|
||||
#
|
||||
# All text on this line needs to be on the same baseline
|
||||
#
|
||||
assert self._lines is not None
|
||||
base = 0
|
||||
for line in self._lines:
|
||||
transform = QTransform()
|
||||
transform.translate(0, base)
|
||||
painter.setTransform(transform)
|
||||
base += line.repaintEvent(painter)
|
||||
painter.restore()
|
||||
return
|
||||
@@ -1,7 +1,7 @@
|
||||
from trycast import trycast
|
||||
import json
|
||||
import re
|
||||
from typing import Any, NamedTuple, TypedDict, cast
|
||||
from typing import Any, Literal, NamedTuple, NotRequired, TypedDict, cast
|
||||
|
||||
from PyQt6.QtCore import QEventLoop, QUrl, Qt
|
||||
from PyQt6.QtGui import QColor, QFont
|
||||
@@ -129,14 +129,26 @@ class Definition(TypedDict):
|
||||
sseq: list[SenseSequence]
|
||||
vd: str
|
||||
|
||||
class Entry(TypedDict):
|
||||
class EntryX(TypedDict):
|
||||
meta: Meta
|
||||
hom: str
|
||||
hom: NotRequired[str]
|
||||
hwi: HeadWordInfo
|
||||
ahws: list[HeadWord]
|
||||
vrs: list[Variant]
|
||||
ahws: NotRequired[list[HeadWord]]
|
||||
vrs: NotRequired[list[Variant]]
|
||||
fl: str
|
||||
def_: list[Definition]
|
||||
Entry = TypedDict(
|
||||
'Entry',
|
||||
{
|
||||
'meta': Meta,
|
||||
'hom': NotRequired[str],
|
||||
'hwi': HeadWordInfo,
|
||||
'ahws': NotRequired[list[HeadWord]],
|
||||
'vrs': NotRequired[list[Variant]],
|
||||
'fl': NotRequired[str],
|
||||
'def': list[Definition],
|
||||
}
|
||||
)
|
||||
|
||||
def fetch(word:str) -> dict[str, Any]:
|
||||
request = QNetworkRequest()
|
||||
@@ -213,30 +225,55 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
|
||||
|
||||
def getDef(definition: list[Entry]) -> list[Line]:
|
||||
lines = []
|
||||
#
|
||||
# Pull the fonts for ease of use
|
||||
#
|
||||
headerFont = trycast(QFont, Word._resources['fonts']['header'])
|
||||
assert headerFont is not None
|
||||
textFont = trycast(QFont, Word._resources['fonts']['text'])
|
||||
assert textFont is not None
|
||||
labelFont = trycast(QFont, Word._resources['fonts']['label'])
|
||||
assert labelFont is not None
|
||||
|
||||
#
|
||||
# Pull the colors for ease of use
|
||||
#
|
||||
baseColor = trycast(QColor, Word._resources['colors']['base'])
|
||||
assert baseColor is not None
|
||||
linkColor = trycast(QColor, Word._resources['colors']['link'])
|
||||
assert linkColor is not None
|
||||
subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
|
||||
assert subduedColor is not None
|
||||
entries = len(definition)
|
||||
for count, entry in enumerate(definition):
|
||||
|
||||
#
|
||||
# Create the First line from the hwi and fl
|
||||
# No need to figure it out each time it is used
|
||||
#
|
||||
entries = 0
|
||||
id = definition[0]['meta']['id']
|
||||
id = ':'.split(id)[0].lower()
|
||||
for entry in definition:
|
||||
if entry['meta']['id'].lower() == id:
|
||||
entries += 1
|
||||
for count, entry in enumerate(definition):
|
||||
if entry['meta']['id'].lower() != id:
|
||||
continue
|
||||
#
|
||||
# Create the First line from the hwi, [ahws] and fl
|
||||
#
|
||||
line = Line()
|
||||
hwi = trycast(HeadWordInfo, entry['hwi'])
|
||||
assert hwi is not None
|
||||
hw = re.sub(r'\*', '', hwi['hw'])
|
||||
line.addFragment(Fragment(hw + ' ', headerFont, color=baseColor))
|
||||
frag = Fragment(f"{count} of {entries} ", textFont, color=linkColor)
|
||||
line.addFragment(Fragment(hw, headerFont, color=baseColor))
|
||||
if 'ahws' in entry:
|
||||
ahws = trycast(list[HeadWord], entry['ahws'])
|
||||
assert ahws is not None
|
||||
for ahw in ahws:
|
||||
hw = re.sub(r'\*', '', ahw['hw'])
|
||||
line.addFragment(Fragment(', ' + hw, headerFont, color=baseColor))
|
||||
if 'hom' in entry:
|
||||
|
||||
if 'fl' in entry:
|
||||
frag = Fragment(f"{count} of {entries} ", textFont, color=
|
||||
frag.setBackground(QColor(Qt.GlobalColor.gray))
|
||||
line.addFragment(frag)
|
||||
line.addFragment(Fragment(entry['fl'], labelFont, color=baseColor))
|
||||
@@ -244,10 +281,14 @@ def getDef(definition: list[Entry]) -> list[Line]:
|
||||
|
||||
#
|
||||
# Next is the pronunciation.
|
||||
# While 'prs' is optional, the headword is not. This gets us what we want.
|
||||
#
|
||||
line = Line()
|
||||
hw = re.sub(r'\*', '\u00b7', hwi['hw'])
|
||||
line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
|
||||
for frag in do_prs(hwi['prs']):
|
||||
line.addFragment(frag)
|
||||
|
||||
#
|
||||
# Try for
|
||||
return [Line()]
|
||||
|
||||
Reference in New Issue
Block a user