update platform checks (eg isWin -> is_win) + devMode

This commit is contained in:
Damien Elmes 2021-11-25 09:06:16 +10:00
parent 9d444b40e0
commit ebad6ad379
24 changed files with 121 additions and 120 deletions

View file

@ -13,7 +13,7 @@ import anki
from anki import card_rendering_pb2, hooks from anki import card_rendering_pb2, hooks
from anki.models import NotetypeDict from anki.models import NotetypeDict
from anki.template import TemplateRenderContext, TemplateRenderOutput from anki.template import TemplateRenderContext, TemplateRenderOutput
from anki.utils import call, isMac, namedtmp, tmpdir from anki.utils import call, is_mac, namedtmp, tmpdir
pngCommands = [ pngCommands = [
["latex", "-interaction=nonstopmode", "tmp.tex"], ["latex", "-interaction=nonstopmode", "tmp.tex"],
@ -29,7 +29,7 @@ svgCommands = [
build = True # pylint: disable=invalid-name build = True # pylint: disable=invalid-name
# add standard tex install location to osx # add standard tex install location to osx
if isMac: if is_mac:
os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin" os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin"

View file

@ -212,7 +212,7 @@ def no_bundled_libs() -> Iterator[None]:
def call(argv: list[str], wait: bool = True, **kwargs: Any) -> int: def call(argv: list[str], wait: bool = True, **kwargs: Any) -> int:
"Execute a command. If WAIT, return exit code." "Execute a command. If WAIT, return exit code."
# ensure we don't open a separate window for forking process on windows # ensure we don't open a separate window for forking process on windows
if isWin: if is_win:
info = subprocess.STARTUPINFO() # type: ignore info = subprocess.STARTUPINFO() # type: ignore
try: try:
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore info.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore
@ -245,10 +245,11 @@ def call(argv: list[str], wait: bool = True, **kwargs: Any) -> int:
# OS helpers # OS helpers
############################################################################## ##############################################################################
isMac = sys.platform.startswith("darwin") is_mac = sys.platform.startswith("darwin")
isWin = sys.platform.startswith("win32") is_win = sys.platform.startswith("win32")
isLin = not isMac and not isWin # also covers *BSD
devMode = os.getenv("ANKIDEV", "") is_lin = not is_mac and not is_win
dev_mode = os.getenv("ANKIDEV", "")
INVALID_FILENAME_CHARS = ':*?"<>|' INVALID_FILENAME_CHARS = ':*?"<>|'
@ -257,9 +258,9 @@ def invalid_filename(str: str, dirsep: bool = True) -> str | None:
for char in INVALID_FILENAME_CHARS: for char in INVALID_FILENAME_CHARS:
if char in str: if char in str:
return char return char
if (dirsep or isWin) and "/" in str: if (dirsep or is_win) and "/" in str:
return "/" return "/"
elif (dirsep or not isWin) and "\\" in str: elif (dirsep or not is_win) and "\\" in str:
return "\\" return "\\"
elif str.strip().startswith("."): elif str.strip().startswith("."):
return "." return "."
@ -272,9 +273,9 @@ def plat_desc() -> str:
for _ in range(100): for _ in range(100):
try: try:
system = platform.system() system = platform.system()
if isMac: if is_mac:
theos = f"mac:{platform.mac_ver()[0]}" theos = f"mac:{platform.mac_ver()[0]}"
elif isWin: elif is_win:
theos = f"win:{platform.win32_ver()[0]}" theos = f"win:{platform.win32_ver()[0]}"
elif system == "Linux": elif system == "Linux":
import distro # pytype: disable=import-error # pylint: disable=import-error import distro # pytype: disable=import-error # pylint: disable=import-error

View file

@ -10,7 +10,7 @@ from anki.collection import Collection as aopen
from anki.dbproxy import emulate_named_args from anki.dbproxy import emulate_named_args
from anki.lang import TR, without_unicode_isolation from anki.lang import TR, without_unicode_isolation
from anki.stdmodels import _legacy_add_basic_model, get_stock_notetypes from anki.stdmodels import _legacy_add_basic_model, get_stock_notetypes
from anki.utils import isWin from anki.utils import is_win
from tests.shared import assertException, getEmptyCol from tests.shared import assertException, getEmptyCol
@ -34,7 +34,7 @@ def test_create_open():
col.close() col.close()
# non-writeable dir # non-writeable dir
if isWin: if is_win:
dir = "c:\root.anki2" dir = "c:\root.anki2"
else: else:
dir = "/attachroot.anki2" dir = "/attachroot.anki2"

View file

@ -6,7 +6,7 @@ import time
from anki.consts import MODEL_CLOZE from anki.consts import MODEL_CLOZE
from anki.errors import NotFoundError from anki.errors import NotFoundError
from anki.utils import isWin, strip_html from anki.utils import is_win, strip_html
from tests.shared import getEmptyCol from tests.shared import getEmptyCol
@ -329,7 +329,7 @@ def test_modelChange():
assert note.cards()[0].id == c1.id assert note.cards()[0].id == c1.id
# delete first card # delete first card
map = {0: None, 1: 1} map = {0: None, 1: 1}
if isWin: if is_win:
# The low precision timer on Windows reveals a race condition # The low precision timer on Windows reveals a race condition
time.sleep(0.05) time.sleep(0.05)
col.models.change(basic, [note.id], basic, noop, map) col.models.change(basic, [note.id], basic, noop, map)

View file

@ -33,7 +33,7 @@ from anki._backend import RustBackend
from anki.buildinfo import version as _version from anki.buildinfo import version as _version
from anki.collection import Collection from anki.collection import Collection
from anki.consts import HELP_SITE from anki.consts import HELP_SITE
from anki.utils import checksum, isLin, isMac from anki.utils import checksum, is_lin, is_mac
from aqt import gui_hooks from aqt import gui_hooks
from aqt.qt import * from aqt.qt import *
from aqt.utils import TR, tr from aqt.utils import TR, tr
@ -238,7 +238,7 @@ def setupLangAndBackend(
# load qt translations # load qt translations
_qtrans = QTranslator() _qtrans = QTranslator()
if isMac and getattr(sys, "frozen", False): if is_mac and getattr(sys, "frozen", False):
qt_dir = os.path.join(sys.prefix, "../Resources/qt_translations") qt_dir = os.path.join(sys.prefix, "../Resources/qt_translations")
else: else:
if qtmajor == 5: if qtmajor == 5:
@ -331,7 +331,7 @@ def parseArgs(argv: list[str]) -> tuple[argparse.Namespace, list[str]]:
"Returns (opts, args)." "Returns (opts, args)."
# py2app fails to strip this in some instances, then anki dies # py2app fails to strip this in some instances, then anki dies
# as there's no such profile # as there's no such profile
if isMac and len(argv) > 1 and argv[1].startswith("-psn"): if is_mac and len(argv) > 1 and argv[1].startswith("-psn"):
argv = [argv[0]] argv = [argv[0]]
parser = argparse.ArgumentParser(description=f"Anki {appVersion}") parser = argparse.ArgumentParser(description=f"Anki {appVersion}")
parser.usage = "%(prog)s [OPTIONS] [file to import/add-on to install]" parser.usage = "%(prog)s [OPTIONS] [file to import/add-on to install]"
@ -353,13 +353,13 @@ def parseArgs(argv: list[str]) -> tuple[argparse.Namespace, list[str]]:
def setupGL(pm: aqt.profiles.ProfileManager) -> None: def setupGL(pm: aqt.profiles.ProfileManager) -> None:
if isMac: if is_mac:
return return
driver = pm.video_driver() driver = pm.video_driver()
# work around pyqt loading wrong GL library # work around pyqt loading wrong GL library
if isLin: if is_lin:
import ctypes import ctypes
ctypes.CDLL("libGL.so.1", ctypes.RTLD_GLOBAL) ctypes.CDLL("libGL.so.1", ctypes.RTLD_GLOBAL)
@ -412,11 +412,11 @@ def setupGL(pm: aqt.profiles.ProfileManager) -> None:
if driver == VideoDriver.OpenGL: if driver == VideoDriver.OpenGL:
pass pass
else: else:
if isWin: if is_win:
os.environ["QT_OPENGL"] = driver.value os.environ["QT_OPENGL"] = driver.value
elif isMac: elif is_mac:
QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL) QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL)
elif isLin: elif is_lin:
os.environ["QT_XCB_FORCE_SOFTWARE_OPENGL"] = "1" os.environ["QT_XCB_FORCE_SOFTWARE_OPENGL"] = "1"
@ -521,7 +521,7 @@ def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiAp
QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL) QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL)
if ( if (
isWin is_win
and qtmajor == 5 and qtmajor == 5
and (qtminor == 14 or (qtminor == 15 and qtpoint == 0)) and (qtminor == 14 or (qtminor == 15 and qtpoint == 0))
and "QT_QPA_PLATFORM" not in os.environ and "QT_QPA_PLATFORM" not in os.environ
@ -545,11 +545,11 @@ def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiAp
return None return None
# disable icons on mac; this must be done before window created # disable icons on mac; this must be done before window created
if isMac: if is_mac:
app.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus) app.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus)
# disable help button in title bar on qt versions that support it # disable help button in title bar on qt versions that support it
if isWin and qtmajor == 5 and qtminor >= 10: if is_win and qtmajor == 5 and qtminor >= 10:
QApplication.setAttribute(Qt.AA_DisableWindowContextHelpButton) # type: ignore QApplication.setAttribute(Qt.AA_DisableWindowContextHelpButton) # type: ignore
# proxy configured? # proxy configured?
@ -606,7 +606,7 @@ def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiAp
backend = setupLangAndBackend(pm, app, opts.lang, pmLoadResult.firstTime) backend = setupLangAndBackend(pm, app, opts.lang, pmLoadResult.firstTime)
driver = pm.video_driver() driver = pm.video_driver()
if isLin and driver == VideoDriver.OpenGL: if is_lin and driver == VideoDriver.OpenGL:
from aqt.utils import gfxDriverIsBroken from aqt.utils import gfxDriverIsBroken
if gfxDriverIsBroken(): if gfxDriverIsBroken():

View file

@ -10,7 +10,7 @@ from anki.collection import OpChanges, SearchNode
from anki.decks import DeckId from anki.decks import DeckId
from anki.models import NotetypeId from anki.models import NotetypeId
from anki.notes import Note, NoteFieldsCheckResult, NoteId from anki.notes import Note, NoteFieldsCheckResult, NoteId
from anki.utils import html_to_text_line, isMac from anki.utils import html_to_text_line, is_mac
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.deckchooser import DeckChooser from aqt.deckchooser import DeckChooser
from aqt.notetypechooser import NotetypeChooser from aqt.notetypechooser import NotetypeChooser
@ -101,7 +101,7 @@ class AddCards(QMainWindow):
bb.addButton(self.helpButton, QDialogButtonBox.ButtonRole.HelpRole) bb.addButton(self.helpButton, QDialogButtonBox.ButtonRole.HelpRole)
# history # history
b = bb.addButton(f"{tr.adding_history()} {downArrow()}", ar) b = bb.addButton(f"{tr.adding_history()} {downArrow()}", ar)
if isMac: if is_mac:
sc = "Ctrl+Shift+H" sc = "Ctrl+Shift+H"
else: else:
sc = "Ctrl+H" sc = "Ctrl+H"

View file

@ -31,7 +31,7 @@ from aqt.utils import (
askUser, askUser,
disable_help_button, disable_help_button,
getFile, getFile,
isWin, is_win,
openFolder, openFolder,
openLink, openLink,
restoreGeom, restoreGeom,
@ -1528,7 +1528,7 @@ class ConfigEditor(QDialog):
) )
text = gui_hooks.addon_config_editor_will_display_json(text) text = gui_hooks.addon_config_editor_will_display_json(text)
self.form.editor.setPlainText(text) self.form.editor.setPlainText(text)
if isMac: if is_mac:
self.form.editor.repaint() self.form.editor.repaint()
def onClose(self) -> None: def onClose(self) -> None:

View file

@ -15,7 +15,7 @@ from anki.errors import NotFoundError
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.notes import NoteId from anki.notes import NoteId
from anki.tags import MARKED_TAG from anki.tags import MARKED_TAG
from anki.utils import isMac from anki.utils import is_mac
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.editor import Editor from aqt.editor import Editor
from aqt.exporting import ExportDialog from aqt.exporting import ExportDialog
@ -177,7 +177,7 @@ class Browser(QMainWindow):
qconnect(f.actionRedo.triggered, self.redo) qconnect(f.actionRedo.triggered, self.redo)
qconnect(f.actionInvertSelection.triggered, self.table.invert_selection) qconnect(f.actionInvertSelection.triggered, self.table.invert_selection)
qconnect(f.actionSelectNotes.triggered, self.selectNotes) qconnect(f.actionSelectNotes.triggered, self.selectNotes)
if not isMac: if not is_mac:
f.actionClose.setVisible(False) f.actionClose.setVisible(False)
qconnect(f.actionCreateFilteredDeck.triggered, self.createFilteredDeck) qconnect(f.actionCreateFilteredDeck.triggered, self.createFilteredDeck)
f.actionCreateFilteredDeck.setShortcuts(["Ctrl+G", "Ctrl+Alt+G"]) f.actionCreateFilteredDeck.setShortcuts(["Ctrl+G", "Ctrl+Alt+G"])

View file

@ -1,11 +1,11 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from anki.utils import isMac from anki.utils import is_mac
from aqt.theme import theme_manager from aqt.theme import theme_manager
def _want_right_border() -> bool: def _want_right_border() -> bool:
return not isMac or theme_manager.night_mode return not is_mac or theme_manager.night_mode
from .item import SidebarItem, SidebarItemType from .item import SidebarItem, SidebarItemType

View file

@ -10,7 +10,7 @@ from anki.cards import Card, CardId
from anki.collection import Collection, Config, OpChanges from anki.collection import Collection, Config, OpChanges
from anki.consts import * from anki.consts import *
from anki.notes import Note, NoteId from anki.notes import Note, NoteId
from anki.utils import isWin from anki.utils import is_win
from aqt import colors, gui_hooks from aqt import colors, gui_hooks
from aqt.browser.table import Columns, ItemId, SearchContext from aqt.browser.table import Columns, ItemId, SearchContext
from aqt.browser.table.model import DataModel from aqt.browser.table.model import DataModel
@ -375,7 +375,7 @@ class Table:
def _setup_headers(self) -> None: def _setup_headers(self) -> None:
vh = self._view.verticalHeader() vh = self._view.verticalHeader()
hh = self._view.horizontalHeader() hh = self._view.horizontalHeader()
if not isWin: if not is_win:
vh.hide() vh.hide()
hh.show() hh.show()
hh.setHighlightSections(False) hh.setHighlightSections(False)

View file

@ -29,7 +29,7 @@ from anki.consts import MODEL_CLOZE
from anki.hooks import runFilter from anki.hooks import runFilter
from anki.httpclient import HttpClient from anki.httpclient import HttpClient
from anki.notes import Note, NoteFieldsCheckResult from anki.notes import Note, NoteFieldsCheckResult
from anki.utils import checksum, isLin, isWin, namedtmp from anki.utils import checksum, is_lin, is_win, namedtmp
from aqt import AnkiQt, colors, gui_hooks from aqt import AnkiQt, colors, gui_hooks
from aqt.operations import QueryOp from aqt.operations import QueryOp
from aqt.operations.note import update_note from aqt.operations.note import update_note
@ -331,7 +331,7 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
parent=self.parentWindow, parent=self.parentWindow,
fill_empty=False, fill_empty=False,
) )
if isWin: if is_win:
self.parentWindow.activateWindow() self.parentWindow.activateWindow()
# JS->Python bridge # JS->Python bridge
@ -988,7 +988,7 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{
# choose new colour # choose new colour
@deprecated(info=_js_legacy) @deprecated(info=_js_legacy)
def onChangeCol(self) -> None: def onChangeCol(self) -> None:
if isLin: if is_lin:
new = QColorDialog.getColor( new = QColorDialog.getColor(
QColor(self.fcolour), QColor(self.fcolour),
None, None,
@ -1277,7 +1277,7 @@ class EditorWebView(AnkiWebView):
# add a comment in the clipboard html so we can tell text is copied # add a comment in the clipboard html so we can tell text is copied
# from us and doesn't need to be stripped # from us and doesn't need to be stripped
clip = self.editor.mw.app.clipboard() clip = self.editor.mw.app.clipboard()
if not isMac and not clip.ownsClipboard(): if not is_mac and not clip.ownsClipboard():
return return
mime = clip.mimeData() mime = clip.mimeData()
if not mime.hasHtml(): if not mime.hasHtml():

View file

@ -31,7 +31,7 @@ from anki.decks import DeckDict, DeckId
from anki.hooks import runHook from anki.hooks import runHook
from anki.notes import NoteId from anki.notes import NoteId
from anki.sound import AVTag, SoundOrVideoTag from anki.sound import AVTag, SoundOrVideoTag
from anki.utils import devMode, ids2str, int_time, isLin, isMac, isWin, split_fields from anki.utils import dev_mode, ids2str, int_time, is_lin, is_mac, is_win, split_fields
from aqt import gui_hooks from aqt import gui_hooks
from aqt.addons import DownloadLogEntry, check_and_prompt_for_updates, show_log_to_user from aqt.addons import DownloadLogEntry, check_and_prompt_for_updates, show_log_to_user
from aqt.dbcheck import check_db from aqt.dbcheck import check_db
@ -125,7 +125,7 @@ class AnkiQt(QMainWindow):
self.onAppMsg(args[0]) self.onAppMsg(args[0])
# Load profile in a timer so we can let the window finish init and not # Load profile in a timer so we can let the window finish init and not
# close on profile load error. # close on profile load error.
if isWin: if is_win:
fn = self.setupProfileAfterWebviewsLoaded fn = self.setupProfileAfterWebviewsLoaded
else: else:
fn = self.setupProfile fn = self.setupProfile
@ -194,7 +194,7 @@ class AnkiQt(QMainWindow):
def setup_shortcuts(self) -> None: def setup_shortcuts(self) -> None:
QShortcut( QShortcut(
QKeySequence("Ctrl+Meta+F" if isMac else "F11"), QKeySequence("Ctrl+Meta+F" if is_mac else "F11"),
self, self,
self.on_toggle_fullscreen, self.on_toggle_fullscreen,
).setContext(Qt.ShortcutContext.ApplicationShortcut) ).setContext(Qt.ShortcutContext.ApplicationShortcut)
@ -557,7 +557,7 @@ class AnkiQt(QMainWindow):
corrupt = False corrupt = False
try: try:
self.maybeOptimize() self.maybeOptimize()
if not devMode: if not dev_mode:
corrupt = self.col.db.scalar("pragma quick_check") != "ok" corrupt = self.col.db.scalar("pragma quick_check") != "ok"
except: except:
corrupt = True corrupt = True
@ -611,7 +611,7 @@ class AnkiQt(QMainWindow):
assert not self.col or not self.col.db assert not self.col or not self.col.db
nbacks = self.pm.profile["numBackups"] nbacks = self.pm.profile["numBackups"]
if not nbacks or devMode: if not nbacks or dev_mode:
return return
dir = self.pm.backupFolder() dir = self.pm.backupFolder()
path = self.pm.collectionPath() path = self.pm.collectionPath()
@ -845,7 +845,7 @@ title="{}" {}>{}</button>""".format(
self.form.centralwidget.setLayout(self.mainLayout) self.form.centralwidget.setLayout(self.mainLayout)
# force webengine processes to load before cwd is changed # force webengine processes to load before cwd is changed
if isWin: if is_win:
for webview in self.web, self.bottomWeb: for webview in self.web, self.bottomWeb:
webview.force_load_hack() webview.force_load_hack()
@ -1005,7 +1005,7 @@ title="{}" {}>{}</button>""".format(
def setupStyle(self) -> None: def setupStyle(self) -> None:
theme_manager.apply_style() theme_manager.apply_style()
if isLin: if is_lin:
# On Linux, the check requires invoking an external binary, # On Linux, the check requires invoking an external binary,
# which we don't want to be doing frequently # which we don't want to be doing frequently
interval_secs = 300 interval_secs = 300
@ -1260,7 +1260,7 @@ title="{}" {}>{}</button>""".format(
aqt.update.showMessages(self, data) aqt.update.showMessages(self, data)
def clockIsOff(self, diff: int) -> None: def clockIsOff(self, diff: int) -> None:
if devMode: if dev_mode:
print("clock is off; ignoring") print("clock is off; ignoring")
return return
diffText = tr.qt_misc_second(count=diff) diffText = tr.qt_misc_second(count=diff)
@ -1560,14 +1560,14 @@ title="{}" {}>{}</button>""".format(
def setupSystemSpecific(self) -> None: def setupSystemSpecific(self) -> None:
self.hideMenuAccels = False self.hideMenuAccels = False
if isMac: if is_mac:
# mac users expect a minimize option # mac users expect a minimize option
self.minimizeShortcut = QShortcut("Ctrl+M", self) self.minimizeShortcut = QShortcut("Ctrl+M", self)
qconnect(self.minimizeShortcut.activated, self.onMacMinimize) qconnect(self.minimizeShortcut.activated, self.onMacMinimize)
self.hideMenuAccels = True self.hideMenuAccels = True
self.maybeHideAccelerators() self.maybeHideAccelerators()
self.hideStatusTips() self.hideStatusTips()
elif isWin: elif is_win:
# make sure ctypes is bundled # make sure ctypes is bundled
from ctypes import windll, wintypes # type: ignore from ctypes import windll, wintypes # type: ignore
@ -1627,7 +1627,7 @@ title="{}" {}>{}</button>""".format(
) )
return None return None
# raise window # raise window
if isWin: if is_win:
# on windows we can raise the window by minimizing and restoring # on windows we can raise the window by minimizing and restoring
self.showMinimized() self.showMinimized()
self.setWindowState(Qt.WindowState.WindowActive) self.setWindowState(Qt.WindowState.WindowActive)

View file

@ -30,7 +30,7 @@ from anki.collection import GraphPreferences, OpChanges
from anki.decks import UpdateDeckConfigs from anki.decks import UpdateDeckConfigs
from anki.models import NotetypeNames from anki.models import NotetypeNames
from anki.scheduler.v3 import NextStates from anki.scheduler.v3 import NextStates
from anki.utils import devMode, from_json_bytes from anki.utils import dev_mode, from_json_bytes
from aqt.changenotetype import ChangeNotetypeDialog from aqt.changenotetype import ChangeNotetypeDialog
from aqt.deckoptions import DeckOptionsDialog from aqt.deckoptions import DeckOptionsDialog
from aqt.operations.deck import update_deck_configs from aqt.operations.deck import update_deck_configs
@ -87,7 +87,7 @@ class MediaServer(threading.Thread):
def run(self) -> None: def run(self) -> None:
try: try:
if devMode: if dev_mode:
# idempotent if logging has already been set up # idempotent if logging has already been set up
logging.basicConfig() logging.basicConfig()
logging.getLogger("waitress").setLevel(logging.ERROR) logging.getLogger("waitress").setLevel(logging.ERROR)
@ -100,7 +100,7 @@ class MediaServer(threading.Thread):
port=desired_port, port=desired_port,
clear_untrusted_proxy_headers=True, clear_untrusted_proxy_headers=True,
) )
if devMode: if dev_mode:
print( print(
"Serving on http://%s:%s" "Serving on http://%s:%s"
% (self.server.effective_host, self.server.effective_port) # type: ignore % (self.server.effective_host, self.server.effective_port) # type: ignore
@ -163,7 +163,7 @@ class MediaServer(threading.Thread):
return 0 return 0
# https://github.com/Pylons/webtest/blob/4b8a3ebf984185ff4fefb31b4d0cf82682e1fcf7/webtest/http.py#L123-L132 # https://github.com/Pylons/webtest/blob/4b8a3ebf984185ff4fefb31b4d0cf82682e1fcf7/webtest/http.py#L123-L132
for index in range(retries): for index in range(retries):
if devMode or index > 0: if dev_mode or index > 0:
print( print(
f"{datetime.datetime.now()} awaiting media server on {host}:{port}..." f"{datetime.datetime.now()} awaiting media server on {host}:{port}..."
) )
@ -236,7 +236,7 @@ def _handle_local_file_request(request: LocalFileRequest) -> Response:
) )
except Exception as error: except Exception as error:
if devMode: if dev_mode:
print( print(
"Caught HTTP server exception,\n%s" "Caught HTTP server exception,\n%s"
% "".join(traceback.format_exception(*sys.exc_info())), % "".join(traceback.format_exception(*sys.exc_info())),
@ -260,7 +260,7 @@ def _builtin_data(path: str) -> bytes:
with open(full_path, "rb") as f: with open(full_path, "rb") as f:
return f.read() return f.read()
else: else:
if isWin and not getattr(sys, "frozen", False): if is_win and not getattr(sys, "frozen", False):
# default Python resource loader expects backslashes on Windows # default Python resource loader expects backslashes on Windows
path = path.replace("/", "\\") path = path.replace("/", "\\")
reader = aqt.__loader__.get_resource_reader("aqt") # type: ignore reader = aqt.__loader__.get_resource_reader("aqt") # type: ignore
@ -281,7 +281,7 @@ def _handle_builtin_file_request(request: BundledFileRequest) -> Response:
HTTPStatus.NOT_FOUND, HTTPStatus.NOT_FOUND,
) )
except Exception as error: except Exception as error:
if devMode: if dev_mode:
print( print(
"Caught HTTP server exception,\n%s" "Caught HTTP server exception,\n%s"
% "".join(traceback.format_exception(*sys.exc_info())), % "".join(traceback.format_exception(*sys.exc_info())),
@ -299,7 +299,7 @@ def _handle_builtin_file_request(request: BundledFileRequest) -> Response:
@app.route("/<path:pathin>", methods=["GET", "POST"]) @app.route("/<path:pathin>", methods=["GET", "POST"])
def handle_request(pathin: str) -> Response: def handle_request(pathin: str) -> Response:
request = _extract_request(pathin) request = _extract_request(pathin)
if devMode: if dev_mode:
print(f"{time.time():.3f} {flask.request.method} /{pathin}") print(f"{time.time():.3f} {flask.request.method} /{pathin}")
if isinstance(request, NotFound): if isinstance(request, NotFound):
@ -383,7 +383,7 @@ def _extract_addon_request(path: str) -> LocalFileRequest | NotFound | None:
try: try:
manager = aqt.mw.addonManager manager = aqt.mw.addonManager
except AttributeError as error: except AttributeError as error:
if devMode: if dev_mode:
print(f"_redirectWebExports: {error}") print(f"_redirectWebExports: {error}")
return None return None

View file

@ -42,7 +42,7 @@ from distutils.spawn import ( # pylint: disable=import-error,no-name-in-module
from queue import Empty, Full, Queue from queue import Empty, Full, Queue
from typing import Optional from typing import Optional
from anki.utils import isWin from anki.utils import is_win
class MPVError(Exception): class MPVError(Exception):
@ -65,7 +65,7 @@ class MPVTimeoutError(MPVError):
pass pass
if isWin: if is_win:
# pylint: disable=import-error # pylint: disable=import-error
import pywintypes import pywintypes
import win32file # pytype: disable=import-error import win32file # pytype: disable=import-error
@ -142,7 +142,7 @@ class MPVBase:
"""Create a random socket filename which we pass to mpv with the """Create a random socket filename which we pass to mpv with the
--input-unix-socket option. --input-unix-socket option.
""" """
if isWin: if is_win:
self._sock_filename = "ankimpv" self._sock_filename = "ankimpv"
return return
fd, self._sock_filename = tempfile.mkstemp(prefix="mpv.") fd, self._sock_filename = tempfile.mkstemp(prefix="mpv.")
@ -157,7 +157,7 @@ class MPVBase:
while self.is_running() and time.time() < start + 10: while self.is_running() and time.time() < start + 10:
time.sleep(0.1) time.sleep(0.1)
if isWin: if is_win:
# named pipe # named pipe
try: try:
self._sock = win32file.CreateFile( self._sock = win32file.CreateFile(
@ -228,7 +228,7 @@ class MPVBase:
""" """
buf = b"" buf = b""
while not self._stop_event.is_set(): while not self._stop_event.is_set():
if isWin: if is_win:
try: try:
(n, b) = win32file.ReadFile(self._sock, 4096) (n, b) = win32file.ReadFile(self._sock, 4096)
buf += b buf += b
@ -327,7 +327,7 @@ class MPVBase:
raise MPVTimeoutError("unable to put request") raise MPVTimeoutError("unable to put request")
# Write the message data to the socket. # Write the message data to the socket.
if isWin: if is_win:
win32file.WriteFile(self._sock, data) win32file.WriteFile(self._sock, data)
else: else:
while data: while data:

View file

@ -40,14 +40,14 @@ import anki.sync
import anki.rsbackend import anki.rsbackend
# platform-specifics # platform-specifics
from anki.utils import isLin, isMac, isWin from anki.utils import is_lin, is_mac, is_win
if isWin: if is_win:
# external module access # external module access
import pythoncom import pythoncom
import pywintypes import pywintypes
import win32com import win32com
if isLin: if is_lin:
# file locking # file locking
import fcntl import fcntl

View file

@ -11,12 +11,12 @@ import sys
from ctypes import CDLL from ctypes import CDLL
import aqt.utils import aqt.utils
from anki.utils import isLin, isMac, isWin from anki.utils import is_lin, is_mac, is_win
def get_windows_dark_mode() -> bool: def get_windows_dark_mode() -> bool:
"True if Windows system is currently in dark mode." "True if Windows system is currently in dark mode."
if not isWin: if not is_win:
return False return False
from winreg import ( # pylint: disable=import-error from winreg import ( # pylint: disable=import-error
@ -34,7 +34,7 @@ def get_windows_dark_mode() -> bool:
def set_macos_dark_mode(enabled: bool) -> bool: def set_macos_dark_mode(enabled: bool) -> bool:
"True if setting successful." "True if setting successful."
if not isMac: if not is_mac:
return False return False
try: try:
_ankihelper().set_darkmode_enabled(enabled) _ankihelper().set_darkmode_enabled(enabled)
@ -47,7 +47,7 @@ def set_macos_dark_mode(enabled: bool) -> bool:
def get_macos_dark_mode() -> bool: def get_macos_dark_mode() -> bool:
"True if macOS system is currently in dark mode." "True if macOS system is currently in dark mode."
if not isMac: if not is_mac:
return False return False
try: try:
return _ankihelper().system_is_dark() return _ankihelper().system_is_dark()
@ -60,7 +60,7 @@ def get_macos_dark_mode() -> bool:
def get_linux_dark_mode() -> bool: def get_linux_dark_mode() -> bool:
"""True if Linux system is in dark mode. """True if Linux system is in dark mode.
This only works if the GTK theme name contains '-dark'""" This only works if the GTK theme name contains '-dark'"""
if not isLin: if not is_lin:
return False return False
try: try:
process = subprocess.run( process = subprocess.run(

View file

@ -295,12 +295,12 @@ def video_driver_name_for_platform(driver: VideoDriver) -> str:
if driver == VideoDriver.ANGLE: if driver == VideoDriver.ANGLE:
return tr.preferences_video_driver_angle() return tr.preferences_video_driver_angle()
elif driver == VideoDriver.Software: elif driver == VideoDriver.Software:
if isMac: if is_mac:
return tr.preferences_video_driver_software_mac() return tr.preferences_video_driver_software_mac()
else: else:
return tr.preferences_video_driver_software_other() return tr.preferences_video_driver_software_other()
else: else:
if isMac: if is_mac:
return tr.preferences_video_driver_opengl_mac() return tr.preferences_video_driver_opengl_mac()
else: else:
return tr.preferences_video_driver_opengl_other() return tr.preferences_video_driver_opengl_other()

View file

@ -20,7 +20,7 @@ from anki.collection import Collection
from anki.db import DB from anki.db import DB
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.sync import SyncAuth from anki.sync import SyncAuth
from anki.utils import int_time, isMac, isWin from anki.utils import int_time, is_mac, is_win
from aqt import appHelpSite from aqt import appHelpSite
from aqt.qt import * from aqt.qt import *
from aqt.theme import Theme from aqt.theme import Theme
@ -39,20 +39,20 @@ class VideoDriver(Enum):
@staticmethod @staticmethod
def default_for_platform() -> VideoDriver: def default_for_platform() -> VideoDriver:
if isMac: if is_mac:
return VideoDriver.OpenGL return VideoDriver.OpenGL
else: else:
return VideoDriver.Software return VideoDriver.Software
def constrained_to_platform(self) -> VideoDriver: def constrained_to_platform(self) -> VideoDriver:
if self == VideoDriver.ANGLE and not isWin: if self == VideoDriver.ANGLE and not is_win:
return VideoDriver.Software return VideoDriver.Software
return self return self
def next(self) -> VideoDriver: def next(self) -> VideoDriver:
if self == VideoDriver.Software: if self == VideoDriver.Software:
return VideoDriver.OpenGL return VideoDriver.OpenGL
elif self == VideoDriver.OpenGL and isWin: elif self == VideoDriver.OpenGL and is_win:
return VideoDriver.ANGLE return VideoDriver.ANGLE
else: else:
return VideoDriver.Software return VideoDriver.Software
@ -60,7 +60,7 @@ class VideoDriver(Enum):
@staticmethod @staticmethod
def all_for_platform() -> list[VideoDriver]: def all_for_platform() -> list[VideoDriver]:
all = [VideoDriver.OpenGL] all = [VideoDriver.OpenGL]
if isWin: if is_win:
all.append(VideoDriver.ANGLE) all.append(VideoDriver.ANGLE)
all.append(VideoDriver.Software) all.append(VideoDriver.Software)
return all return all
@ -340,11 +340,11 @@ class ProfileManager:
self.ensureBaseExists() self.ensureBaseExists()
def _defaultBase(self) -> str: def _defaultBase(self) -> str:
if isWin: if is_win:
from aqt.winpaths import get_appdata from aqt.winpaths import get_appdata
return os.path.join(get_appdata(), "Anki2") return os.path.join(get_appdata(), "Anki2")
elif isMac: elif is_mac:
return os.path.expanduser("~/Library/Application Support/Anki2") return os.path.expanduser("~/Library/Application Support/Anki2")
else: else:
dataDir = os.environ.get( dataDir = os.environ.get(

View file

@ -20,7 +20,7 @@ else:
from . import qt5_compat # needs to be imported first from . import qt5_compat # needs to be imported first
from .qt6 import * from .qt6 import *
from anki.utils import isMac, isWin from anki.utils import is_mac, is_win
# fix buggy ubuntu12.04 display of language selector # fix buggy ubuntu12.04 display of language selector
os.environ["LIBOVERLAY_SCROLLBAR"] = "0" os.environ["LIBOVERLAY_SCROLLBAR"] = "0"

View file

@ -23,7 +23,7 @@ import aqt
from anki import hooks from anki import hooks
from anki.cards import Card from anki.cards import Card
from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag
from anki.utils import isLin, isMac, isWin, namedtmp from anki.utils import is_lin, is_mac, is_win, namedtmp
from aqt import gui_hooks from aqt import gui_hooks
from aqt.mpv import MPV, MPVBase, MPVCommandError from aqt.mpv import MPV, MPVBase, MPVCommandError
from aqt.qt import * from aqt.qt import *
@ -235,7 +235,7 @@ def _packagedCmd(cmd: list[str]) -> tuple[Any, dict[str, str]]:
if "LD_LIBRARY_PATH" in env: if "LD_LIBRARY_PATH" in env:
del env["LD_LIBRARY_PATH"] del env["LD_LIBRARY_PATH"]
packaged_path = Path(sys.prefix) / "audio" / (cmd[0] + (".exe" if isWin else "")) packaged_path = Path(sys.prefix) / "audio" / (cmd[0] + (".exe" if is_win else ""))
if packaged_path.exists(): if packaged_path.exists():
cmd[0] = str(packaged_path) cmd[0] = str(packaged_path)
@ -359,7 +359,7 @@ class SimpleMpvPlayer(SimpleProcessPlayer, VideoPlayer):
class SimpleMplayerPlayer(SimpleProcessPlayer, SoundOrVideoPlayer): class SimpleMplayerPlayer(SimpleProcessPlayer, SoundOrVideoPlayer):
args, env = _packagedCmd(["mplayer", "-really-quiet", "-noautosub"]) args, env = _packagedCmd(["mplayer", "-really-quiet", "-noautosub"])
if isWin: if is_win:
args += ["-ao", "win32"] args += ["-ao", "win32"]
@ -369,7 +369,7 @@ class SimpleMplayerPlayer(SimpleProcessPlayer, SoundOrVideoPlayer):
class MpvManager(MPV, SoundOrVideoPlayer): class MpvManager(MPV, SoundOrVideoPlayer):
if not isLin: if not is_lin:
default_argv = MPVBase.default_argv + [ default_argv = MPVBase.default_argv + [
"--input-media-keys=no", "--input-media-keys=no",
] ]
@ -801,7 +801,7 @@ def setup_audio(taskman: TaskManager, base_folder: str) -> None:
if mpvManager is not None: if mpvManager is not None:
av_player.players.append(mpvManager) av_player.players.append(mpvManager)
if isWin: if is_win:
mpvPlayer = SimpleMpvPlayer(taskman, base_folder) mpvPlayer = SimpleMpvPlayer(taskman, base_folder)
av_player.players.append(mpvPlayer) av_player.players.append(mpvPlayer)
else: else:
@ -809,11 +809,11 @@ def setup_audio(taskman: TaskManager, base_folder: str) -> None:
av_player.players.append(mplayer) av_player.players.append(mplayer)
# tts support # tts support
if isMac: if is_mac:
from aqt.tts import MacTTSPlayer from aqt.tts import MacTTSPlayer
av_player.players.append(MacTTSPlayer(taskman)) av_player.players.append(MacTTSPlayer(taskman))
elif isWin: elif is_win:
from aqt.tts import WindowsTTSPlayer from aqt.tts import WindowsTTSPlayer
av_player.players.append(WindowsTTSPlayer(taskman)) av_player.players.append(WindowsTTSPlayer(taskman))

View file

@ -8,8 +8,8 @@ import platform
from dataclasses import dataclass from dataclasses import dataclass
import aqt import aqt
from anki.utils import isMac from anki.utils import is_mac
from aqt import QApplication, colors, gui_hooks, isWin from aqt import QApplication, colors, gui_hooks, is_win
from aqt.platform import ( from aqt.platform import (
get_linux_dark_mode, get_linux_dark_mode,
get_macos_dark_mode, get_macos_dark_mode,
@ -65,7 +65,7 @@ class ThemeManager:
def macos_dark_mode(self) -> bool: def macos_dark_mode(self) -> bool:
"True if the user has night mode on, and has forced native widgets." "True if the user has night mode on, and has forced native widgets."
if not isMac: if not is_mac:
return False return False
if not self._night_mode_preference: if not self._night_mode_preference:
@ -128,9 +128,9 @@ class ThemeManager:
def body_class(self, night_mode: bool | None = None) -> str: def body_class(self, night_mode: bool | None = None) -> str:
"Returns space-separated class list for platform/theme." "Returns space-separated class list for platform/theme."
classes = [] classes = []
if isWin: if is_win:
classes.append("isWin") classes.append("isWin")
elif isMac: elif is_mac:
classes.append("isMac") classes.append("isMac")
else: else:
classes.append("isLin") classes.append("isLin")
@ -164,9 +164,9 @@ class ThemeManager:
elif theme == Theme.DARK: elif theme == Theme.DARK:
return True return True
else: else:
if isWin: if is_win:
return get_windows_dark_mode() return get_windows_dark_mode()
elif isMac: elif is_mac:
return get_macos_dark_mode() return get_macos_dark_mode()
else: else:
return get_linux_dark_mode() return get_linux_dark_mode()
@ -192,7 +192,7 @@ class ThemeManager:
def _apply_style(self, app: QApplication) -> None: def _apply_style(self, app: QApplication) -> None:
buf = "" buf = ""
if isWin and platform.release() == "10": if is_win and platform.release() == "10":
# day mode is missing a bottom border; background must be # day mode is missing a bottom border; background must be
# also set for border to apply # also set for border to apply
buf += f""" buf += f"""

View file

@ -41,7 +41,7 @@ from typing import Any, cast
import anki import anki
from anki import hooks from anki import hooks
from anki.sound import AVTag, TTSTag from anki.sound import AVTag, TTSTag
from anki.utils import checksum, isWin, tmpdir from anki.utils import checksum, is_win, tmpdir
from aqt import gui_hooks from aqt import gui_hooks
from aqt.sound import OnDoneCallback, SimpleProcessPlayer from aqt.sound import OnDoneCallback, SimpleProcessPlayer
from aqt.utils import tooltip, tr from aqt.utils import tooltip, tr
@ -264,7 +264,7 @@ class WindowsVoice(TTSVoice):
handle: Any handle: Any
if isWin: if is_win:
import win32com.client # pylint: disable=import-error import win32com.client # pylint: disable=import-error
# language ID map from https://github.com/sindresorhus/lcid/blob/master/lcid.json # language ID map from https://github.com/sindresorhus/lcid/blob/master/lcid.json

View file

@ -14,8 +14,8 @@ from anki.collection import Collection, HelpPage
from anki.lang import TR, tr_legacyglobal # pylint: disable=unused-import from anki.lang import TR, tr_legacyglobal # pylint: disable=unused-import
from anki.utils import ( from anki.utils import (
invalid_filename, invalid_filename,
isMac, is_mac,
isWin, is_win,
no_bundled_libs, no_bundled_libs,
version_with_build, version_with_build,
) )
@ -509,7 +509,7 @@ def getSaveFile(
def saveGeom(widget: QWidget, key: str) -> None: def saveGeom(widget: QWidget, key: str) -> None:
key += "Geom" key += "Geom"
if isMac and (widget.windowState() & Qt.WindowState.WindowFullScreen): if is_mac and (widget.windowState() & Qt.WindowState.WindowFullScreen):
geom = None geom = None
else: else:
geom = widget.saveGeometry() geom = widget.saveGeometry()
@ -522,7 +522,7 @@ def restoreGeom(
key += "Geom" key += "Geom"
if aqt.mw.pm.profile.get(key): if aqt.mw.pm.profile.get(key):
widget.restoreGeometry(aqt.mw.pm.profile[key]) widget.restoreGeometry(aqt.mw.pm.profile[key])
if isMac and offset: if is_mac and offset:
if qtmajor > 5 or qtminor > 6: if qtmajor > 5 or qtminor > 6:
# bug in osx toolkit # bug in osx toolkit
s = widget.size() s = widget.size()
@ -659,7 +659,7 @@ def mungeQA(col: Collection, txt: str) -> str:
def openFolder(path: str) -> None: def openFolder(path: str) -> None:
if isWin: if is_win:
subprocess.run(["explorer", f"file://{path}"], check=False) subprocess.run(["explorer", f"file://{path}"], check=False)
else: else:
with no_bundled_libs(): with no_bundled_libs():
@ -667,20 +667,20 @@ def openFolder(path: str) -> None:
def shortcut(key: str) -> str: def shortcut(key: str) -> str:
if isMac: if is_mac:
return re.sub("(?i)ctrl", "Command", key) return re.sub("(?i)ctrl", "Command", key)
return key return key
def maybeHideClose(bbox: QDialogButtonBox) -> None: def maybeHideClose(bbox: QDialogButtonBox) -> None:
if isMac: if is_mac:
b = bbox.button(QDialogButtonBox.StandardButton.Close) b = bbox.button(QDialogButtonBox.StandardButton.Close)
if b: if b:
bbox.removeButton(b) bbox.removeButton(b)
def addCloseShortcut(widg: QDialog) -> None: def addCloseShortcut(widg: QDialog) -> None:
if not isMac: if not is_mac:
return return
shortcut = QShortcut(QKeySequence("Ctrl+W"), widg) shortcut = QShortcut(QKeySequence("Ctrl+W"), widg)
qconnect(shortcut.activated, widg.reject) qconnect(shortcut.activated, widg.reject)
@ -688,7 +688,7 @@ def addCloseShortcut(widg: QDialog) -> None:
def downArrow() -> str: def downArrow() -> str:
if isWin: if is_win:
return "" return ""
# windows 10 is lacking the smaller arrow on English installs # windows 10 is lacking the smaller arrow on English installs
return "" return ""
@ -853,9 +853,9 @@ def supportText() -> str:
from aqt import mw from aqt import mw
if isWin: if is_win:
platname = f"Windows {platform.win32_ver()[0]}" platname = f"Windows {platform.win32_ver()[0]}"
elif isMac: elif is_mac:
platname = f"Mac {platform.mac_ver()[0]}" platname = f"Mac {platform.mac_ver()[0]}"
else: else:
platname = "Linux" platname = "Linux"

View file

@ -9,7 +9,7 @@ from typing import Any, Callable, Optional, Sequence, cast
import anki import anki
from anki.lang import is_rtl from anki.lang import is_rtl
from anki.utils import isLin, isMac, isWin from anki.utils import is_lin, is_mac, is_win
from aqt import colors, gui_hooks from aqt import colors, gui_hooks
from aqt.qt import * from aqt.qt import *
from aqt.theme import theme_manager from aqt.theme import theme_manager
@ -265,7 +265,7 @@ class AnkiWebView(QWebEngineView):
isinstance(evt, QMouseEvent) isinstance(evt, QMouseEvent)
and evt.type() == QEvent.Type.MouseButtonRelease and evt.type() == QEvent.Type.MouseButtonRelease
): ):
if evt.button() == Qt.MouseButton.MiddleButton and isLin: if evt.button() == Qt.MouseButton.MiddleButton and is_lin:
self.onMiddleClickPaste() self.onMiddleClickPaste()
return True return True
return False return False
@ -356,7 +356,7 @@ class AnkiWebView(QWebEngineView):
if webscale: if webscale:
return float(webscale) return float(webscale)
if qtmajor > 5 or isMac: if qtmajor > 5 or is_mac:
return 1 return 1
screen = QApplication.desktop().screen() # type: ignore screen = QApplication.desktop().screen() # type: ignore
if screen is None: if screen is None:
@ -364,7 +364,7 @@ class AnkiWebView(QWebEngineView):
dpi = screen.logicalDpiX() dpi = screen.logicalDpiX()
factor = dpi / 96.0 factor = dpi / 96.0
if isLin: if is_lin:
factor = max(1, factor) factor = max(1, factor)
return factor return factor
return 1 return 1
@ -388,7 +388,7 @@ class AnkiWebView(QWebEngineView):
def get_window_bg_color(self, night_mode: bool) -> QColor: def get_window_bg_color(self, night_mode: bool) -> QColor:
if night_mode: if night_mode:
return QColor(colors.WINDOW_BG[1]) return QColor(colors.WINDOW_BG[1])
elif isMac: elif is_mac:
# standard palette does not return correct window color on macOS # standard palette does not return correct window color on macOS
return QColor("#ececec") return QColor("#ececec")
else: else:
@ -398,13 +398,13 @@ class AnkiWebView(QWebEngineView):
palette = theme_manager.default_palette palette = theme_manager.default_palette
color_hl = palette.color(QPalette.ColorRole.Highlight).name() color_hl = palette.color(QPalette.ColorRole.Highlight).name()
if isWin: if is_win:
# T: include a font for your language on Windows, eg: "Segoe UI", "MS Mincho" # T: include a font for your language on Windows, eg: "Segoe UI", "MS Mincho"
family = tr.qt_misc_segoe_ui() family = tr.qt_misc_segoe_ui()
button_style = "button { font-family:%s; }" % family button_style = "button { font-family:%s; }" % family
button_style += "\n:focus { outline: 1px solid %s; }" % color_hl button_style += "\n:focus { outline: 1px solid %s; }" % color_hl
font = f"font-size:12px;font-family:{family};" font = f"font-size:12px;font-family:{family};"
elif isMac: elif is_mac:
family = "Helvetica" family = "Helvetica"
font = f'font-size:15px;font-family:"{family}";' font = f'font-size:15px;font-family:"{family}";'
button_style = """ button_style = """