Skip to content

Commit

Permalink
Simplify bolding logic, remove font weight bolding
Browse files Browse the repository at this point in the history
It's not that useful since the result in Anki is the same and
significantly complicates the logic
  • Loading branch information
1over137 committed Mar 21, 2024
1 parent 5564886 commit b98a4c2
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 225 deletions.
4 changes: 4 additions & 0 deletions vocabsieve/config/base_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def update_map(v):
def update_json(v):
settings.setValue(key, json.dumps(v))

# Initialize the setting if not present
if settings.value(key) is None:
settings.setValue(key, default)

if isinstance(widget, QCheckBox):
widget.setChecked(settings.value(key, default, type=bool))
widget.clicked.connect(update)
Expand Down
21 changes: 3 additions & 18 deletions vocabsieve/config/general_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,7 @@ def initWidgets(self):
"Lemmatize words before trying to find them in the frequency list." +
"\nUse this for frequency lists displayed on FLT.org, but do not use it " +
"\nfor frequency lists from Migaku. ")
self.bold_style = QComboBox()
self.bold_style.setToolTip(
'"Font weight" bolds words directly on the textbox.\n'
'"Underscores" displays bolded words in double underscores, __word__\n'
'(Both will look the same in Anki)\n'
'"<disabled>" disables bolding words in both Vocabsieve and Anki')
self.bold_word = QCheckBox("Bold selected word")
self.audio_format = QComboBox()
self.freq_source = QComboBox()
self.gtrans_lang = QComboBox()
Expand All @@ -48,11 +43,6 @@ def setupWidgets(self) -> None:
"Tatoeba",
"Custom (Enter below)"
])
self.bold_style.addItems([
BOLD_STYLES[1],
BOLD_STYLES[2],
BOLD_STYLES[0]
])
self.gtrans_lang.addItems(langs_supported.values())

self.importdict.clicked.connect(self.dictmanager)
Expand All @@ -64,7 +54,7 @@ def setupLayout(self):
QLabel("Target language"),
self.target_language)

layout.addRow(QLabel("Bold words"), self.bold_style)
layout.addRow(self.bold_word)

layout.addRow(QLabel("Forvo audio format"), self.audio_format)
layout.addRow(QLabel("<i>◊ Choose mp3 for playing on iOS, "
Expand Down Expand Up @@ -113,12 +103,6 @@ def setupAutosave(self):
self.register_config_handler(self.audio_format, 'audio_format', 'mp3')
self.register_config_handler(self.lemfreq, 'lemfreq', True)

self.bold_style.setCurrentText(BOLD_STYLES[
settings.value("bold_style", 1, type=int)])
self.bold_style.currentTextChanged.connect(
lambda t: settings.setValue(
"bold_style", BOLD_STYLES.index(t) if t in BOLD_STYLES else 1))

self.register_config_handler(
self.gtrans_lang,
'gtrans_lang',
Expand All @@ -130,6 +114,7 @@ def setupAutosave(self):
self.web_preset,
'web_preset',
'English Wiktionary')
self.register_config_handler(self.bold_word, 'bold_word', True)
self.register_config_handler(self.custom_url, 'custom_url', "https://en.wiktionary.org/wiki/@@@@")
self.target_language.currentTextChanged.connect(self.load_dictionaries)
self.target_language.currentTextChanged.connect(self.load_freq_sources)
Expand Down
6 changes: 3 additions & 3 deletions vocabsieve/importer/GenericImporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ..ui.main_window_base import MainWindowBase
from .models import ReadingNote
from ..models import SRSNote
from ..tools import prepareAnkiNoteDict, addNotes
from ..tools import prepareAnkiNoteDict, addNotes, remove_punctuations
from .utils import truncate_middle

import re
Expand Down Expand Up @@ -160,9 +160,9 @@ def defineWords(self) -> None:
logger.debug(f"Handling reading note: {note}")
self.lastDate = max(note.date, self.lastDate)
# Remove punctuations
word = re.sub('[\\?\\.!«»…,()\\[\\]]*', "", note.lookup_term)
word = remove_punctuations(note.lookup_term)
if settings.value("bold_word", True, type=bool):
sentence = note.sentence.replace("_", "").replace(word, f"<strong>{word}</strong>")
sentence = note.sentence.replace(word, f"<strong>{word}</strong>")
else:
sentence = note.sentence

Expand Down
5 changes: 2 additions & 3 deletions vocabsieve/importer/KindleVocabImporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import os
import re
from PyQt5.QtWidgets import QCheckBox, QLabel
from ..tools import grouper
from ..lemmatizer import lem_word
from ..tools import grouper, remove_punctuations
from .models import ReadingNote
from ..models import LookupRecord
from ..global_names import settings
Expand Down Expand Up @@ -40,7 +39,7 @@ def getNotes(self):
with open(clippings_path, encoding="utf-8") as file:
clippings_titleauthors, _, _, clippings_words, _ = zip(
*list(grouper(file.read().splitlines(), 5))) # type: ignore
clippings_words = [re.sub('[\\?\\.!«»…,()\\[\\]]*', "", str(word)).lower()
clippings_words = [remove_punctuations(str(word)).lower()
for word in clippings_words] # type: ignore
clippings_titles = [remove_author(titleauthor.strip("\ufeff"))
for titleauthor in clippings_titleauthors]
Expand Down
42 changes: 16 additions & 26 deletions vocabsieve/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import qdarktheme
from .global_names import datapath, lock, app, settings # First local import
from .text_manipulation import apply_bold_char, apply_bold_tags, bold_word_in_text
from .analyzer import BookAnalyzer
from .config import ConfigDialog
from .stats import StatisticsWindow
Expand All @@ -42,6 +41,7 @@
make_dict_source,
getVersion,
make_freq_source,
remove_punctuations,
unix_milliseconds_to_datetime_str,
apply_word_rules)
from .ui import MainWindowBase, WordMarkingDialog
Expand Down Expand Up @@ -744,6 +744,7 @@ def findDuplicates(self, word: str, sentence: str) -> list[int]:

def lookup(self, target: str, no_lemma=False, trigger=LookupTrigger.double_clicked) -> None:
target = target.strip()
target = remove_punctuations(target)
# refuse to look up if the word is empty
if not target:
return
Expand Down Expand Up @@ -867,29 +868,18 @@ def clipboardChanged(self, even_when_focused=False, selection=False):
def discard_current_audio(self):
self.audio_selector.clear()

def boldWordInSentence(self, word) -> None:
sentence_text = self.sentence.unboldedText
if settings.value("bold_style", type=int) != 0:
# Bold word that was clicked on, either with "<b>{word}</b>" or
# "__{word}__".
if settings.value("bold_style", type=int) == 1:
apply_bold = apply_bold_tags
elif settings.value("bold_style", type=int) == 2:
apply_bold = apply_bold_char
else:
raise ValueError("Invalid bold style")

sentence_text = bold_word_in_text(
word,
sentence_text,
apply_bold,
self.getLanguage()
)

if sentence_text is not None:
self.sentence.setHtml(sentence_text)

QCoreApplication.processEvents()
def boldWordInSentence(self, word: str) -> None:
self.sentence.unbold()
sentence_text = self.sentence.toPlainText()
# Remove all bold underscores
# Add bold underscores around for each word with the same lemma
lemma = lem_word(word, self.getLanguage())
already_bolded = set()
for token in sentence_text.split():
token = re.sub('[\\?\\.!«»…()\\[\\]]*', "", token)
if lem_word(token, self.getLanguage()) == lemma and token not in already_bolded:
self.sentence.bold(token)
already_bolded.add(token)

def getLanguage(self) -> str:
return settings.value("target_language", "en") # type: ignore
Expand All @@ -902,7 +892,7 @@ def createNote(self) -> None:
return

allow_duplicates = False
sentence = self.sentence.textBoldedByTags.replace("\n", "<br>")
sentence = self.sentence.toAnki()
if note_ids := self.findDuplicates(self.word.text(), sentence):
msgBox = QMessageBox()
msgBox.setIcon(QMessageBox.Warning)
Expand Down Expand Up @@ -934,7 +924,7 @@ def createNote(self) -> None:

note = SRSNote(
word=self.word.text(),
sentence=self.sentence.textBoldedByTags.replace("\n", "<br>"),
sentence=sentence,
definition1=self.definition.toAnki(),
definition2=self.definition2.toAnki(),
audio_path=self.audio_selector.current_audio_path,
Expand Down
80 changes: 0 additions & 80 deletions vocabsieve/text_manipulation.py

This file was deleted.

4 changes: 4 additions & 0 deletions vocabsieve/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,7 @@ def process_defi_anki(plaintext: str, markdown: str, defi: Definition, source: D
return defi.definition or "" # no editing, just send the original html, using toHtml will change the html
case _:
raise NotImplementedError(f"Unknown display mode {source.display_mode}")


def remove_punctuations(s: str) -> str:
return re.sub('[\\?\\.!«»…,()\\[\\]]*', "", s)
109 changes: 14 additions & 95 deletions vocabsieve/ui/searchable_boldable_text_edit.py
Original file line number Diff line number Diff line change
@@ -1,100 +1,19 @@
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSlot
from .searchable_text_edit import SearchableTextEdit
from ..text_manipulation import (markdown_boldings_to_bold_tag_boldings,
bold_char_boldings_to_bold_tag_boldings,
apply_bold_tags,
remove_bold_char_boldings
)
from ..global_names import logger
import re
from ..global_names import settings


class SearchableBoldableTextEdit(SearchableTextEdit):
def __init__(self):
super().__init__()
self.currentCharFormatChanged.connect(self.handleCurrentCharFormatChanged)

@property
def textBoldedByTags(self):
"""
Current text content with bolded words defined by <b/>, irrespective of
`settings.value("bold_style")`.
"""
if settings.value("bold_style", type=int) == 1:
return markdown_boldings_to_bold_tag_boldings(
# toMarkdown() includes erroneous newlines
self.toMarkdown()[:-2])
elif settings.value("bold_style", type=int) == 2:
return bold_char_boldings_to_bold_tag_boldings(self.toPlainText())[0]
else:
return self.toPlainText()

def keyPressEvent(self, e):
super().keyPressEvent(e)

# Every time the user writes "_", check if we need to perform the
# substitution "__{word}__" -> "<b>{word}</b>"
if settings.value("bold_style", type=int) == 1 \
and e.text() == settings.value("bold_char"):
self.performBoldSubstitutions()

def performBoldSubstitutions(self):
bold_tags_substituted, subs_performed = \
bold_char_boldings_to_bold_tag_boldings(self.toPlainText())

if subs_performed:
def rebold_previously_bolded_words(string: str):
for should_bold in set(re.findall(r"\*\*(.+?)\*\*", self.toMarkdown())):
string = re.sub(
re.escape(should_bold),
lambda match: apply_bold_tags(match.group(0)),
string)
return string

bold_tags_substituted = rebold_previously_bolded_words(
bold_tags_substituted)

# Save cursor position
old_cursor_pos = self.textCursor().position()
# Set text
self.setText(bold_tags_substituted)

# Move back by the 4 characters removed when substituting
# "__{word}__" => "<b>{word}</b>" (note that `cursor` does not
# consider "<b/>" when calling `cursor.setPosition()`
cursor = self.textCursor()
cursor.setPosition(old_cursor_pos - 4 * subs_performed)
self.setTextCursor(cursor)

@pyqtSlot()
def handleCurrentCharFormatChanged(self):
"""
If `settings.value("bold_style") == BoldStyle.FONTWEIGHT.value`, bolded characters are
added to the text editor. By default, the user cannot type in non-bold
font adjacent to these characters, so we reset the font weight to
non-bold every time it changes.
"""

def ensureNormalFontWeight():
cursor = self.textCursor()
fmt = cursor.charFormat()
if fmt.fontWeight() != QFont.Weight.Normal:
fmt.setFontWeight(QFont.Weight.Normal)
cursor.setCharFormat(fmt)
self.setTextCursor(cursor)
ensureNormalFontWeight()

@property
def unboldedText(self):
if settings.value("bold_style", type=int) == 1:
# `.toPlainText()` strips <b/> for us
return self.toPlainText()
elif settings.value("bold_style", type=int) == 2:
# Remove bolds using bold_char
return remove_bold_char_boldings(self.toPlainText())
elif settings.value("bold_style", type=int) == 0:
# Text was never bolded in the first place
return self.toPlainText()
else:
raise ValueError("Invalid bold style")
def unbold(self):
self.setPlainText(self.toPlainText().replace('__', ''))

def bold(self, word):
logger.debug(f'bolding {word}')
self.setPlainText(re.sub(r'\b' + re.escape(word) + r'\b', '__' + word + '__', self.toPlainText()))

def toAnki(self):
# substitute __word__ with <b>word</b>
result = re.sub(r'__(.*?)__', r'<b>\1</b>', self.toPlainText())
# substitute newlines with <br>
result = result.replace('\n', '<br>')
return result

0 comments on commit b98a4c2

Please sign in to comment.