Lint
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# pyright: ignore
|
||||
from .utils import query_error # isort: skip
|
||||
from .books import Book
|
||||
from .definition import Definition, Fragment, Line
|
||||
from .person import PersonDialog
|
||||
from .read import ReadDialog
|
||||
from .session import SessionDialog
|
||||
from .words import DefinitionArea, Word
|
||||
from .definition import Fragment, Line, Definition
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
import re
|
||||
from typing import Any, Optional, Self, cast, overload
|
||||
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 typing import Any, Callable, Optional, Self, cast, overload
|
||||
|
||||
from PyQt6.QtCore import QMargins, QPoint, QRect, QSize, Qt, QUrl, pyqtSignal
|
||||
from PyQt6.QtGui import (
|
||||
QBrush,
|
||||
QColor,
|
||||
QFont,
|
||||
QFontMetrics,
|
||||
QMouseEvent,
|
||||
QPainter,
|
||||
QPaintEvent,
|
||||
QResizeEvent,
|
||||
QTextOption,
|
||||
QTransform,
|
||||
)
|
||||
from PyQt6.QtWidgets import QWidget
|
||||
|
||||
|
||||
class Fragment:
|
||||
"""A fragment of text to be displayed"""
|
||||
|
||||
_indentAmount = 35
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
which: str|Self,
|
||||
font: QFont|None = None,
|
||||
audio: str = "",
|
||||
color: Optional[QColor] = None,
|
||||
asis: bool = False,
|
||||
self,
|
||||
which: str | Self,
|
||||
font: QFont | None = None,
|
||||
audio: str = "",
|
||||
color: Optional[QColor] = None,
|
||||
asis: bool = False,
|
||||
) -> None:
|
||||
if isinstance(which, Fragment):
|
||||
for k,v in which.__dict__.items():
|
||||
for k, v in which.__dict__.items():
|
||||
self.__dict__[k] = v
|
||||
return
|
||||
self._text:str = which
|
||||
self._text: str = which
|
||||
if font is None:
|
||||
raise TypeError("Missing required parameter 'font'")
|
||||
self._font = font
|
||||
@@ -63,13 +76,14 @@ class Fragment:
|
||||
return f"({self._position.x()}, {self._position.y()}): {self._text}"
|
||||
|
||||
@overload
|
||||
def paintEvent(self, widthSrc:int) -> QSize:
|
||||
def paintEvent(self, widthSrc: int) -> QSize:
|
||||
...
|
||||
|
||||
@overload
|
||||
def paintEvent(self, widthSrc: QPainter) -> int:
|
||||
...
|
||||
|
||||
def paintEvent(self, widthSrc) -> int|QSize:
|
||||
def paintEvent(self, widthSrc: QPainter | int) -> int | QSize:
|
||||
if isinstance(widthSrc, QPainter):
|
||||
viewportWidth = widthSrc.viewport().width()
|
||||
painter = widthSrc
|
||||
@@ -77,28 +91,21 @@ class Fragment:
|
||||
viewportWidth = widthSrc
|
||||
painter = None
|
||||
fm = QFontMetrics(self._font)
|
||||
top = (
|
||||
self._position.y()
|
||||
+ fm.descent()
|
||||
- fm.height()
|
||||
)
|
||||
top = self._position.y() + fm.descent() - fm.height()
|
||||
left = self._position.x()
|
||||
width = viewportWidth - left
|
||||
height = 2000
|
||||
rect = QRect(left, top, width, height)
|
||||
indent = self._indent * self._indentAmount
|
||||
flags = (
|
||||
Qt.AlignmentFlag.AlignLeft
|
||||
| Qt.AlignmentFlag.AlignBaseline
|
||||
)
|
||||
flags = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBaseline
|
||||
boundingNoWrap = fm.boundingRect(
|
||||
rect, flags|Qt.TextFlag.TextSingleLine, self._text
|
||||
rect, flags | Qt.TextFlag.TextSingleLine, self._text
|
||||
)
|
||||
bounding = fm.boundingRect(
|
||||
rect, flags|Qt.TextFlag.TextWordWrap, self._text
|
||||
rect, flags | Qt.TextFlag.TextWordWrap, self._text
|
||||
)
|
||||
text = self._text
|
||||
remainingText = ''
|
||||
remainingText = ""
|
||||
if boundingNoWrap.height() < bounding.height():
|
||||
#
|
||||
# This is not optimal, but it is only a few iterations
|
||||
@@ -107,39 +114,29 @@ class Fragment:
|
||||
char = 0
|
||||
pos = rect.x()
|
||||
while pos < rect.right():
|
||||
if text[char] == ' ':
|
||||
if text[char] == " ":
|
||||
lastSpace = char
|
||||
pos += fm.horizontalAdvance(
|
||||
text[char]
|
||||
)
|
||||
pos += fm.horizontalAdvance(text[char])
|
||||
char += 1
|
||||
if lastSpace > 0:
|
||||
remainingText = text[lastSpace+1:]
|
||||
remainingText = text[lastSpace + 1 :]
|
||||
text = text[:lastSpace]
|
||||
|
||||
size = boundingNoWrap.size()
|
||||
|
||||
boundingNoWrap = fm.boundingRect(
|
||||
rect, flags|Qt.TextFlag.TextSingleLine, text
|
||||
rect, flags | Qt.TextFlag.TextSingleLine, text
|
||||
)
|
||||
rect.setSize(boundingNoWrap.size())
|
||||
|
||||
|
||||
if remainingText != '':
|
||||
if remainingText != "":
|
||||
top += size.height()
|
||||
remainingRect = QRect(
|
||||
indent, top,
|
||||
viewportWidth - indent, height
|
||||
)
|
||||
remainingRect = QRect(indent, top, viewportWidth - indent, height)
|
||||
boundingRemaingRect = fm.boundingRect(
|
||||
remainingRect, flags | Qt.TextFlag.TextWordWrap, remainingText
|
||||
)
|
||||
|
||||
size = size.grownBy(
|
||||
QMargins(
|
||||
0,0,0, boundingRemaingRect.height()
|
||||
)
|
||||
)
|
||||
size = size.grownBy(QMargins(0, 0, 0, boundingRemaingRect.height()))
|
||||
remainingRect.setSize(boundingRemaingRect.size())
|
||||
size = size.grownBy(self._margin)
|
||||
size = size.grownBy(self._border)
|
||||
@@ -163,12 +160,14 @@ class Fragment:
|
||||
brush.setColor(self._background)
|
||||
brush.setStyle(Qt.BrushStyle.SolidPattern)
|
||||
painter.setBrush(brush)
|
||||
painter.fillRect(rect,brush)
|
||||
painter.fillRect(rect, brush)
|
||||
painter.drawText(rect, flags, text)
|
||||
if remainingText:
|
||||
if self._background.isValid():
|
||||
painter.fillRect(remainingRect, brush)
|
||||
painter.drawText(remainingRect, flags|Qt.TextFlag.TextWordWrap, remainingText)
|
||||
painter.drawText(
|
||||
remainingRect, flags | Qt.TextFlag.TextWordWrap, remainingText
|
||||
)
|
||||
painter.restore()
|
||||
return size.height()
|
||||
|
||||
@@ -310,6 +309,7 @@ class Fragment:
|
||||
def setBackground(self, color: QColor) -> None:
|
||||
self._background = color
|
||||
return
|
||||
|
||||
def setIndent(self, indent: int) -> None:
|
||||
self._indent = indent
|
||||
return
|
||||
@@ -365,8 +365,10 @@ class Fragment:
|
||||
def pixelIndent(self) -> int:
|
||||
return self._indent * self._indentAmount
|
||||
|
||||
|
||||
class Line:
|
||||
parseText = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._maxHeight = -1
|
||||
self._baseLine = -1
|
||||
@@ -379,11 +381,12 @@ class Line:
|
||||
"|".join([x.text() for x in self._fragments])
|
||||
+ f"|{self._maxHeight}"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setParseText(cls, call) -> None:
|
||||
def setParseText(cls, call: Callable) -> None:
|
||||
cls.parseText = call
|
||||
return
|
||||
|
||||
|
||||
def paintEvent(self, painter: QPainter) -> int:
|
||||
#
|
||||
# we do not have an event field because we are not a true widget
|
||||
@@ -395,12 +398,16 @@ class Line:
|
||||
lineSpacing = ls
|
||||
return lineSpacing
|
||||
|
||||
|
||||
def addFragment(self, frags: Fragment|list[Fragment],) -> None:
|
||||
def addFragment(
|
||||
self,
|
||||
frags: Fragment | list[Fragment],
|
||||
) -> None:
|
||||
SPEAKER = "\U0001F508"
|
||||
|
||||
if not isinstance(frags, list):
|
||||
frags = [frags, ]
|
||||
frags = [
|
||||
frags,
|
||||
]
|
||||
for frag in frags:
|
||||
if frag.audio().isValid():
|
||||
frag.setText(frag.text() + " " + SPEAKER)
|
||||
@@ -485,6 +492,7 @@ class Line:
|
||||
def getLineSpacing(self) -> int:
|
||||
return self._leading + self._maxHeight
|
||||
|
||||
|
||||
class Definition(QWidget):
|
||||
pronounce = pyqtSignal(str)
|
||||
|
||||
@@ -499,7 +507,7 @@ class Definition(QWidget):
|
||||
|
||||
def setWord(self, word: Any) -> None:
|
||||
self._word = word
|
||||
lines:list[Line] = word.get_def()
|
||||
lines: list[Line] = word.get_def()
|
||||
assert lines is not None
|
||||
self._lines = lines
|
||||
self._buttons: list[Fragment] = []
|
||||
|
||||
@@ -101,7 +101,7 @@ class SoundOff(QObject):
|
||||
|
||||
@pyqtSlot(QMediaPlayer.MediaStatus)
|
||||
def mediaStatus(self, status: QMediaPlayer.MediaStatus) -> None:
|
||||
#print(f"mediaStatus: {status}")
|
||||
# print(f"mediaStatus: {status}")
|
||||
if status == QMediaPlayer.MediaStatus.LoadedMedia:
|
||||
player: Optional[QMediaPlayer] = cast(QMediaPlayer, self.sender())
|
||||
assert player is not None
|
||||
@@ -110,7 +110,7 @@ class SoundOff(QObject):
|
||||
|
||||
@pyqtSlot(QMediaPlayer.PlaybackState)
|
||||
def playbackState(self, state: QMediaPlayer.PlaybackState) -> None:
|
||||
#print(f"playbackState: {state}")
|
||||
# print(f"playbackState: {state}")
|
||||
return
|
||||
|
||||
#
|
||||
@@ -164,7 +164,7 @@ class SoundOff(QObject):
|
||||
continue
|
||||
self._storage[player] = QByteArray(storage)
|
||||
crypto.addData(self._storage[player])
|
||||
#print(player, crypto.result().toHex())
|
||||
# print(player, crypto.result().toHex())
|
||||
crypto.reset()
|
||||
self._buffer[player] = QBuffer(self._storage[player])
|
||||
url = reply.request().url()
|
||||
@@ -172,5 +172,5 @@ class SoundOff(QObject):
|
||||
player.setPosition(0)
|
||||
if player.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia:
|
||||
player.play()
|
||||
#print("play")
|
||||
# print("play")
|
||||
return
|
||||
|
||||
@@ -22,6 +22,7 @@ def query_error(query: QSqlQuery) -> NoReturn:
|
||||
)
|
||||
raise Exception(translate("MainWindow", "SQL Error"))
|
||||
|
||||
|
||||
class Resources:
|
||||
_instance = None
|
||||
nam = QNetworkAccessManager()
|
||||
@@ -39,7 +40,7 @@ class Resources:
|
||||
subduedColor: QColor
|
||||
|
||||
subduedBackground: QColor
|
||||
|
||||
|
||||
def __new__(cls: type[Self]) -> Self:
|
||||
if cls._instance:
|
||||
return cls._instance
|
||||
|
||||
54
lib/words.py
54
lib/words.py
@@ -1,40 +1,46 @@
|
||||
import importlib
|
||||
import pkgutil
|
||||
import json
|
||||
from typing import Any, TypedDict, cast
|
||||
import pkgutil
|
||||
from types import ModuleType
|
||||
from typing import Any, Iterable, TypedDict, cast
|
||||
|
||||
from PyQt6.QtCore import (
|
||||
Qt,
|
||||
pyqtSlot,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSlot
|
||||
from PyQt6.QtSql import QSqlQuery
|
||||
from PyQt6.QtWidgets import QScrollArea
|
||||
|
||||
from lib.utils import query_error
|
||||
from lib.sounds import SoundOff
|
||||
from lib.definition import Definition, Line
|
||||
from trycast import trycast
|
||||
|
||||
import plugins
|
||||
def find_plugins(ns_pkg):
|
||||
return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + '.')
|
||||
from lib.definition import Definition, Line
|
||||
from lib.sounds import SoundOff
|
||||
from lib.utils import query_error
|
||||
|
||||
|
||||
def find_plugins(ns_pkg: ModuleType) -> Iterable[pkgutil.ModuleInfo]:
|
||||
return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".")
|
||||
|
||||
|
||||
discovered_plugins = {
|
||||
# finder, name, ispkg
|
||||
importlib.import_module(name).registration['source']: importlib.import_module(name) for _, name, _ in find_plugins(plugins)
|
||||
importlib.import_module(name).registration[
|
||||
"source"
|
||||
]: importlib.import_module(name)
|
||||
for _, name, _ in find_plugins(plugins)
|
||||
}
|
||||
|
||||
API = "https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
|
||||
|
||||
|
||||
class WordType(TypedDict):
|
||||
word: str
|
||||
source: str
|
||||
definition: str
|
||||
|
||||
|
||||
|
||||
class Word:
|
||||
"""All processing of a dictionary word."""
|
||||
|
||||
_words: dict[str, WordType] = {}
|
||||
|
||||
|
||||
def __init__(self, word: str) -> None:
|
||||
#
|
||||
# Have we already retrieved this word?
|
||||
@@ -79,30 +85,38 @@ class Word:
|
||||
|
||||
@pyqtSlot()
|
||||
def playSound(self) -> None:
|
||||
url = discovered_plugins[self.current['source']].getFirstSound(self.current['definition'])
|
||||
url = discovered_plugins[self.current["source"]].getFirstSound(
|
||||
self.current["definition"]
|
||||
)
|
||||
if url.isValid():
|
||||
snd = SoundOff()
|
||||
snd.playSound(url)
|
||||
return
|
||||
|
||||
def playPRS(self) -> None:
|
||||
return
|
||||
|
||||
def getWord(self) -> str:
|
||||
return cast(str, self.current["word"])
|
||||
return self.current["word"]
|
||||
|
||||
def get_html(self) -> str | None:
|
||||
src = self.current['source']
|
||||
src = self.current["source"]
|
||||
try:
|
||||
return discovered_plugins[src].getHtml(self.current)
|
||||
return cast(str, discovered_plugins[src].getHtml(self.current))
|
||||
except KeyError:
|
||||
raise Exception(f"Unknown source: {src}")
|
||||
|
||||
def get_def(self) -> list[Line]:
|
||||
src = self.current['source']
|
||||
src = self.current["source"]
|
||||
try:
|
||||
lines = discovered_plugins[src].getDef(self.current["definition"])
|
||||
lines = trycast(list[Line], lines)
|
||||
assert lines is not None
|
||||
return lines
|
||||
except KeyError:
|
||||
raise Exception(f"Unknown source: {self.current['source']}")
|
||||
|
||||
|
||||
class DefinitionArea(QScrollArea):
|
||||
def __init__(self, w: Word, *args: Any, **kwargs: Any) -> None:
|
||||
super(DefinitionArea, self).__init__(*args, *kwargs)
|
||||
|
||||
Reference in New Issue
Block a user