convert qt strings to f-strings with flynt

Also revealed an incorrect type def in editor.py that mypy wasn't
noticing before :-(
This commit is contained in:
Damien Elmes 2021-02-11 10:09:06 +10:00
parent 5ab115c145
commit 88c002f4eb
30 changed files with 169 additions and 181 deletions

View file

@ -232,7 +232,7 @@ def setupLangAndBackend(
else:
qt_dir = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
qt_lang = lang.replace("-", "_")
if _qtrans.load("qtbase_" + qt_lang, qt_dir):
if _qtrans.load(f"qtbase_{qt_lang}", qt_dir):
app.installTranslator(_qtrans)
return anki.lang.current_i18n
@ -249,7 +249,7 @@ class AnkiApp(QApplication):
appMsg = pyqtSignal(str)
KEY = "anki" + checksum(getpass.getuser())
KEY = f"anki{checksum(getpass.getuser())}"
TMOUT = 30000
def __init__(self, argv: List[str]) -> None:
@ -319,7 +319,7 @@ def parseArgs(argv: List[str]) -> Tuple[argparse.Namespace, List[str]]:
# as there's no such profile
if isMac and len(argv) > 1 and argv[1].startswith("-psn"):
argv = [argv[0]]
parser = argparse.ArgumentParser(description="Anki " + appVersion)
parser = argparse.ArgumentParser(description=f"Anki {appVersion}")
parser.usage = "%(prog)s [OPTIONS] [file to import/add-on to install]"
parser.add_argument("-b", "--base", help="path to base folder", default="")
parser.add_argument("-p", "--profile", help="profile name to load", default="")
@ -424,7 +424,7 @@ def run() -> None:
QMessageBox.critical(
None,
"Startup Error",
"Please notify support of this error:\n\n" + traceback.format_exc(),
f"Please notify support of this error:\n\n{traceback.format_exc()}",
)

View file

@ -81,7 +81,7 @@ def show(mw: aqt.AnkiQt) -> QDialog:
(add-on provided name [Add-on folder, installed at, version, is config changed])
{newline.join(sorted(inactive))}
"""
info = " " + " ".join(info.splitlines(True))
info = f" {' '.join(info.splitlines(True))}"
QApplication.clipboard().setText(info)
tooltip(tr(TR.ABOUT_COPIED_TO_CLIPBOARD), parent=dialog)
@ -93,9 +93,9 @@ def show(mw: aqt.AnkiQt) -> QDialog:
# WebView contents
######################################################################
abouttext = "<center><img src='/_anki/imgs/anki-logo-thin.png'></center>"
abouttext += "<p>" + tr(TR.ABOUT_ANKI_IS_A_FRIENDLY_INTELLIGENT_SPACED)
abouttext += "<p>" + tr(TR.ABOUT_ANKI_IS_LICENSED_UNDER_THE_AGPL3)
abouttext += "<p>" + tr(TR.ABOUT_VERSION, val=versionWithBuild()) + "<br>"
abouttext += f"<p>{tr(TR.ABOUT_ANKI_IS_A_FRIENDLY_INTELLIGENT_SPACED)}"
abouttext += f"<p>{tr(TR.ABOUT_ANKI_IS_LICENSED_UNDER_THE_AGPL3)}"
abouttext += f"<p>{tr(TR.ABOUT_VERSION, val=versionWithBuild())}<br>"
abouttext += ("Python %s Qt %s PyQt %s<br>") % (
platform.python_version(),
QT_VERSION_STR,
@ -211,8 +211,8 @@ def show(mw: aqt.AnkiQt) -> QDialog:
abouttext += "<p>" + tr(
TR.ABOUT_WRITTEN_BY_DAMIEN_ELMES_WITH_PATCHES, cont=", ".join(allusers)
)
abouttext += "<p>" + tr(TR.ABOUT_IF_YOU_HAVE_CONTRIBUTED_AND_ARE)
abouttext += "<p>" + tr(TR.ABOUT_A_BIG_THANKS_TO_ALL_THE)
abouttext += f"<p>{tr(TR.ABOUT_IF_YOU_HAVE_CONTRIBUTED_AND_ARE)}"
abouttext += f"<p>{tr(TR.ABOUT_A_BIG_THANKS_TO_ALL_THE)}"
abt.label.setMinimumWidth(800)
abt.label.setMinimumHeight(600)
dialog.show()

View file

@ -84,7 +84,7 @@ class AddCards(QDialog):
self.helpButton.setAutoDefault(False)
bb.addButton(self.helpButton, QDialogButtonBox.HelpRole)
# history
b = bb.addButton(tr(TR.ADDING_HISTORY) + " " + downArrow(), ar)
b = bb.addButton(f"{tr(TR.ADDING_HISTORY)} {downArrow()}", ar)
if isMac:
sc = "Ctrl+Shift+H"
else:
@ -149,7 +149,7 @@ class AddCards(QDialog):
fields = note.fields
txt = htmlToTextLine(", ".join(fields))
if len(txt) > 30:
txt = txt[:30] + "..."
txt = f"{txt[:30]}..."
line = tr(TR.ADDING_EDIT, val=txt)
line = gui_hooks.addcards_will_add_history_entry(line, note)
a = m.addAction(line)

View file

@ -309,7 +309,7 @@ class AddonManager:
meta = self.addon_meta(dir)
name = meta.human_name()
if not meta.enabled:
name += " " + tr(TR.ADDONS_DISABLED)
name += f" {tr(TR.ADDONS_DISABLED)}"
return name
# Conflict resolution
@ -741,11 +741,9 @@ class AddonsDialog(QDialog):
name = addon.human_name()
if not addon.enabled:
return name + " " + tr(TR.ADDONS_DISABLED2)
return f"{name} {tr(TR.ADDONS_DISABLED2)}"
elif not addon.compatible():
return (
name + " " + tr(TR.ADDONS_REQUIRES, val=self.compatible_string(addon))
)
return f"{name} {tr(TR.ADDONS_REQUIRES, val=self.compatible_string(addon))}"
return name
@ -818,7 +816,7 @@ class AddonsDialog(QDialog):
if not addon:
return
if re.match(r"^\d+$", addon):
openLink(aqt.appShared + f"info/{addon}")
openLink(f"{aqt.appShared}info/{addon}")
else:
showWarning(tr(TR.ADDONS_ADDON_WAS_NOT_DOWNLOADED_FROM_ANKIWEB))
@ -864,7 +862,7 @@ class AddonsDialog(QDialog):
def onInstallFiles(self, paths: Optional[List[str]] = None) -> Optional[bool]:
if not paths:
key = tr(TR.ADDONS_PACKAGED_ANKI_ADDON) + f" (*{self.mgr.ext})"
key = f"{tr(TR.ADDONS_PACKAGED_ANKI_ADDON)} (*{self.mgr.ext})"
paths_ = getFile(
self, tr(TR.ADDONS_INSTALL_ADDONS), None, key, key="addons", multi=True
)
@ -924,7 +922,7 @@ class GetAddons(QDialog):
saveGeom(self, "getaddons")
def onBrowse(self) -> None:
openLink(aqt.appShared + "addons/2.1")
openLink(f"{aqt.appShared}addons/2.1")
def accept(self) -> None:
# get codes
@ -946,7 +944,7 @@ def download_addon(client: HttpClient, id: int) -> Union[DownloadOk, DownloadErr
"Fetch a single add-on from AnkiWeb."
try:
resp = client.get(
aqt.appShared + f"download/{id}?v=2.1&p={current_point_version}"
f"{aqt.appShared}download/{id}?v=2.1&p={current_point_version}"
)
if resp.status_code != 200:
return DownloadError(status_code=resp.status_code)
@ -1107,7 +1105,7 @@ def show_log_to_user(parent: QWidget, log: List[DownloadLogEntry]) -> None:
text = tr(TR.ADDONS_ONE_OR_MORE_ERRORS_OCCURRED)
else:
text = tr(TR.ADDONS_DOWNLOAD_COMPLETE_PLEASE_RESTART_ANKI_TO)
text += "<br><br>" + download_log_to_html(log)
text += f"<br><br>{download_log_to_html(log)}"
if have_problem:
showWarning(text, textFormat="rich", parent=parent)
@ -1153,7 +1151,7 @@ def _fetch_update_info_batch(
"""Get update info from AnkiWeb.
Chunk must not contain more than 25 ids."""
resp = client.get(aqt.appShared + "updates/" + ",".join(chunk) + "?v=3")
resp = client.get(f"{aqt.appShared}updates/{','.join(chunk)}?v=3")
if resp.status_code == 200:
return resp.json()
else:
@ -1366,7 +1364,7 @@ class ConfigEditor(QDialog):
showInfo(msg)
return
except Exception as e:
showInfo(tr(TR.ADDONS_INVALID_CONFIGURATION) + " " + repr(e))
showInfo(f"{tr(TR.ADDONS_INVALID_CONFIGURATION)} {repr(e)}")
return
if not isinstance(new_conf, dict):
@ -1419,7 +1417,7 @@ def installAddonPackages(
if log:
log_html = "<br>".join(log)
if advise_restart:
log_html += "<br><br>" + tr(TR.ADDONS_PLEASE_RESTART_ANKI_TO_COMPLETE_THE)
log_html += f"<br><br>{tr(TR.ADDONS_PLEASE_RESTART_ANKI_TO_COMPLETE_THE)}"
if len(log) == 1 and not strictly_modal:
tooltip(log_html, parent=parent)
else:

View file

@ -115,7 +115,7 @@ class CardLayout(QDialog):
self.topAreaForm = aqt.forms.clayout_top.Ui_Form()
self.topAreaForm.setupUi(self.topArea)
self.topAreaForm.templateOptions.setText(
tr(TR.ACTIONS_OPTIONS) + " " + downArrow()
f"{tr(TR.ACTIONS_OPTIONS)} {downArrow()}"
)
qconnect(self.topAreaForm.templateOptions.clicked, self.onMore)
qconnect(
@ -305,7 +305,7 @@ class CardLayout(QDialog):
qconnect(pform.preview_front.clicked, self.on_preview_toggled)
qconnect(pform.preview_back.clicked, self.on_preview_toggled)
pform.preview_settings.setText(
tr(TR.CARD_TEMPLATES_PREVIEW_SETTINGS) + " " + downArrow()
f"{tr(TR.CARD_TEMPLATES_PREVIEW_SETTINGS)} {downArrow()}"
)
qconnect(pform.preview_settings.clicked, self.on_preview_settings)
@ -491,7 +491,7 @@ class CardLayout(QDialog):
text = a
# use _showAnswer to avoid the longer delay
self.preview_web.eval("_showAnswer(%s,'%s');" % (json.dumps(text), bodyclass))
self.preview_web.eval(f"_showAnswer({json.dumps(text)},'{bodyclass}');")
self.preview_web.eval(
f"_emulateMobile({json.dumps(self.mobile_emulation_enabled)});"
)
@ -522,14 +522,14 @@ class CardLayout(QDialog):
def answerRepl(match: Match) -> str:
res = self.mw.reviewer.correct("exomple", "an example")
if hadHR:
res = "<hr id=answer>" + res
res = f"<hr id=answer>{res}"
return res
repl: Union[str, Callable]
if type == "q":
repl = "<input id='typeans' type=text value='exomple' readonly='readonly'>"
repl = "<center>%s</center>" % repl
repl = f"<center>{repl}</center>"
else:
repl = answerRepl
return re.sub(r"\[\[type:.+?\]\]", repl, txt)

View file

@ -59,7 +59,7 @@ class CustomStudy(QDialog):
def plus(num: Union[int, str]) -> str:
if num == 1000:
num = "1000+"
return "<b>" + str(num) + "</b>"
return f"<b>{str(num)}</b>"
if idx == RADIO_NEW:
new = self.mw.col.sched.totalNewForCurrentDeck()

View file

@ -288,7 +288,7 @@ class DeckBrowser:
extra = tr(TR.DECKS_IT_HAS_CARD, count=count)
if askUser(
tr(TR.DECKS_ARE_YOU_SURE_YOU_WISH_TO, val=deck["name"]) + " " + extra
f"{tr(TR.DECKS_ARE_YOU_SURE_YOU_WISH_TO, val=deck['name'])} {extra}"
):
return True
return False
@ -332,4 +332,4 @@ class DeckBrowser:
)
def _onShared(self) -> None:
openLink(aqt.appShared + "decks/")
openLink(f"{aqt.appShared}decks/")

View file

@ -130,7 +130,7 @@ class Editor:
None,
"fields",
tr(TR.EDITING_CUSTOMIZE_FIELDS),
tr(TR.EDITING_FIELDS) + "...",
f"{tr(TR.EDITING_FIELDS)}...",
disables=False,
rightside=False,
),
@ -138,7 +138,7 @@ class Editor:
None,
"cards",
tr(TR.EDITING_CUSTOMIZE_CARD_TEMPLATES_CTRLANDL),
tr(TR.EDITING_CARDS) + "...",
f"{tr(TR.EDITING_CARDS)}...",
disables=False,
rightside=False,
),
@ -239,7 +239,7 @@ class Editor:
with open(path, "rb") as fp:
data = fp.read()
data64 = b"".join(base64.encodebytes(data).splitlines())
return "data:%s;base64,%s" % (mime, data64.decode("ascii"))
return f"data:{mime};base64,{data64.decode('ascii')}"
def addButton(
self,
@ -314,7 +314,7 @@ class Editor:
else:
imgelm = ""
if label or not imgelm:
labelelm = """<span class=blabel>{}</span>""".format(label or cmd)
labelelm = f"""<span class=blabel>{label or cmd}</span>"""
else:
labelelm = ""
if id:
@ -539,7 +539,7 @@ class Editor:
if err == 2:
cols[0] = "dupe"
self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
self.web.eval(f"setBackgrounds({json.dumps(cols)});")
def showDupes(self) -> None:
aqt.dialogs.open(
@ -734,23 +734,23 @@ class Editor:
self._wrapWithColour(self.fcolour)
def _updateForegroundButton(self) -> None:
self.web.eval("setFGButton('%s')" % self.fcolour)
self.web.eval(f"setFGButton('{self.fcolour}')")
def onColourChanged(self) -> None:
self._updateForegroundButton()
self.mw.pm.profile["lastColour"] = self.fcolour
def _wrapWithColour(self, colour: str) -> None:
self.web.eval("setFormat('forecolor', '%s')" % colour)
self.web.eval(f"setFormat('forecolor', '{colour}')")
# Audio/video/images
######################################################################
def onAddMedia(self) -> None:
extension_filter = " ".join(
"*." + extension for extension in sorted(itertools.chain(pics, audio))
f"*.{extension}" for extension in sorted(itertools.chain(pics, audio))
)
key = tr(TR.EDITING_MEDIA) + " (" + extension_filter + ")"
key = f"{tr(TR.EDITING_MEDIA)} ({extension_filter})"
def accept(file: str) -> None:
self.addMedia(file, canDelete=True)
@ -770,7 +770,7 @@ class Editor:
except Exception as e:
showWarning(str(e))
return
self.web.eval("setFormat('inserthtml', %s);" % json.dumps(html))
self.web.eval(f"setFormat('inserthtml', {json.dumps(html)});")
def _addMedia(self, path: str, canDelete: bool = False) -> str:
"Add to media folder and return local img or sound tag."
@ -810,15 +810,15 @@ class Editor:
ext = fname.split(".")[-1].lower()
if ext in pics:
name = urllib.parse.quote(fname.encode("utf8"))
return '<img src="%s">' % name
return f'<img src="{name}">'
else:
av_player.play_file(fname)
return "[sound:%s]" % html.escape(fname, quote=False)
return f"[sound:{html.escape(fname, quote=False)}]"
def urlToFile(self, url: str) -> Optional[str]:
l = url.lower()
for suffix in pics + audio:
if l.endswith("." + suffix):
if l.endswith(f".{suffix}"):
return self._retrieveURL(url)
# not a supported type
return None
@ -842,7 +842,7 @@ class Editor:
data = base64.b64decode(b64data, validate=True)
if ext == "jpeg":
ext = "jpg"
return self._addPastedImage(data, "." + ext)
return self._addPastedImage(data, f".{ext}")
return ""
@ -857,7 +857,7 @@ class Editor:
def _addPastedImage(self, data: bytes, ext: str) -> str:
# hash and write
csum = checksum(data)
fname = "{}-{}{}".format("paste", csum, ext)
fname = f"paste-{csum}{ext}"
return self._addMediaFromData(fname, data)
def _retrieveURL(self, url: str) -> Optional[str]:
@ -967,9 +967,7 @@ class Editor:
ext = "true"
else:
ext = "false"
self.web.eval(
"pasteHTML(%s, %s, %s);" % (json.dumps(html), json.dumps(internal), ext)
)
self.web.eval(f"pasteHTML({json.dumps(html)}, {json.dumps(internal)}, {ext});")
def doDrop(self, html: str, internal: bool, extended: bool = False) -> None:
def pasteIfField(ret: bool) -> None:
@ -1187,8 +1185,8 @@ class EditorWebView(AnkiWebView):
token = html.escape(token).replace("\t", " " * 4)
# if there's more than one consecutive space,
# use non-breaking spaces for the second one on
def repl(match: Match) -> None:
return match.group(1).replace(" ", "&nbsp;") + " "
def repl(match: Match) -> str:
return f"{match.group(1).replace(' ', '&nbsp;')} "
token = re.sub(" ( +)", repl, token)
processed.append(token)
@ -1247,7 +1245,7 @@ class EditorWebView(AnkiWebView):
if not mime.hasHtml():
return
html = mime.html()
mime.setHtml("<!--anki-->" + html)
mime.setHtml(f"<!--anki-->{html}")
clip.setMimeData(mime)
def contextMenuEvent(self, evt: QContextMenuEvent) -> None:

View file

@ -95,13 +95,13 @@ class ErrorHandler(QObject):
if self.mw.addonManager.dirty:
txt = markdown(tr(TR.ERRORS_ADDONS_ACTIVE_POPUP))
error = supportText() + self._addonText(error) + "\n" + error
error = f"{supportText() + self._addonText(error)}\n{error}"
else:
txt = markdown(tr(TR.ERRORS_STANDARD_POPUP))
error = supportText() + "\n" + error
error = f"{supportText()}\n{error}"
# show dialog
txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
txt = f"{txt}<div style='white-space: pre-wrap'>{error}</div>"
showText(txt, type="html", copyBtn=True)
def _addonText(self, error: str) -> str:
@ -113,6 +113,6 @@ class ErrorHandler(QObject):
mw.addonManager.addonName(i) for i in dict.fromkeys(reversed(matches))
]
# highlight importance of first add-on:
addons[0] = "<b>{}</b>".format(addons[0])
addons[0] = f"<b>{addons[0]}</b>"
addons_str = ", ".join(addons)
return tr(TR.ADDONS_POSSIBLY_INVOLVED, addons=addons_str) + "\n"
return f"{tr(TR.ADDONS_POSSIBLY_INVOLVED, addons=addons_str)}\n"

View file

@ -60,7 +60,7 @@ class FieldDialog(QDialog):
self.currentIdx = None
self.form.fieldList.clear()
for c, f in enumerate(self.model["flds"]):
self.form.fieldList.addItem("{}: {}".format(c + 1, f["name"]))
self.form.fieldList.addItem(f"{c + 1}: {f['name']}")
def setupSignals(self) -> None:
f = self.form
@ -244,7 +244,7 @@ class FieldDialog(QDialog):
fut.result()
except TemplateError as e:
# fixme: i18n
showWarning("Unable to save changes: " + str(e))
showWarning(f"Unable to save changes: {str(e)}")
return
self.mw.reset()
tooltip("Changes saved.", parent=self.mw)

View file

@ -194,7 +194,7 @@ class ImportDialog(QDialog):
showUnicodeWarning()
return
except Exception as e:
msg = tr(TR.IMPORTING_FAILED_DEBUG_INFO) + "\n"
msg = f"{tr(TR.IMPORTING_FAILED_DEBUG_INFO)}\n"
err = repr(str(e))
if "1-character string" in err:
msg += err
@ -205,7 +205,7 @@ class ImportDialog(QDialog):
showText(msg)
return
else:
txt = tr(TR.IMPORTING_IMPORTING_COMPLETE) + "\n"
txt = f"{tr(TR.IMPORTING_IMPORTING_COMPLETE)}\n"
if self.importer.log:
txt += "\n".join(self.importer.log)
self.close()
@ -326,7 +326,7 @@ def importFile(mw: AnkiQt, file: str) -> None:
if done:
break
for mext in re.findall(r"[( ]?\*\.(.+?)[) ]", i[0]):
if file.endswith("." + mext):
if file.endswith(f".{mext}"):
importerClass = i[1]
done = True
break
@ -352,7 +352,7 @@ def importFile(mw: AnkiQt, file: str) -> None:
if msg == "'unknownFormat'":
showWarning(tr(TR.IMPORTING_UNKNOWN_FILE_FORMAT))
else:
msg = tr(TR.IMPORTING_FAILED_DEBUG_INFO) + "\n"
msg = f"{tr(TR.IMPORTING_FAILED_DEBUG_INFO)}\n"
msg += str(traceback.format_exc())
showText(msg)
return
@ -392,7 +392,7 @@ def importFile(mw: AnkiQt, file: str) -> None:
elif "readonly" in err:
showWarning(tr(TR.IMPORTING_UNABLE_TO_IMPORT_FROM_A_READONLY))
else:
msg = tr(TR.IMPORTING_FAILED_DEBUG_INFO) + "\n"
msg = f"{tr(TR.IMPORTING_FAILED_DEBUG_INFO)}\n"
msg += str(traceback.format_exc())
showText(msg)
else:

View file

@ -397,7 +397,7 @@ class AnkiQt(QMainWindow):
restoreGeom(self, "mainWindow")
restoreState(self, "mainWindow")
# titlebar
self.setWindowTitle(self.pm.name + " - Anki")
self.setWindowTitle(f"{self.pm.name} - Anki")
# show and raise window for osx
self.show()
self.activateWindow()
@ -489,7 +489,7 @@ class AnkiQt(QMainWindow):
)
else:
showWarning(
tr(TR.ERRORS_UNABLE_OPEN_COLLECTION) + "\n" + traceback.format_exc()
f"{tr(TR.ERRORS_UNABLE_OPEN_COLLECTION)}\n{traceback.format_exc()}"
)
# clean up open collection if possible
try:
@ -643,14 +643,14 @@ class AnkiQt(QMainWindow):
def moveToState(self, state: str, *args: Any) -> None:
# print("-> move from", self.state, "to", state)
oldState = self.state or "dummy"
cleanup = getattr(self, "_" + oldState + "Cleanup", None)
cleanup = getattr(self, f"_{oldState}Cleanup", None)
if cleanup:
# pylint: disable=not-callable
cleanup(state)
self.clearStateShortcuts()
self.state = state
gui_hooks.state_will_change(state, oldState)
getattr(self, "_" + state + "State")(oldState, *args)
getattr(self, f"_{state}State")(oldState, *args)
if state != "resetRequired":
self.bottomWeb.show()
gui_hooks.state_did_change(state, oldState)
@ -730,14 +730,13 @@ class AnkiQt(QMainWindow):
i = tr(TR.QT_MISC_WAITING_FOR_EDITING_TO_FINISH)
b = self.button("refresh", tr(TR.QT_MISC_RESUME_NOW), id="resume")
self.web.stdHtml(
"""
<center><div style="height: 100%%">
f"""
<center><div style="height: 100%">
<div style="position:relative; vertical-align: middle;">
%s<br><br>
%s</div></div></center>
{i}<br><br>
{b}</div></div></center>
<script>$('#resume').focus()</script>
"""
% (i, b),
""",
context=web_context,
)
self.bottomWeb.hide()
@ -755,7 +754,7 @@ class AnkiQt(QMainWindow):
id: str = "",
extra: str = "",
) -> str:
class_ = "but " + class_
class_ = f"but {class_}"
if key:
key = tr(TR.ACTIONS_SHORTCUT_KEY, val=key)
else:
@ -989,7 +988,7 @@ title="%s" %s>%s</button>""" % (
def setStateShortcuts(self, shortcuts: List[Tuple[str, Callable]]) -> None:
gui_hooks.state_shortcuts_will_change(self.state, shortcuts)
# legacy hook
runHook(self.state + "StateShortcuts", shortcuts)
runHook(f"{self.state}StateShortcuts", shortcuts)
self.stateShortcuts = self.applyShortcuts(shortcuts)
def clearStateShortcuts(self) -> None:
@ -1285,7 +1284,7 @@ title="%s" %s>%s</button>""" % (
if not existed:
f.write(b"nid\tmid\tfields\n")
for id, mid, flds in col.db.execute(
"select id, mid, flds from notes where id in %s" % ids2str(nids)
f"select id, mid, flds from notes where id in {ids2str(nids)}"
):
fields = splitFields(flds)
f.write(("\t".join([str(id), str(mid)] + fields)).encode("utf8"))
@ -1471,9 +1470,9 @@ title="%s" %s>%s</button>""" % (
buf = ""
for c, line in enumerate(text.strip().split("\n")):
if c == 0:
buf += ">>> %s\n" % line
buf += f">>> {line}\n"
else:
buf += "... %s\n" % line
buf += f"... {line}\n"
try:
to_append = buf + (self._output or "<no output>")
to_append = gui_hooks.debug_console_did_evaluate_python(
@ -1616,7 +1615,7 @@ title="%s" %s>%s</button>""" % (
self.mediaServer.start()
def baseHTML(self) -> str:
return '<base href="%s">' % self.serverURL()
return f'<base href="{self.serverURL()}">'
def serverURL(self) -> str:
return "http://127.0.0.1:%d/" % self.mediaServer.getPort()

View file

@ -33,7 +33,7 @@ def _getExportFolder() -> str:
return webInSrcFolder
elif isMac:
dir = os.path.dirname(os.path.abspath(__file__))
return os.path.abspath(dir + "/../../Resources/web")
return os.path.abspath(f"{dir}/../../Resources/web")
else:
if os.environ.get("TEST_TARGET"):
# running tests in bazel; we have no data
@ -110,7 +110,7 @@ def allroutes(pathin: str) -> Response:
isdir = os.path.isdir(os.path.join(directory, path))
except ValueError:
return flask.make_response(
"Path for '%s - %s' is too long!" % (directory, path),
f"Path for '{directory} - {path}' is too long!",
HTTPStatus.BAD_REQUEST,
)
@ -121,13 +121,13 @@ def allroutes(pathin: str) -> Response:
# protect against directory transversal: https://security.openstack.org/guidelines/dg_using-file-paths.html
if not fullpath.startswith(directory):
return flask.make_response(
"Path for '%s - %s' is a security leak!" % (directory, path),
f"Path for '{directory} - {path}' is a security leak!",
HTTPStatus.FORBIDDEN,
)
if isdir:
return flask.make_response(
"Path for '%s - %s' is a directory (not supported)!" % (directory, path),
f"Path for '{directory} - {path}' is a directory (not supported)!",
HTTPStatus.FORBIDDEN,
)
@ -221,7 +221,7 @@ def _redirectWebExports(path: str) -> Tuple[str, str]:
addMgr = aqt.mw.addonManager
except AttributeError as error:
if devMode:
print("_redirectWebExports: %s" % error)
print(f"_redirectWebExports: {error}")
return None
try:

View file

@ -128,7 +128,7 @@ class Models(QDialog):
self.models = notetypes
for m in self.models:
mUse = tr(TR.BROWSING_NOTE_COUNT, count=m.use_count)
item = QListWidgetItem("%s [%s]" % (m.name, mUse))
item = QListWidgetItem(f"{m.name} [{mUse}]")
self.form.modelsList.addItem(item)
self.form.modelsList.setCurrentRow(row)

View file

@ -119,9 +119,9 @@ class MPVBase:
"""Prepare the argument list for the mpv process."""
self.argv = [self.executable]
self.argv += self.default_argv
self.argv += ["--input-ipc-server=" + self._sock_filename]
self.argv += [f"--input-ipc-server={self._sock_filename}"]
if self.window_id is not None:
self.argv += ["--wid=" + str(self.window_id)]
self.argv += [f"--wid={str(self.window_id)}"]
def _start_process(self):
"""Start the mpv process."""
@ -258,7 +258,7 @@ class MPVBase:
buf = buf[newline + 1 :]
if self.debug:
sys.stdout.write("<<< " + data.decode("utf8", "replace"))
sys.stdout.write(f"<<< {data.decode('utf8', 'replace')}")
message = self._parse_message(data)
self._handle_message(message)
@ -298,7 +298,7 @@ class MPVBase:
self._event_queue.put(message)
else:
raise MPVCommunicationError("invalid message %r" % message)
raise MPVCommunicationError(f"invalid message {message!r}")
def _send_message(self, message, timeout=None):
"""Send a message/command to the mpv process, message must be a
@ -308,7 +308,7 @@ class MPVBase:
data = self._compose_message(message)
if self.debug:
sys.stdout.write(">>> " + data.decode("utf8", "replace"))
sys.stdout.write(f">>> {data.decode('utf8', 'replace')}")
# Request/response cycles are coordinated across different threads, so
# that they don't get mixed up. This makes it possible to use commands
@ -371,7 +371,7 @@ class MPVBase:
self._send_message(message, timeout)
return self._get_response(timeout)
except MPVCommandError as e:
raise MPVCommandError("%r: %s" % (message["command"], e))
raise MPVCommandError(f"{message['command']!r}: {e}")
except Exception as e:
if _retry:
print("mpv timed out, restarting")
@ -512,7 +512,7 @@ class MPV(MPVBase):
return
if message["event"] == "property-change":
name = "property-" + message["name"]
name = f"property-{message['name']}"
else:
name = message["event"]
@ -527,7 +527,7 @@ class MPV(MPVBase):
try:
self.command("enable_event", name)
except MPVCommandError:
raise MPVError("no such event %r" % name)
raise MPVError(f"no such event {name!r}")
self._callbacks.setdefault(name, []).append(callback)
@ -538,12 +538,12 @@ class MPV(MPVBase):
try:
callbacks = self._callbacks[name]
except KeyError:
raise MPVError("no callbacks registered for event %r" % name)
raise MPVError(f"no callbacks registered for event {name!r}")
try:
callbacks.remove(callback)
except ValueError:
raise MPVError("callback %r not registered for event %r" % (callback, name))
raise MPVError(f"callback {callback!r} not registered for event {name!r}")
def register_property_callback(self, name, callback):
"""Register a function `callback` for the property-change event on
@ -556,9 +556,9 @@ class MPV(MPVBase):
# Apparently observe_property does not check it :-(
proplist = self.command("get_property", "property-list")
if name not in proplist:
raise MPVError("no such property %r" % name)
raise MPVError(f"no such property {name!r}")
self._callbacks.setdefault("property-" + name, []).append(callback)
self._callbacks.setdefault(f"property-{name}", []).append(callback)
# 'observe_property' expects some kind of id which can be used later
# for unregistering with 'unobserve_property'.
@ -572,15 +572,15 @@ class MPV(MPVBase):
property-change event on property `name`.
"""
try:
callbacks = self._callbacks["property-" + name]
callbacks = self._callbacks[f"property-{name}"]
except KeyError:
raise MPVError("no callbacks registered for property %r" % name)
raise MPVError(f"no callbacks registered for property {name!r}")
try:
callbacks.remove(callback)
except ValueError:
raise MPVError(
"callback %r not registered for property %r" % (callback, name)
f"callback {callback!r} not registered for property {name!r}"
)
serial = self._property_serials.pop((name, callback))

View file

@ -80,7 +80,7 @@ class Overview:
elif url == "decks":
self.mw.moveToState("deckBrowser")
elif url == "review":
openLink(aqt.appShared + "info/%s?v=%s" % (self.sid, self.sidVer))
openLink(f"{aqt.appShared}info/{self.sid}?v={self.sidVer}")
elif url == "studymore" or url == "customStudy":
self.onStudyMore()
elif url == "unbury":
@ -180,8 +180,8 @@ class Overview:
def _desc(self, deck: Dict[str, Any]) -> str:
if deck["dyn"]:
desc = tr(TR.STUDYING_THIS_IS_A_SPECIAL_DECK_FOR)
desc += " " + tr(TR.STUDYING_CARDS_WILL_BE_AUTOMATICALLY_RETURNED_TO)
desc += " " + tr(TR.STUDYING_DELETING_THIS_DECK_FROM_THE_DECK)
desc += f" {tr(TR.STUDYING_CARDS_WILL_BE_AUTOMATICALLY_RETURNED_TO)}"
desc += f" {tr(TR.STUDYING_DELETING_THIS_DECK_FROM_THE_DECK)}"
else:
desc = deck.get("desc", "")
if deck.get("md", False):
@ -192,7 +192,7 @@ class Overview:
dyn = "dyn"
else:
dyn = ""
return '<div class="descfont descmid description %s">%s</div>' % (dyn, desc)
return f'<div class="descfont descmid description {dyn}">{desc}</div>'
def _table(self) -> Optional[str]:
counts = list(self.mw.col.sched.counts())

View file

@ -214,9 +214,9 @@ class Previewer(QDialog):
av_player.clear_queue_and_maybe_interrupt()
txt = self.mw.prepare_card_text_for_display(txt)
txt = gui_hooks.card_will_show(txt, c, "preview" + self._state.capitalize())
txt = gui_hooks.card_will_show(txt, c, f"preview{self._state.capitalize()}")
self._last_state = self._state_and_mod()
self._web.eval("{}({},'{}');".format(func, json.dumps(txt), bodyclass))
self._web.eval(f"{func}({json.dumps(txt)},'{bodyclass}');")
self._card_changed = False
def _on_show_both_sides(self, toggle: bool) -> None:

View file

@ -522,7 +522,7 @@ create table if not exists profiles
without_unicode_isolation(
tr(
TR.PROFILES_FOLDER_README,
link=appHelpSite + "files?id=startup-options",
link=f"{appHelpSite}files?id=startup-options",
)
)
)

View file

@ -45,7 +45,7 @@ class ProgressManager:
def handler() -> None:
if requiresCollection and not self.mw.col:
# no current collection; timer is no longer valid
print("Ignored progress func as collection unloaded: %s" % repr(func))
print(f"Ignored progress func as collection unloaded: {repr(func)}")
return
if not self._levels:

View file

@ -97,9 +97,7 @@ class Reviewer:
mins = int(round(elapsed[0] / 60))
part2 = tr(TR.STUDYING_MINUTE, count=mins)
fin = tr(TR.STUDYING_FINISH)
diag = askUserDialog(
"%s %s" % (part1, part2), [tr(TR.STUDYING_CONTINUE), fin]
)
diag = askUserDialog(f"{part1} {part2}", [tr(TR.STUDYING_CONTINUE), fin])
diag.setIcon(QMessageBox.Information)
if diag.run() == fin:
return self.mw.moveToState("deckBrowser")
@ -142,15 +140,13 @@ class Reviewer:
fade = ""
if self.mw.pm.video_driver() == VideoDriver.Software:
fade = "<script>qFade=0;</script>"
return """
return f"""
<div id=_mark>&#x2605;</div>
<div id=_flag>&#x2691;</div>
{}
{fade}
<div id=qa></div>
{}
""".format(
fade, extra
)
{extra}
"""
def _initWeb(self) -> None:
self._reps = 0
@ -207,7 +203,7 @@ class Reviewer:
bodyclass = theme_manager.body_classes_for_card_ord(c.ord)
self.web.eval("_showQuestion(%s,'%s');" % (json.dumps(q), bodyclass))
self.web.eval(f"_showQuestion({json.dumps(q)},'{bodyclass}');")
self._drawFlag()
self._drawMark()
self._showAnswerButton()
@ -220,10 +216,10 @@ class Reviewer:
return card.autoplay()
def _drawFlag(self) -> None:
self.web.eval("_drawFlag(%s);" % self.card.userFlag())
self.web.eval(f"_drawFlag({self.card.userFlag()});")
def _drawMark(self) -> None:
self.web.eval("_drawMark(%s);" % json.dumps(self.card.note().hasTag("marked")))
self.web.eval(f"_drawMark({json.dumps(self.card.note().hasTag('marked'))});")
# Showing the answer
##########################################################################
@ -248,7 +244,7 @@ class Reviewer:
a = self._mungeQA(a)
a = gui_hooks.card_will_show(a, c, "reviewAnswer")
# render and update bottom
self.web.eval("_showAnswer(%s);" % json.dumps(a))
self.web.eval(f"_showAnswer({json.dumps(a)});")
self._showEaseButtons()
self.mw.web.setFocus()
# user hook
@ -399,13 +395,12 @@ class Reviewer:
return re.sub(self.typeAnsPat, "", buf)
return re.sub(
self.typeAnsPat,
"""
f"""
<center>
<input type=text id=typeans onkeypress="_typeAnsPress();"
style="font-family: '%s'; font-size: %spx;">
style="font-family: '{self.typeFont}'; font-size: {self.typeSize}px;">
</center>
"""
% (self.typeFont, self.typeSize),
""",
buf,
)
@ -440,7 +435,7 @@ class Reviewer:
if hadHR:
# a hack to ensure the q/a separator falls before the answer
# comparison when user is using {{FrontSide}}
s = "<hr id=answer>" + s
s = f"<hr id=answer>{s}"
return s
return re.sub(self.typeAnsPat, repl, buf)
@ -506,13 +501,13 @@ class Reviewer:
givenElems, correctElems = self.tokenizeComparison(given, correct)
def good(s: str) -> str:
return "<span class=typeGood>" + html.escape(s) + "</span>"
return f"<span class=typeGood>{html.escape(s)}</span>"
def bad(s: str) -> str:
return "<span class=typeBad>" + html.escape(s) + "</span>"
return f"<span class=typeBad>{html.escape(s)}</span>"
def missed(s: str) -> str:
return "<span class=typeMissed>" + html.escape(s) + "</span>"
return f"<span class=typeMissed>{html.escape(s)}</span>"
if given == correct:
res = good(given)
@ -531,14 +526,14 @@ class Reviewer:
res += good(txt)
else:
res += missed(txt)
res = "<div><code id=typeans>" + res + "</code></div>"
res = f"<div><code id=typeans>{res}</code></div>"
return res
def _noLoneMarks(self, s: str) -> str:
# ensure a combining character at the start does not join to
# previous text
if s and ucd.category(s[0]).startswith("M"):
return "\xa0" + s
return f"\xa0{s}"
return s
def _getTypedAnswer(self) -> None:
@ -602,7 +597,7 @@ time = %(time)d;
def _showEaseButtons(self) -> None:
middle = self._answerButtons()
self.bottom.web.eval("showAnswer(%s);" % json.dumps(middle))
self.bottom.web.eval(f"showAnswer({json.dumps(middle)});")
def _remaining(self) -> str:
if not self.mw.col.conf["dueCounts"]:
@ -613,11 +608,11 @@ time = %(time)d;
else:
counts = list(self.mw.col.sched.counts(self.card))
idx = self.mw.col.sched.countIdx(self.card)
counts[idx] = "<u>%s</u>" % (counts[idx])
counts[idx] = f"<u>{counts[idx]}</u>"
space = " + "
ctxt = "<span class=new-count>%s</span>" % counts[0]
ctxt += space + "<span class=learn-count>%s</span>" % counts[1]
ctxt += space + "<span class=review-count>%s</span>" % counts[2]
ctxt = f"<span class=new-count>{counts[0]}</span>"
ctxt += f"{space}<span class=learn-count>{counts[1]}</span>"
ctxt += f"{space}<span class=review-count>{counts[2]}</span>"
return ctxt
def _defaultEase(self) -> int:
@ -683,7 +678,7 @@ time = %(time)d;
if not self.mw.col.conf["estTimes"]:
return "<div class=spacer></div>"
txt = self.mw.col.sched.nextIvlStr(self.card, i, True) or "&nbsp;"
return "<span class=nobold>%s</span><br>" % txt
return f"<span class=nobold>{txt}</span><br>"
# Leeches
##########################################################################
@ -692,7 +687,7 @@ time = %(time)d;
# for now
s = tr(TR.STUDYING_CARD_WAS_A_LEECH)
if card.queue < 0:
s += " " + tr(TR.STUDYING_IT_HAS_BEEN_SUSPENDED)
s += f" {tr(TR.STUDYING_IT_HAS_BEEN_SUSPENDED)}"
tooltip(s)
# Context menu

View file

@ -805,7 +805,7 @@ class SidebarTreeView(QTreeView):
full_name=head + node.name,
)
root.add_child(item)
newhead = head + node.name + "::"
newhead = f"{head + node.name}::"
render(item, node.children, newhead)
tree = self.col.tags.tree()
@ -854,7 +854,7 @@ class SidebarTreeView(QTreeView):
full_name=head + node.name,
)
root.add_child(item)
newhead = head + node.name + "::"
newhead = f"{head + node.name}::"
render(item, node.children, newhead)
tree = self.col.decks.deck_tree()
@ -906,7 +906,7 @@ class SidebarTreeView(QTreeView):
SearchTerm(note=nt["name"]), SearchTerm(template=c)
),
item_type=SidebarItemType.NOTETYPE_TEMPLATE,
full_name=nt["name"] + "::" + tmpl["name"],
full_name=f"{nt['name']}::{tmpl['name']}",
)
item.add_child(child)

View file

@ -240,7 +240,7 @@ def _packagedCmd(cmd: List[str]) -> Tuple[Any, Dict[str, str]]:
del env["LD_LIBRARY_PATH"]
if isMac:
dir = os.path.dirname(os.path.abspath(__file__))
exeDir = os.path.abspath(dir + "/../../Resources/audio")
exeDir = os.path.abspath(f"{dir}/../../Resources/audio")
else:
exeDir = os.path.dirname(os.path.abspath(sys.argv[0]))
if isWin and not cmd[0].endswith(".exe"):
@ -353,7 +353,7 @@ class SimpleMpvPlayer(SimpleProcessPlayer, VideoPlayer):
def __init__(self, taskman: TaskManager, base_folder: str) -> None:
super().__init__(taskman)
self.args += ["--config-dir=" + base_folder]
self.args += [f"--config-dir={base_folder}"]
class SimpleMplayerPlayer(SimpleProcessPlayer, SoundOrVideoPlayer):
@ -377,7 +377,7 @@ class MpvManager(MPV, SoundOrVideoPlayer):
mpvPath, self.popenEnv = _packagedCmd(["mpv"])
self.executable = mpvPath[0]
self._on_done: Optional[OnDoneCallback] = None
self.default_argv += ["--config-dir=" + base_path]
self.default_argv += [f"--config-dir={base_path}"]
super().__init__(window_id=None, debug=False)
def on_init(self) -> None:
@ -764,7 +764,7 @@ class RecordDialog(QDialog):
def _on_timer(self) -> None:
self._recorder.on_timer()
duration = self._recorder.duration()
self.label.setText(tr(TR.MEDIA_RECORDINGTIME, secs="%0.1f" % duration))
self.label.setText(tr(TR.MEDIA_RECORDINGTIME, secs=f"{duration:0.1f}"))
def accept(self) -> None:
self._timer.stop()

View file

@ -66,7 +66,7 @@ class NewDeckStats(QDialog):
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = "anki-" + tr(TR.STATISTICS_STATS) + name
name = f"anki-{tr(TR.STATISTICS_STATS)}{name}"
file = getSaveFile(
self,
title=tr(TR.STATISTICS_SAVE_PDF),
@ -156,7 +156,7 @@ class DeckStats(QDialog):
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = "anki-" + tr(TR.STATISTICS_STATS) + name
name = f"anki-{tr(TR.STATISTICS_STATS)}{name}"
file = getSaveFile(
self,
title=tr(TR.STATISTICS_SAVE_PDF),
@ -189,7 +189,7 @@ class DeckStats(QDialog):
self.report = stats.report(type=self.period)
self.form.web.title = "deck stats"
self.form.web.stdHtml(
"<html><body>" + self.report + "</body></html>",
f"<html><body>{self.report}</body></html>",
js=["js/vendor/jquery.min.js", "js/vendor/plot.js"],
context=self,
)

View file

@ -45,7 +45,7 @@ class StudyDeck(QDialog):
self.form.filter.installEventFilter(self)
self.cancel = cancel
gui_hooks.state_did_reset.append(self.onReset)
self.geomKey = "studyDeck-" + geomKey
self.geomKey = f"studyDeck-{geomKey}"
restoreGeom(self, self.geomKey)
disable_help_button(self)
if not cancel:

View file

@ -137,4 +137,4 @@ class TagCompleter(QCompleter):
self.tags.remove("")
except ValueError:
pass
return " ".join(self.tags) + " "
return f"{' '.join(self.tags)} "

View file

@ -98,12 +98,12 @@ class TagLimit(QDialog):
if yes:
arr = []
for req in yes:
arr.append('tag:"%s"' % req)
self.tags += "(" + " or ".join(arr) + ")"
arr.append(f'tag:"{req}"')
self.tags += f"({' or '.join(arr)})"
if no:
arr = []
for req in no:
arr.append('-tag:"%s"' % req)
self.tags += " " + " ".join(arr)
arr.append(f'-tag:"{req}"')
self.tags += f" {' '.join(arr)}"
saveGeom(self, "tagLimit")
QDialog.accept(self)

View file

@ -141,7 +141,7 @@ def on_tts_voices(
f"{{{{tts {v.lang} voices={v.name}}}}}" # pylint: disable=no-member
for v in voices
)
return buf + "</div>"
return f"{buf}</div>"
hooks.field_filter.append(on_tts_voices)
@ -199,7 +199,7 @@ class MacTTSPlayer(TTSProcessPlayer):
return None
original_name = m.group(1).strip()
tidy_name = "Apple_" + original_name.replace(" ", "_")
tidy_name = f"Apple_{original_name.replace(' ', '_')}"
return MacVoice(name=tidy_name, original_name=original_name, lang=m.group(2))

View file

@ -479,7 +479,7 @@ def getFile(
"Ask the user for a file."
assert not dir or not key
if not dir:
dirkey = key + "Directory"
dirkey = f"{key}Directory"
dir = aqt.mw.pm.profile.get(dirkey, "")
else:
dirkey = None
@ -521,7 +521,7 @@ def getSaveFile(
) -> str:
"""Ask the user for a file to save. Use DIR_DESCRIPTION as config
variable. The file dialog will default to open with FNAME."""
config_key = dir_description + "Directory"
config_key = f"{dir_description}Directory"
defaultPath = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
base = aqt.mw.pm.profile.get(config_key, defaultPath)
@ -644,8 +644,8 @@ def restore_is_checked(widget: QWidget, key: str) -> None:
def save_combo_index_for_session(widget: QComboBox, key: str) -> None:
textKey = key + "ComboActiveText"
indexKey = key + "ComboActiveIndex"
textKey = f"{key}ComboActiveText"
indexKey = f"{key}ComboActiveIndex"
aqt.mw.pm.session[textKey] = widget.currentText()
aqt.mw.pm.session[indexKey] = widget.currentIndex()
@ -653,8 +653,8 @@ def save_combo_index_for_session(widget: QComboBox, key: str) -> None:
def restore_combo_index_for_session(
widget: QComboBox, history: List[str], key: str
) -> None:
textKey = key + "ComboActiveText"
indexKey = key + "ComboActiveIndex"
textKey = f"{key}ComboActiveText"
indexKey = f"{key}ComboActiveIndex"
text = aqt.mw.pm.session.get(textKey)
index = aqt.mw.pm.session.get(indexKey)
if text is not None and index is not None:
@ -696,10 +696,10 @@ def mungeQA(col: Collection, txt: str) -> str:
def openFolder(path: str) -> None:
if isWin:
subprocess.Popen(["explorer", "file://" + path])
subprocess.Popen(["explorer", f"file://{path}"])
else:
with noBundledLibs():
QDesktopServices.openUrl(QUrl("file://" + path))
QDesktopServices.openUrl(QUrl(f"file://{path}"))
def shortcut(key: str) -> str:
@ -755,13 +755,11 @@ def tooltip(
closeTooltip()
aw = parent or aqt.mw.app.activeWindow() or aqt.mw
lab = CustomLabel(
"""\
<table cellpadding=10>
f"""<table cellpadding=10>
<tr>
<td>%s</td>
<td>{msg}</td>
</tr>
</table>"""
% msg,
</table>""",
aw,
)
lab.setFrameStyle(QFrame.Panel)
@ -886,9 +884,9 @@ def supportText() -> str:
from aqt import mw
if isWin:
platname = "Windows " + platform.win32_ver()[0]
platname = f"Windows {platform.win32_ver()[0]}"
elif isMac:
platname = "Mac " + platform.mac_ver()[0]
platname = f"Mac {platform.mac_ver()[0]}"
else:
platname = "Linux"

View file

@ -97,7 +97,7 @@ class AnkiWebPage(QWebEnginePage):
else:
level_str = str(level)
buf = "JS %(t)s %(f)s:%(a)d %(b)s" % dict(
t=level_str, a=line, f=srcID, b=msg + "\n"
t=level_str, a=line, f=srcID, b=f"{msg}\n"
)
if "MathJax localStorage" in buf:
# silence localStorage noise
@ -392,10 +392,10 @@ class AnkiWebView(QWebEngineView):
family = tr(TR.QT_MISC_SEGOE_UI)
button_style = "button { font-family:%s; }" % family
button_style += "\n:focus { outline: 1px solid %s; }" % color_hl
font = "font-size:12px;font-family:%s;" % family
font = f"font-size:12px;font-family:{family};"
elif isMac:
family = "Helvetica"
font = 'font-size:15px;font-family:"%s";' % family
font = f'font-size:15px;font-family:"{family}";'
button_style = """
button { -webkit-appearance: none; background: #fff; border: 1px solid #ccc;
border-radius:5px; font-family: Helvetica }"""
@ -403,7 +403,7 @@ border-radius:5px; font-family: Helvetica }"""
family = self.font().family()
color_hl_txt = palette.color(QPalette.HighlightedText).name()
color_btn = palette.color(QPalette.Button).name()
font = 'font-size:14px;font-family:"%s";' % family
font = f'font-size:14px;font-family:"{family}";'
button_style = """
/* Buttons */
button{
@ -503,7 +503,7 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
return f"http://127.0.0.1:{mw.mediaServer.getPort()}{subpath}{path}"
def bundledScript(self, fname: str) -> str:
return '<script src="%s"></script>' % self.webBundlePath(fname)
return f'<script src="{self.webBundlePath(fname)}"></script>'
def bundledCSS(self, fname: str) -> str:
return '<link rel="stylesheet" type="text/css" href="%s">' % self.webBundlePath(
@ -641,5 +641,5 @@ document.head.appendChild(style);
else:
extra = ""
self.hide_while_preserving_layout()
self.load(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html" + extra))
self.load(QUrl(f"{mw.serverURL()}_anki/pages/{name}.html{extra}"))
self.inject_dynamic_style_and_show()

View file

@ -86,7 +86,7 @@ def _err_unless_zero(result):
if result == 0:
return result
else:
raise WinPathsException("Failed to retrieve windows path: %s" % result)
raise WinPathsException(f"Failed to retrieve windows path: {result}")
_SHGetFolderPath = windll.shell32.SHGetFolderPathW