209 lines
7.3 KiB
Python
209 lines
7.3 KiB
Python
# This file is a part of Lector, a Qt based ebook reader
|
|
# Copyright (C) 2017-2019 BasioMeusPuga
|
|
|
|
# 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 json
|
|
import logging
|
|
import urllib.request
|
|
|
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
from PyQt5 import QtMultimedia
|
|
multimedia_available = True
|
|
except ImportError:
|
|
error_string = 'QtMultimedia not found. Sounds will not play.'
|
|
logger.error(error_string)
|
|
multimedia_available = False
|
|
|
|
from lector.resources import definitions
|
|
|
|
|
|
class DefinitionsUI(QtWidgets.QDialog, definitions.Ui_Dialog):
|
|
def __init__(self, parent):
|
|
super(DefinitionsUI, self).__init__()
|
|
self.setupUi(self)
|
|
self._translate = QtCore.QCoreApplication.translate
|
|
|
|
self.parent = parent
|
|
self.previous_position = None
|
|
|
|
self.setWindowFlags(
|
|
QtCore.Qt.Popup |
|
|
QtCore.Qt.FramelessWindowHint)
|
|
|
|
radius = 15
|
|
path = QtGui.QPainterPath()
|
|
path.addRoundedRect(QtCore.QRectF(self.rect()), radius, radius)
|
|
|
|
try:
|
|
mask = QtGui.QRegion(path.toFillPolygon().toPolygon())
|
|
self.setMask(mask)
|
|
except TypeError: # Required for older versions of Qt
|
|
pass
|
|
|
|
self.definitionView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
|
self.app_id = 'bb7a91f9'
|
|
self.app_key = 'fefacdf6775c347b52e9efa2efe642ef'
|
|
|
|
self.root_url = 'https://od-api.oxforddictionaries.com:443/api/v1/inflections/'
|
|
self.define_url = 'https://od-api.oxforddictionaries.com:443/api/v1/entries/'
|
|
|
|
self.pronunciation_mp3 = None
|
|
|
|
self.okButton.clicked.connect(self.hide)
|
|
self.dialogBackground.clicked.connect(self.color_background)
|
|
if multimedia_available:
|
|
self.pronounceButton.clicked.connect(self.play_pronunciation)
|
|
else:
|
|
self.pronounceButton.setEnabled(False)
|
|
|
|
def api_call(self, url, word):
|
|
language = self.parent.settings['dictionary_language']
|
|
url = url + language + '/' + word.lower()
|
|
|
|
req = urllib.request.Request(url)
|
|
req.add_header('app_id', self.app_id)
|
|
req.add_header('app_key', self.app_key)
|
|
|
|
try:
|
|
response = urllib.request.urlopen(req)
|
|
if response.getcode() == 200:
|
|
return_json = json.loads(response.read())
|
|
return return_json
|
|
except Exception as e:
|
|
this_error = f'API access error'
|
|
logger.exception(this_error + f' {type(e).__name__} Arguments: {e.args}')
|
|
self.parent.display_error_notification(None)
|
|
return None
|
|
|
|
def find_definition(self, word):
|
|
word_root_json = self.api_call(self.root_url, word)
|
|
if not word_root_json:
|
|
logger.error('Word root json noped out: ' + word)
|
|
self.set_text(word, None, None, True)
|
|
return
|
|
|
|
word_root = word_root_json['results'][0]['lexicalEntries'][0]['inflectionOf'][0]['id']
|
|
self.pronounceButton.setToolTip(f'Pronounce "{word_root}"')
|
|
|
|
definition_json = self.api_call(self.define_url, word_root)
|
|
if not definition_json:
|
|
logger.error('Definition json noped out: ' + word_root)
|
|
self.set_text(word, None, None, True)
|
|
return
|
|
|
|
definitions = {}
|
|
for i in definition_json['results'][0]['lexicalEntries']:
|
|
category = i['lexicalCategory']
|
|
|
|
try:
|
|
self.pronunciation_mp3 = i['pronunciations'][0]['audioFile']
|
|
except KeyError:
|
|
self.pronounceButton.setEnabled(False)
|
|
|
|
this_sense = i['entries'][0]['senses']
|
|
for j in this_sense:
|
|
try:
|
|
this_definition = j['definitions'][0].capitalize()
|
|
except KeyError:
|
|
# The API also reports crossReferenceMarkers here
|
|
this_definition = '<Not found>'
|
|
|
|
try:
|
|
definitions[category].add(this_definition)
|
|
except KeyError:
|
|
definitions[category] = set()
|
|
definitions[category].add(this_definition)
|
|
|
|
self.set_text(word, word_root, definitions)
|
|
|
|
def set_text(self, word, word_root, definitions, nothing_found=False):
|
|
html_string = ''
|
|
|
|
# Word heading
|
|
html_string += f'<h2><em><strong>{word}</strong></em></h2>\n'
|
|
|
|
if nothing_found:
|
|
nope_string = self._translate('DefinitionsUI', 'No definitions found in')
|
|
language = self.parent.settings['dictionary_language'].upper()
|
|
html_string += f'<p><em>{nope_string} {language}<em></p>\n'
|
|
else:
|
|
# Word root
|
|
html_string += f'<p><em>Word root: <em>{word_root}</p>\n'
|
|
|
|
# Definitions per category as an ordered list
|
|
for i in definitions.items():
|
|
category = i[0]
|
|
html_string += f'<p><strong>{category}</strong>:</p>\n<ol>\n'
|
|
|
|
for j in i[1]:
|
|
html_string += f'<li>{j}</li>\n'
|
|
|
|
html_string += '</ol>\n'
|
|
|
|
self.definitionView.setHtml(html_string)
|
|
self.show()
|
|
|
|
def color_background(self, set_initial=False):
|
|
if set_initial:
|
|
background = self.parent.settings['dialog_background']
|
|
else:
|
|
self.previous_position = self.pos()
|
|
self.parent.get_color()
|
|
background = self.parent.settings['dialog_background']
|
|
|
|
# Calculate inverse color for the background so that
|
|
# the text doesn't look blank
|
|
r, g, b, alpha = background.getRgb()
|
|
inv_average = 255 - (r + g + b) // 3
|
|
if 100 < inv_average < 150:
|
|
inv_average = 255
|
|
|
|
foreground = QtGui.QColor(
|
|
inv_average, inv_average, inv_average, alpha)
|
|
|
|
self.setStyleSheet(
|
|
"QDialog {{background-color: {0}}}".format(background.name()))
|
|
self.definitionView.setStyleSheet(
|
|
"QTextBrowser {{color:{0}; background-color: {1}}}".format(
|
|
foreground.name(), background.name()))
|
|
|
|
if not set_initial:
|
|
self.show()
|
|
|
|
def play_pronunciation(self):
|
|
if not self.pronunciation_mp3 or not multimedia_available:
|
|
return
|
|
|
|
media_content = QtMultimedia.QMediaContent(
|
|
QtCore.QUrl(self.pronunciation_mp3))
|
|
|
|
player = QtMultimedia.QMediaPlayer(self)
|
|
player.setMedia(media_content)
|
|
player.play()
|
|
|
|
def showEvent(self, event):
|
|
self.color_background(True)
|
|
|
|
size = self.size()
|
|
desktop_size = QtWidgets.QDesktopWidget().screenGeometry()
|
|
top = (desktop_size.height() / 2) - (size.height() / 2)
|
|
left = (desktop_size.width() / 2) - (size.width() / 2)
|
|
self.move(left, top)
|