checkpoint
This commit is contained in:
75
lib/utils.py
75
lib/utils.py
@@ -1,7 +1,9 @@
|
|||||||
"""Utility Functions."""
|
"""Utility Functions."""
|
||||||
from typing import NoReturn
|
from typing import NoReturn, Self
|
||||||
|
|
||||||
from PyQt6.QtCore import QCoreApplication
|
from PyQt6.QtCore import QCoreApplication, QDir, QStandardPaths, Qt
|
||||||
|
from PyQt6.QtGui import QColor, QFont, QFontDatabase
|
||||||
|
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkDiskCache
|
||||||
from PyQt6.QtSql import QSqlQuery
|
from PyQt6.QtSql import QSqlQuery
|
||||||
|
|
||||||
translate = QCoreApplication.translate
|
translate = QCoreApplication.translate
|
||||||
@@ -19,3 +21,72 @@ def query_error(query: QSqlQuery) -> NoReturn:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
raise Exception(translate("MainWindow", "SQL Error"))
|
raise Exception(translate("MainWindow", "SQL Error"))
|
||||||
|
|
||||||
|
class Resources:
|
||||||
|
_instance = None
|
||||||
|
nam = QNetworkAccessManager()
|
||||||
|
headerFont: QFont
|
||||||
|
labelFont: QFont
|
||||||
|
boldFont: QFont
|
||||||
|
textFont: QFont
|
||||||
|
italicFont: QFont
|
||||||
|
capsFont: QFont
|
||||||
|
smallCapsFont: QFont
|
||||||
|
phonicFont: QFont
|
||||||
|
|
||||||
|
baseColor: QColor
|
||||||
|
linkColor: QColor
|
||||||
|
subduedColor: QColor
|
||||||
|
|
||||||
|
def __new__(cls: type[Self]) -> Self:
|
||||||
|
if cls._instance:
|
||||||
|
return cls._instance
|
||||||
|
cls._instance = super(Resources, cls).__new__(cls)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super(Resources, self).__init__()
|
||||||
|
#
|
||||||
|
# Fonts
|
||||||
|
#
|
||||||
|
self.headerFont = QFontDatabase.font("OpenDyslexic", None, 10)
|
||||||
|
self.headerFont.setPixelSize(48)
|
||||||
|
self.labelFont = QFont(self.headerFont)
|
||||||
|
self.labelFont.setPixelSize(30)
|
||||||
|
self.boldFont = QFont(self.headerFont)
|
||||||
|
self.boldFont.setPixelSize(20)
|
||||||
|
self.textFont = QFont(self.boldFont)
|
||||||
|
self.italicFont = QFont(self.boldFont)
|
||||||
|
self.capsFont = QFont(self.boldFont)
|
||||||
|
self.smallCapsFont = QFont(self.boldFont)
|
||||||
|
|
||||||
|
self.headerFont.setWeight(QFont.Weight.Bold)
|
||||||
|
self.boldFont.setBold(True)
|
||||||
|
self.italicFont.setItalic(True)
|
||||||
|
self.capsFont.setCapitalization(QFont.Capitalization.AllUppercase)
|
||||||
|
self.smallCapsFont.setCapitalization(QFont.Capitalization.SmallCaps)
|
||||||
|
|
||||||
|
self.phonicFont = QFontDatabase.font("Gentium", None, 10)
|
||||||
|
self.phonicFont.setPixelSize(20)
|
||||||
|
|
||||||
|
#
|
||||||
|
# colors
|
||||||
|
#
|
||||||
|
self.baseColor = QColor(Qt.GlobalColor.white)
|
||||||
|
self.linkColor = QColor("#4a7d95")
|
||||||
|
self.subduedColor = QColor(Qt.GlobalColor.gray)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setup the Network Manager
|
||||||
|
#
|
||||||
|
cacheDir = QDir(
|
||||||
|
QStandardPaths.writableLocation(
|
||||||
|
QStandardPaths.StandardLocation.GenericCacheLocation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cacheDir.mkdir("Troglodite")
|
||||||
|
cacheDir = QDir(cacheDir.path() + QDir.separator() + "Troglodite")
|
||||||
|
netCache = QNetworkDiskCache()
|
||||||
|
netCache.setCacheDirectory(cacheDir.path())
|
||||||
|
self.nam.setCache(netCache)
|
||||||
|
return
|
||||||
|
|||||||
142
lib/words.py
142
lib/words.py
@@ -2,22 +2,20 @@ import importlib
|
|||||||
import pkgutil
|
import pkgutil
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, cast
|
from typing import Any, TypedDict, cast
|
||||||
|
|
||||||
from PyQt6.QtCore import (
|
from PyQt6.QtCore import (
|
||||||
|
QUrl,
|
||||||
Qt,
|
Qt,
|
||||||
pyqtSlot,
|
pyqtSlot,
|
||||||
)
|
)
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
QColor,
|
QColor,
|
||||||
QFont,
|
|
||||||
QFontDatabase,
|
|
||||||
)
|
)
|
||||||
from PyQt6.QtNetwork import QNetworkAccessManager
|
|
||||||
from PyQt6.QtSql import QSqlQuery
|
from PyQt6.QtSql import QSqlQuery
|
||||||
from PyQt6.QtWidgets import QScrollArea
|
from PyQt6.QtWidgets import QScrollArea
|
||||||
|
|
||||||
from lib import query_error
|
from lib.utils import query_error, Resources
|
||||||
from lib.sounds import SoundOff
|
from lib.sounds import SoundOff
|
||||||
from lib.definition import Definition, Line, Fragment
|
from lib.definition import Definition, Line, Fragment
|
||||||
|
|
||||||
@@ -32,20 +30,22 @@ discovered_plugins = {
|
|||||||
|
|
||||||
API = "https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
|
API = "https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
|
||||||
|
|
||||||
|
class WordType(TypedDict):
|
||||||
|
word: str
|
||||||
|
source: str
|
||||||
|
definition: str
|
||||||
|
|
||||||
class Word:
|
class Word:
|
||||||
"""All processing of a dictionary word."""
|
"""All processing of a dictionary word."""
|
||||||
|
|
||||||
_words: dict[str, Any] = {}
|
_words: dict[str, WordType] = {}
|
||||||
_resources: Dict[str, Any] = {}
|
|
||||||
_nam = QNetworkAccessManager()
|
|
||||||
def __init__(self, word: str) -> None:
|
def __init__(self, word: str) -> None:
|
||||||
Word.set_resources()
|
|
||||||
#
|
#
|
||||||
# Have we already retrieved this word?
|
# Have we already retrieved this word?
|
||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
self.current = json.loads(Word._words[word])
|
self.current = Word._words[word]
|
||||||
return
|
return
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
@@ -82,50 +82,6 @@ class Word:
|
|||||||
query_error(query)
|
query_error(query)
|
||||||
return
|
return
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_resources(cls) -> None:
|
|
||||||
if len(cls._resources.keys()) > 0:
|
|
||||||
return
|
|
||||||
#
|
|
||||||
# Colors we used
|
|
||||||
#
|
|
||||||
headerFont = QFontDatabase.font("OpenDyslexic", None, 10)
|
|
||||||
headerFont.setPixelSize(48)
|
|
||||||
labelFont = QFont(headerFont)
|
|
||||||
labelFont.setPixelSize(30)
|
|
||||||
boldFont = QFont(headerFont)
|
|
||||||
boldFont.setPixelSize(20)
|
|
||||||
textFont = QFont(boldFont)
|
|
||||||
italicFont = QFont(boldFont)
|
|
||||||
capsFont = QFont(boldFont)
|
|
||||||
smallCapsFont = QFont(boldFont)
|
|
||||||
|
|
||||||
headerFont.setWeight(QFont.Weight.Bold)
|
|
||||||
boldFont.setBold(True)
|
|
||||||
italicFont.setItalic(True)
|
|
||||||
capsFont.setCapitalization(QFont.Capitalization.AllUppercase)
|
|
||||||
smallCapsFont.setCapitalization(QFont.Capitalization.SmallCaps)
|
|
||||||
|
|
||||||
phonicFont = QFontDatabase.font("Gentium", None, 10)
|
|
||||||
phonicFont.setPixelSize(20)
|
|
||||||
|
|
||||||
cls._resources = {
|
|
||||||
"colors": {
|
|
||||||
"base": QColor(Qt.GlobalColor.white),
|
|
||||||
"link": QColor("#4a7d95"),
|
|
||||||
"subdued": QColor(Qt.GlobalColor.gray),
|
|
||||||
},
|
|
||||||
"fonts": {
|
|
||||||
"header": headerFont,
|
|
||||||
"label": labelFont,
|
|
||||||
"phonic": phonicFont,
|
|
||||||
"bold": boldFont,
|
|
||||||
"italic": italicFont,
|
|
||||||
"text": textFont,
|
|
||||||
"caps": capsFont,
|
|
||||||
"smallCaps": smallCapsFont,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def playSound(self) -> None:
|
def playSound(self) -> None:
|
||||||
url = discovered_plugins[self.current['source']].getFirstSound(self.current['definition'])
|
url = discovered_plugins[self.current['source']].getFirstSound(self.current['definition'])
|
||||||
@@ -145,23 +101,15 @@ class Word:
|
|||||||
raise Exception(f"Unknown source: {src}")
|
raise Exception(f"Unknown source: {src}")
|
||||||
|
|
||||||
def get_def(self) -> list[Line]:
|
def get_def(self) -> list[Line]:
|
||||||
if len(self._lines) > 0:
|
|
||||||
return self._lines
|
|
||||||
src = self.current['source']
|
src = self.current['source']
|
||||||
try:
|
try:
|
||||||
return discovered_plugins[src].getDef(self.current)
|
lines = discovered_plugins[src].getDef(self.current["definition"])
|
||||||
|
return lines
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise Exception(f"Unknown source: {self.current['source']}")
|
raise Exception(f"Unknown source: {self.current['source']}")
|
||||||
|
|
||||||
def mw_def(self) -> list[Line]:
|
|
||||||
lines: list[Line] = []
|
|
||||||
# print(json.dumps(self.current,indent=2))
|
|
||||||
for entry in self.current["definition"]:
|
|
||||||
lines += self.mw_def_entry(entry)
|
|
||||||
self._lines = lines
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def mw_seq(self, seq: list[Any]) -> list[Line]:
|
def mw_seq(self, seq: list[Any]) -> list[Line]:
|
||||||
|
r=Resources()
|
||||||
lines: list[Line] = []
|
lines: list[Line] = []
|
||||||
outer = " "
|
outer = " "
|
||||||
inner = " "
|
inner = " "
|
||||||
@@ -189,15 +137,15 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
f"{outer} {inner} ",
|
f"{outer} {inner} ",
|
||||||
self._resources["fonts"]["bold"],
|
r.boldFont,
|
||||||
color=self._resources["colors"]["base"],
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
outer = " "
|
outer = " "
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
text,
|
text,
|
||||||
self._resources["fonts"]["italic"],
|
r.italicFont,
|
||||||
color=self._resources["colors"]["base"],
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
frag.setLeft(30)
|
frag.setLeft(30)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
@@ -209,16 +157,16 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
f"{outer} {inner} ",
|
f"{outer} {inner} ",
|
||||||
self._resources["fonts"]["bold"],
|
r.boldFont,
|
||||||
color=self._resources["colors"]["base"],
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
outer = " "
|
outer = " "
|
||||||
frag.setLeft(10)
|
frag.setLeft(10)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
dt[1],
|
dt[1],
|
||||||
self._resources["fonts"]["text"],
|
r.textFont,
|
||||||
color=self._resources["colors"]["base"],
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
frag.setLeft(30)
|
frag.setLeft(30)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
@@ -228,14 +176,14 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
f" ",
|
f" ",
|
||||||
self._resources["fonts"]["bold"],
|
r.boldFont
|
||||||
)
|
)
|
||||||
frag.setLeft(45)
|
frag.setLeft(45)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
line.addFragment(
|
line.addFragment(
|
||||||
Fragment(
|
Fragment(
|
||||||
vis["t"],
|
vis["t"],
|
||||||
self._resources["fonts"]["text"],
|
r.textFont,
|
||||||
color=QColor("#aaa"),
|
color=QColor("#aaa"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -250,8 +198,8 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
"\u27F6 " + seg[1],
|
"\u27F6 " + seg[1],
|
||||||
self._resources["fonts"]["text"],
|
r.textFont,
|
||||||
color=self._resources["colors"]["base"],
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
frag.setLeft(30)
|
frag.setLeft(30)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
@@ -265,19 +213,17 @@ class Word:
|
|||||||
return lines
|
return lines
|
||||||
|
|
||||||
def mw_def_entry(self, entry: dict[str, Any]) -> list[Line]:
|
def mw_def_entry(self, entry: dict[str, Any]) -> list[Line]:
|
||||||
|
r = Resources()
|
||||||
#
|
#
|
||||||
# Easy reference to colors
|
# Easy reference to colors
|
||||||
#
|
#
|
||||||
base = self._resources["colors"]["base"]
|
|
||||||
blue = self._resources["colors"]["blue"]
|
|
||||||
|
|
||||||
lines: list[Line] = []
|
lines: list[Line] = []
|
||||||
line = Line()
|
line = Line()
|
||||||
hw = re.sub(r"\*", "", entry["hwi"]["hw"])
|
hw = re.sub(r"\*", "", entry["hwi"]["hw"])
|
||||||
frag = Fragment(hw, self._resources["fonts"]["header"], color=base)
|
frag = Fragment(hw, r.headerFont, color=r.baseColor)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
" " + entry["fl"], self._resources["fonts"]["label"], color=blue
|
" " + entry["fl"], r.labelFont, color=r.linkColor
|
||||||
)
|
)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
@@ -288,8 +234,8 @@ class Word:
|
|||||||
for vrs in entry["vrs"]:
|
for vrs in entry["vrs"]:
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
space + vrs["va"],
|
space + vrs["va"],
|
||||||
self._resources["fonts"]["label"],
|
r.labelFont,
|
||||||
color=base,
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
space = " "
|
space = " "
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
@@ -298,16 +244,16 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
entry["hwi"]["hw"] + " ",
|
entry["hwi"]["hw"] + " ",
|
||||||
self._resources["fonts"]["phonic"],
|
r.phonicFont,
|
||||||
color=base,
|
color=r.baseColor,
|
||||||
)
|
)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
for prs in entry["hwi"]["prs"]:
|
for prs in entry["hwi"]["prs"]:
|
||||||
audio = self.mw_sound_url(prs)
|
audio = None
|
||||||
if audio is None:
|
if audio is None:
|
||||||
audio = ""
|
audio = ""
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
prs["mw"], self._resources["fonts"]["phonic"], color=blue
|
prs["mw"], r.phonicFont, color=r.linkColor
|
||||||
)
|
)
|
||||||
frag.setAudio(audio)
|
frag.setAudio(audio)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
@@ -318,7 +264,7 @@ class Word:
|
|||||||
for ins in entry["ins"]:
|
for ins in entry["ins"]:
|
||||||
try:
|
try:
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
ins["il"], self._resources["fonts"]["text"], color=base
|
ins["il"], r.textFont, color=r.baseColor
|
||||||
)
|
)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
space = " "
|
space = " "
|
||||||
@@ -326,8 +272,8 @@ class Word:
|
|||||||
pass
|
pass
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
space + ins["if"],
|
space + ins["if"],
|
||||||
self._resources["fonts"]["bold"],
|
r.boldFont,
|
||||||
color=base,
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
space = "; "
|
space = "; "
|
||||||
@@ -336,8 +282,8 @@ class Word:
|
|||||||
line = Line()
|
line = Line()
|
||||||
frag = Fragment(
|
frag = Fragment(
|
||||||
"; ".join(entry["lbs"]),
|
"; ".join(entry["lbs"]),
|
||||||
self._resources["fonts"]["bold"],
|
r.boldFont,
|
||||||
color=base,
|
color=r.baseColor
|
||||||
)
|
)
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
@@ -345,13 +291,13 @@ class Word:
|
|||||||
for k, v in value.items():
|
for k, v in value.items():
|
||||||
if k == "sseq": # has multiple 'senses'
|
if k == "sseq": # has multiple 'senses'
|
||||||
for seq in v:
|
for seq in v:
|
||||||
r = self.mw_seq(seq)
|
rr = self.mw_seq(seq)
|
||||||
lines += r
|
lines += rr
|
||||||
elif k == "vd":
|
elif k == "vd":
|
||||||
line = Line()
|
line = Line()
|
||||||
line.addFragment(
|
line.addFragment(
|
||||||
Fragment(
|
Fragment(
|
||||||
v, self._resources["fonts"]["italic"], color=blue
|
v, r.italicFont, color=r.linkColor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
@@ -361,7 +307,7 @@ class Word:
|
|||||||
#
|
#
|
||||||
# Create the header, base word and its label
|
# Create the header, base word and its label
|
||||||
#
|
#
|
||||||
word = self.current["hwi"]["hw"]
|
word = self.current['definition']["hwi"]["hw"]
|
||||||
label = self.current["fl"]
|
label = self.current["fl"]
|
||||||
html = f'<h1 class="def-word">{word} <span class="def-label">{label}</span></h1>\n'
|
html = f'<h1 class="def-word">{word} <span class="def-label">{label}</span></h1>\n'
|
||||||
|
|
||||||
@@ -383,7 +329,7 @@ class Word:
|
|||||||
if "prs" in self.current["hwi"].keys():
|
if "prs" in self.current["hwi"].keys():
|
||||||
tmp = []
|
tmp = []
|
||||||
for prs in self.current["hwi"]["prs"]:
|
for prs in self.current["hwi"]["prs"]:
|
||||||
url = self.mw_sound_url(prs)
|
url = QUrl()
|
||||||
how = prs["mw"]
|
how = prs["mw"]
|
||||||
if url:
|
if url:
|
||||||
tmp.append(f'<a href="{url}">\\{how}\\</a>')
|
tmp.append(f'<a href="{url}">\\{how}\\</a>')
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
from PyQt6.QtGui import QColor
|
||||||
from trycast import trycast
|
from trycast import trycast
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Any, Literal, NamedTuple, NotRequired, TypedDict, cast
|
from typing import Any, NamedTuple, NotRequired, TypedDict
|
||||||
|
|
||||||
from PyQt6.QtCore import QEventLoop, QUrl, Qt
|
from PyQt6.QtCore import QEventLoop, QUrl, Qt
|
||||||
from PyQt6.QtGui import QColor, QFont
|
|
||||||
from PyQt6.QtNetwork import QNetworkRequest
|
from PyQt6.QtNetwork import QNetworkRequest
|
||||||
from lib.words import Word
|
from lib.utils import Resources
|
||||||
from lib.definition import Line, Fragment
|
from lib.definition import Line, Fragment
|
||||||
|
|
||||||
registration = {
|
registration = {
|
||||||
@@ -27,10 +27,6 @@ class VerbalIllustration(TypedDict):
|
|||||||
t: str
|
t: str
|
||||||
aq: str
|
aq: str
|
||||||
|
|
||||||
class VerbalIllustrationTuple(NamedTuple):
|
|
||||||
type_: str # 'vis'
|
|
||||||
data: list[VerbalIllustration]
|
|
||||||
|
|
||||||
class Sound(TypedDict):
|
class Sound(TypedDict):
|
||||||
audio: str
|
audio: str
|
||||||
ref: str
|
ref: str
|
||||||
@@ -38,12 +34,10 @@ class Sound(TypedDict):
|
|||||||
|
|
||||||
class Pronunciation(TypedDict):
|
class Pronunciation(TypedDict):
|
||||||
mw: str
|
mw: str
|
||||||
l: str
|
l: NotRequired[str]
|
||||||
l2: str
|
l2: NotRequired[str]
|
||||||
pun: str
|
pun: NotRequired[str]
|
||||||
sound: Sound
|
sound: NotRequired[Sound]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Meta(TypedDict):
|
class Meta(TypedDict):
|
||||||
id: str
|
id: str
|
||||||
@@ -56,12 +50,12 @@ class Meta(TypedDict):
|
|||||||
|
|
||||||
class HeadWordInfo(TypedDict):
|
class HeadWordInfo(TypedDict):
|
||||||
hw: str
|
hw: str
|
||||||
prs: list[Pronunciation]
|
prs: NotRequired[list[Pronunciation]]
|
||||||
|
|
||||||
class HeadWord(TypedDict):
|
class HeadWord(TypedDict):
|
||||||
hw: str
|
hw: str
|
||||||
prs: list[Pronunciation]
|
prs: NotRequired[list[Pronunciation]]
|
||||||
psl: str
|
psl: NotRequired[str]
|
||||||
|
|
||||||
class Variant(TypedDict):
|
class Variant(TypedDict):
|
||||||
va: str
|
va: str
|
||||||
@@ -109,34 +103,26 @@ class RunInWrap(TypedDict):
|
|||||||
text: str
|
text: str
|
||||||
vrs: list[Variant]
|
vrs: list[Variant]
|
||||||
|
|
||||||
class Sense:
|
class Sense(TypedDict):
|
||||||
dt: list[str] # not full
|
dt: list[list] # not full
|
||||||
et: list[str] # not full
|
et: NotRequired[list[str]]
|
||||||
ins: list[Inflection]
|
ins: NotRequired[list[Inflection]]
|
||||||
lbs: list[str]
|
lbs: NotRequired[list[str]]
|
||||||
prs: list[Pronunciation]
|
prs: NotRequired[list[Pronunciation]]
|
||||||
sdsense: DividedSense
|
sdsense: NotRequired[DividedSense]
|
||||||
sgram: str
|
sgram: NotRequired[str]
|
||||||
sls: list[str]
|
sls: NotRequired[list[str]]
|
||||||
sn: str
|
sn: NotRequired[str]
|
||||||
vrs: list[Variant]
|
vrs: NotRequired[list[Variant]]
|
||||||
|
|
||||||
class SenseSequence(TypedDict):
|
|
||||||
sense: Sense
|
|
||||||
sen: Sense
|
|
||||||
|
|
||||||
class Definition(TypedDict):
|
class Definition(TypedDict):
|
||||||
sseq: list[SenseSequence]
|
sseq: list[list[list[Any]]]
|
||||||
vd: str
|
vd: NotRequired[str]
|
||||||
|
|
||||||
|
class Pair(TypedDict):
|
||||||
|
objType: str
|
||||||
|
obj: list[Sense]|Sense|str|list[VerbalIllustration]|list[Any]
|
||||||
|
|
||||||
class EntryX(TypedDict):
|
|
||||||
meta: Meta
|
|
||||||
hom: NotRequired[str]
|
|
||||||
hwi: HeadWordInfo
|
|
||||||
ahws: NotRequired[list[HeadWord]]
|
|
||||||
vrs: NotRequired[list[Variant]]
|
|
||||||
fl: str
|
|
||||||
def_: list[Definition]
|
|
||||||
Entry = TypedDict(
|
Entry = TypedDict(
|
||||||
'Entry',
|
'Entry',
|
||||||
{
|
{
|
||||||
@@ -149,13 +135,29 @@ Entry = TypedDict(
|
|||||||
'def': list[Definition],
|
'def': list[Definition],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
class WordType(TypedDict):
|
||||||
|
word: str
|
||||||
|
source: str
|
||||||
|
definition: dict[str, Any]
|
||||||
|
|
||||||
def fetch(word:str) -> dict[str, Any]:
|
def make_pairs(src: list[Any]) -> list[Pair]:
|
||||||
|
result:list[Pair] = []
|
||||||
|
iters = [iter(src)]*2
|
||||||
|
for entry in zip(*iters):
|
||||||
|
pair = { 'objType': entry[0],
|
||||||
|
'obj': entry[1],
|
||||||
|
}
|
||||||
|
pair = trycast(Pair, pair)
|
||||||
|
assert pair is not None
|
||||||
|
result.append(pair)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def fetch(word:str) -> WordType:
|
||||||
request = QNetworkRequest()
|
request = QNetworkRequest()
|
||||||
url = QUrl(API.format(word=word, key=key))
|
url = QUrl(API.format(word=word, key=key))
|
||||||
request.setUrl(url)
|
request.setUrl(url)
|
||||||
request.setTransferTimeout(3000)
|
request.setTransferTimeout(3000)
|
||||||
reply = Word._nam.get(request)
|
reply = Resources.nam.get(request)
|
||||||
assert reply is not None
|
assert reply is not None
|
||||||
loop = QEventLoop()
|
loop = QEventLoop()
|
||||||
reply.finished.connect(loop.quit)
|
reply.finished.connect(loop.quit)
|
||||||
@@ -195,16 +197,16 @@ def getFirstSound(definition: list[Entry]) -> QUrl:
|
|||||||
return url
|
return url
|
||||||
return QUrl()
|
return QUrl()
|
||||||
|
|
||||||
def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
|
def do_prs(hwi: HeadWordInfo) -> list[Fragment]:
|
||||||
|
r = Resources()
|
||||||
frags: list[Fragment] = []
|
frags: list[Fragment] = []
|
||||||
font = trycast(QFont, Word._resources['fonts']['label'])
|
font = r.labelFont
|
||||||
assert font is not None
|
linkColor = r.linkColor
|
||||||
linkColor = trycast(QColor, Word._resources['colors']['link'])
|
subduedColor = r.subduedColor
|
||||||
assert linkColor is not None
|
|
||||||
subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
|
|
||||||
assert subduedColor is not None
|
|
||||||
|
|
||||||
for pr in prs:
|
if 'prs' not in hwi:
|
||||||
|
return []
|
||||||
|
for pr in hwi['prs']:
|
||||||
if 'pun' in pr:
|
if 'pun' in pr:
|
||||||
pun = pr['pun']
|
pun = pr['pun']
|
||||||
else:
|
else:
|
||||||
@@ -216,6 +218,7 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
|
|||||||
frag = Fragment(pr['mw'], font, color=subduedColor)
|
frag = Fragment(pr['mw'], font, color=subduedColor)
|
||||||
if 'sound' in pr:
|
if 'sound' in pr:
|
||||||
frag.setAudio(soundUrl(pr['sound']))
|
frag.setAudio(soundUrl(pr['sound']))
|
||||||
|
frag.setColor(linkColor)
|
||||||
frags.append(frag)
|
frags.append(frag)
|
||||||
if 'l2' in pr:
|
if 'l2' in pr:
|
||||||
frags.append(
|
frags.append(
|
||||||
@@ -223,38 +226,141 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
|
|||||||
)
|
)
|
||||||
return frags
|
return frags
|
||||||
|
|
||||||
|
def do_sense(sense: Sense|None) -> tuple[list[Fragment], list[Line]]:
|
||||||
|
if sense is None:
|
||||||
|
return ([],[])
|
||||||
|
lines: list[Line] = []
|
||||||
|
frags: list[Fragment] = []
|
||||||
|
r = Resources()
|
||||||
|
if 'sn' in sense:
|
||||||
|
sn = sense['sn']
|
||||||
|
else:
|
||||||
|
sn = ''
|
||||||
|
print(f'{sn}\n\n',json.dumps(sense['dt'], indent=2))
|
||||||
|
iters = [iter(sense['dt'])]*2
|
||||||
|
for pair in zip(*iters):
|
||||||
|
pair = trycast(tuple[str, Any], pair)
|
||||||
|
assert pair is not None
|
||||||
|
print(pair[0])
|
||||||
|
if pair[0] == 'text':
|
||||||
|
line = Line()
|
||||||
|
line.addFragment(
|
||||||
|
Fragment(pair[1], r.textFont, color=r.baseColor)
|
||||||
|
)
|
||||||
|
lines.append(line)
|
||||||
|
return (frags, lines)
|
||||||
|
|
||||||
|
def do_pseq(outer: int,
|
||||||
|
inner: int,
|
||||||
|
pseq: list[list[Pair]]| None ) -> tuple[list[Fragment], list[Line]]:
|
||||||
|
assert pseq is not None
|
||||||
|
lines: list[Line] = []
|
||||||
|
frags: list[Fragment] = []
|
||||||
|
for entry in pseq:
|
||||||
|
pairs = make_pairs(entry)
|
||||||
|
for pair in pairs:
|
||||||
|
if pair['objType'] == 'bs':
|
||||||
|
(newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
|
||||||
|
frags += newFrags
|
||||||
|
lines += newLines
|
||||||
|
elif pair['objType'] == 'sense':
|
||||||
|
(newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
|
||||||
|
frags += newFrags
|
||||||
|
lines += newLines
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown object type {pair['objType']}")
|
||||||
|
return (frags, lines)
|
||||||
|
|
||||||
|
def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]:
|
||||||
|
lines: list[Line] = []
|
||||||
|
r = Resources()
|
||||||
|
for outer, item_o in enumerate(sseq):
|
||||||
|
line = Line()
|
||||||
|
line.addFragment(
|
||||||
|
Fragment(str(outer+1), r.boldFont, color=r.baseColor)
|
||||||
|
)
|
||||||
|
for inner, item_i in enumerate(item_o):
|
||||||
|
line.addFragment(
|
||||||
|
Fragment(chr(ord('a')+inner), r.boldFont, color=r.baseColor)
|
||||||
|
)
|
||||||
|
pairs = make_pairs(item_i)
|
||||||
|
for pair in pairs:
|
||||||
|
objType = pair['objType']
|
||||||
|
if objType == 'sense':
|
||||||
|
sense = trycast(Sense, pair['obj'])
|
||||||
|
(frags, newlines) = do_sense(sense)
|
||||||
|
for frag in frags:
|
||||||
|
line.addFragment(frag)
|
||||||
|
lines.append(line)
|
||||||
|
lines += newlines
|
||||||
|
elif objType == 'sen':
|
||||||
|
raise Exception(f"sen unimplimented")
|
||||||
|
elif objType == 'pseq':
|
||||||
|
pseq = trycast(list[list[Pair]], pair['obj'])
|
||||||
|
(frags, newlines) = do_pseq(inner, outer, trycast(list[list[Pair]], pair['obj']))
|
||||||
|
for frag in frags:
|
||||||
|
line.addFragment(frag)
|
||||||
|
lines.append(line)
|
||||||
|
lines += newlines
|
||||||
|
elif objType == 'bs':
|
||||||
|
raise Exception(f"bs unimplimented")
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown object[{objType}] for \n{json.dumps(pair['obj'],indent=2)}")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def do_def(entry: Definition) -> list[Line]:
|
||||||
|
r = Resources()
|
||||||
|
lines: list[Line] = []
|
||||||
|
assert trycast(Definition, entry) is not None
|
||||||
|
if 'vd' in entry:
|
||||||
|
line = Line()
|
||||||
|
line.addFragment(
|
||||||
|
Fragment(entry['vd'], r.italicFont, color = r.linkColor)
|
||||||
|
)
|
||||||
|
lines.append(line)
|
||||||
|
#
|
||||||
|
# sseg is required
|
||||||
|
#
|
||||||
|
sseq = entry['sseq']
|
||||||
|
lines += do_sseq(sseq)
|
||||||
|
return lines
|
||||||
|
|
||||||
def getDef(definition: list[Entry]) -> list[Line]:
|
def getDef(definition: list[Entry]) -> list[Line]:
|
||||||
lines = []
|
r = Resources()
|
||||||
|
lines:list[Line] = []
|
||||||
#
|
#
|
||||||
# Pull the fonts for ease of use
|
# Pull the fonts for ease of use
|
||||||
#
|
#
|
||||||
headerFont = trycast(QFont, Word._resources['fonts']['header'])
|
headerFont = r.headerFont
|
||||||
assert headerFont is not None
|
textFont = r.textFont
|
||||||
textFont = trycast(QFont, Word._resources['fonts']['text'])
|
labelFont = r.labelFont
|
||||||
assert textFont is not None
|
|
||||||
labelFont = trycast(QFont, Word._resources['fonts']['label'])
|
|
||||||
assert labelFont is not None
|
|
||||||
#
|
#
|
||||||
# Pull the colors for ease of use
|
# Pull the colors for ease of use
|
||||||
#
|
#
|
||||||
baseColor = trycast(QColor, Word._resources['colors']['base'])
|
baseColor = r.baseColor
|
||||||
assert baseColor is not None
|
linkColor = r.linkColor
|
||||||
linkColor = trycast(QColor, Word._resources['colors']['link'])
|
subduedColor = r.subduedColor
|
||||||
assert linkColor is not None
|
|
||||||
subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
|
|
||||||
assert subduedColor is not None
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# No need to figure it out each time it is used
|
# No need to figure it out each time it is used
|
||||||
#
|
#
|
||||||
entries = 0
|
entries = 0
|
||||||
id = definition[0]['meta']['id']
|
id = definition[0]['meta']['id'].lower().split(':')[0]
|
||||||
id = ':'.split(id)[0].lower()
|
uses: dict[str,int] = {}
|
||||||
for entry in definition:
|
for entry in definition:
|
||||||
if entry['meta']['id'].lower() == id:
|
testId = entry['meta']['id'].lower().split(':')[0]
|
||||||
|
if testId == id:
|
||||||
entries += 1
|
entries += 1
|
||||||
|
try:
|
||||||
|
uses[entry['fl']] = uses.get(entry['fl'], 0) + 1
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
used: dict[str, int] = {}
|
||||||
|
for k in uses.keys():
|
||||||
|
used[k] = 0
|
||||||
for count, entry in enumerate(definition):
|
for count, entry in enumerate(definition):
|
||||||
if entry['meta']['id'].lower() != id:
|
testId = entry['meta']['id'].lower().split(':')[0]
|
||||||
|
if testId != id:
|
||||||
continue
|
continue
|
||||||
#
|
#
|
||||||
# Create the First line from the hwi, [ahws] and fl
|
# Create the First line from the hwi, [ahws] and fl
|
||||||
@@ -270,13 +376,16 @@ def getDef(definition: list[Entry]) -> list[Line]:
|
|||||||
for ahw in ahws:
|
for ahw in ahws:
|
||||||
hw = re.sub(r'\*', '', ahw['hw'])
|
hw = re.sub(r'\*', '', ahw['hw'])
|
||||||
line.addFragment(Fragment(', ' + hw, headerFont, color=baseColor))
|
line.addFragment(Fragment(', ' + hw, headerFont, color=baseColor))
|
||||||
if 'hom' in entry:
|
if entries > 1:
|
||||||
|
frag = Fragment(f" {count + 1} of {entries} ", textFont, color= subduedColor)
|
||||||
|
frag.setBackground(QColor(Qt.GlobalColor.gray))
|
||||||
|
line.addFragment(frag)
|
||||||
if 'fl' in entry:
|
if 'fl' in entry:
|
||||||
frag = Fragment(f"{count} of {entries} ", textFont, color=
|
text = entry['fl']
|
||||||
frag.setBackground(QColor(Qt.GlobalColor.gray))
|
used[text] += 1
|
||||||
line.addFragment(frag)
|
if uses[text] > 1:
|
||||||
line.addFragment(Fragment(entry['fl'], labelFont, color=baseColor))
|
text += f' ({used[text]})'
|
||||||
|
line.addFragment(Fragment(text, labelFont, color=baseColor))
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -284,11 +393,15 @@ def getDef(definition: list[Entry]) -> list[Line]:
|
|||||||
# While 'prs' is optional, the headword is not. This gets us what we want.
|
# While 'prs' is optional, the headword is not. This gets us what we want.
|
||||||
#
|
#
|
||||||
line = Line()
|
line = Line()
|
||||||
hw = re.sub(r'\*', '\u00b7', hwi['hw'])
|
if hwi['hw'].find('*') >= 0:
|
||||||
line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
|
hw = re.sub(r'\*', '\u00b7', hwi['hw'])
|
||||||
for frag in do_prs(hwi['prs']):
|
line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
|
||||||
|
for frag in do_prs(hwi):
|
||||||
line.addFragment(frag)
|
line.addFragment(frag)
|
||||||
|
if len(line.getLine()) > 0:
|
||||||
#
|
lines.append(line)
|
||||||
# Try for
|
defines = trycast(list[Definition], entry['def'])
|
||||||
return [Line()]
|
assert defines is not None
|
||||||
|
for define in defines:
|
||||||
|
lines += do_def(define)
|
||||||
|
return lines
|
||||||
|
|||||||
Reference in New Issue
Block a user