# ensure unicode filenames are supported try: "テスト".encode(sys.getfilesystemencoding()) except UnicodeEncodeError as exc: raise Exception("Anki requires a UTF-8 locale.") from exc from .package import packaged_build_setup packaged_build_setup() # syncserver needs to be run before Qt loaded if "--syncserver" in sys.argv: from anki.syncserver import serve serve() sys.exit(0) import argparse import builtins import cProfile import getpass import locale import os import tempfile import traceback from typing import TYPE_CHECKING, Any, Callable, Optional, cast import anki.lang from anki._backend import RustBackend from anki.buildinfo import version as _version
def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiApp]: """Start AnkiQt application or reuse an existing instance if one exists. If the function is invoked with exec=False, the AnkiQt will not enter the main event loop - instead the application object will be returned. The 'exec' and 'argv' arguments will be useful for testing purposes. If no 'argv' is supplied then 'sys.argv' will be used. """ global mw global profiler if argv is None: argv = sys.argv # parse args opts, args = parseArgs(argv) if opts.version: print(f"Anki {appVersion}") return None elif opts.syncserver: from anki.syncserver import serve serve() return None if PROFILE_CODE: profiler = cProfile.Profile() profiler.enable() if (getattr(sys, "frozen", False) and os.getenv("QT_QPA_PLATFORM") == "wayland" and not os.getenv("ANKI_WAYLAND")): # users need to opt in to wayland support, given the issues it has print("Wayland support is disabled by default due to bugs.") print("You can force it on with an env var: ANKI_WAYLAND=1") os.environ["QT_QPA_PLATFORM"] = "xcb" # default to specified/system language before getting user's preference so that we can localize some more strings lang = anki.lang.get_def_lang(opts.lang) anki.lang.set_lang(lang[1]) # profile manager pm = None try: pm = ProfileManager(opts.base) pmLoadResult = pm.setupMeta() except: # will handle below traceback.print_exc() pm = None if pm: # gl workarounds setupGL(pm) # apply user-provided scale factor os.environ["QT_SCALE_FACTOR"] = str(pm.uiScale()) # opt in to full hidpi support? if not os.environ.get("ANKI_NOHIGHDPI") and qtmajor == 5: QCoreApplication.setAttribute( Qt.ApplicationAttribute.AA_EnableHighDpiScaling) # type: ignore QCoreApplication.setAttribute( Qt.ApplicationAttribute.AA_UseHighDpiPixmaps) # type: ignore os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" os.environ["QT_SCALE_FACTOR_ROUNDING_POLICY"] = "PassThrough" # Opt into software rendering. Useful for buggy systems. if os.environ.get("ANKI_SOFTWAREOPENGL"): QCoreApplication.setAttribute( Qt.ApplicationAttribute.AA_UseSoftwareOpenGL) if (isWin and qtmajor == 5 and (qtminor == 14 or (qtminor == 15 and qtpoint == 0)) and "QT_QPA_PLATFORM" not in os.environ): os.environ["QT_QPA_PLATFORM"] = "windows:altgr" # create the app QCoreApplication.setApplicationName("Anki") QGuiApplication.setDesktopFileName("anki.desktop") app = AnkiApp(argv) if app.secondInstance(): # we've signaled the primary instance, so we should close return None if not pm: QMessageBox.critical( None, tr.qt_misc_error(), tr.profiles_could_not_create_data_folder(), ) return None # disable icons on mac; this must be done before window created if isMac: app.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus) # disable help button in title bar on qt versions that support it if isWin and qtmajor == 5 and qtminor >= 10: QApplication.setAttribute( QApplication.Attribute. AA_DisableWindowContextHelpButton # type: ignore ) # proxy configured? from urllib.request import getproxies, proxy_bypass disable_proxies = False try: if "http" in getproxies(): # if it's not set up to bypass localhost, we'll # need to disable proxies in the webviews if not proxy_bypass("127.0.0.1"): disable_proxies = True except UnicodeDecodeError: # proxy_bypass can't handle unicode in hostnames; assume we need # to disable proxies disable_proxies = True if disable_proxies: print("webview proxy use disabled") proxy = QNetworkProxy() proxy.setType(QNetworkProxy.ProxyType.NoProxy) QNetworkProxy.setApplicationProxy(proxy) # we must have a usable temp dir try: tempfile.gettempdir() except: QMessageBox.critical( None, tr.qt_misc_error(), tr.qt_misc_no_temp_folder(), ) return None # make image resources available from aqt.utils import aqt_data_folder QDir.addSearchPath("icons", os.path.join(aqt_data_folder(), "qt", "icons")) if pmLoadResult.firstTime: pm.setDefaultLang(lang[0]) if pmLoadResult.loadError: QMessageBox.warning( None, tr.profiles_prefs_corrupt_title(), tr.profiles_prefs_file_is_corrupt(), ) if opts.profile: pm.openProfile(opts.profile) # i18n & backend backend = setupLangAndBackend(pm, app, opts.lang, pmLoadResult.firstTime) driver = pm.video_driver() if isLin and driver == VideoDriver.OpenGL: from aqt.utils import gfxDriverIsBroken if gfxDriverIsBroken(): pm.set_video_driver(driver.next()) QMessageBox.critical( None, tr.qt_misc_error(), tr.qt_misc_incompatible_video_driver(), ) sys.exit(1) # load the main window import aqt.main mw = aqt.main.AnkiQt(app, pm, backend, opts, args) if exec: app.exec() else: return app if PROFILE_CODE: write_profile_results() return None
def _run(argv=None, exec=True) -> Optional[AnkiApp]: """Start AnkiQt application or reuse an existing instance if one exists. If the function is invoked with exec=False, the AnkiQt will not enter the main event loop - instead the application object will be returned. The 'exec' and 'argv' arguments will be useful for testing purposes. If no 'argv' is supplied then 'sys.argv' will be used. """ global mw global profiler if argv is None: argv = sys.argv # parse args opts, args = parseArgs(argv) if opts.version: print(f"Anki {appVersion}") return None elif opts.syncserver: from anki.syncserver import serve serve() return None if PROFILE_CODE: profiler = cProfile.Profile() profiler.enable() # default to specified/system language before getting user's preference so that we can localize some more strings lang = anki.lang.get_def_lang(opts.lang) anki.lang.set_lang(lang[1], locale_dir()) # profile manager pm = None try: pm = ProfileManager(opts.base) pmLoadResult = pm.setupMeta() except AnkiRestart as error: if error.exitcode: sys.exit(error.exitcode) return None except: # will handle below traceback.print_exc() pm = None if pm: # gl workarounds setupGL(pm) # apply user-provided scale factor os.environ["QT_SCALE_FACTOR"] = str(pm.uiScale()) # opt in to full hidpi support? if not os.environ.get("ANKI_NOHIGHDPI"): QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling) os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" os.environ["QT_SCALE_FACTOR_ROUNDING_POLICY"] = "PassThrough" # Opt into software rendering. Useful for buggy systems. if os.environ.get("ANKI_SOFTWAREOPENGL"): QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL) if ( isWin and (qtminor == 14 or (qtminor == 15 and qtpoint == 0)) and "QT_QPA_PLATFORM" not in os.environ ): os.environ["QT_QPA_PLATFORM"] = "windows:altgr" # create the app QCoreApplication.setApplicationName("Anki") QGuiApplication.setDesktopFileName("anki.desktop") app = AnkiApp(argv) if app.secondInstance(): # we've signaled the primary instance, so we should close return None if not pm: QMessageBox.critical( None, tr(TR.QT_MISC_ERROR), tr(TR.PROFILES_COULD_NOT_CREATE_DATA_FOLDER), ) return None # disable icons on mac; this must be done before window created if isMac: app.setAttribute(Qt.AA_DontShowIconsInMenus) # disable help button in title bar on qt versions that support it if isWin and qtminor >= 10: QApplication.setAttribute(Qt.AA_DisableWindowContextHelpButton) # proxy configured? from urllib.request import getproxies, proxy_bypass disable_proxies = False try: if "http" in getproxies(): # if it's not set up to bypass localhost, we'll # need to disable proxies in the webviews if not proxy_bypass("127.0.0.1"): disable_proxies = True except UnicodeDecodeError: # proxy_bypass can't handle unicode in hostnames; assume we need # to disable proxies disable_proxies = True if disable_proxies: print("webview proxy use disabled") proxy = QNetworkProxy() proxy.setType(QNetworkProxy.NoProxy) QNetworkProxy.setApplicationProxy(proxy) # we must have a usable temp dir try: tempfile.gettempdir() except: QMessageBox.critical( None, tr(TR.QT_MISC_ERROR), tr(TR.QT_MISC_NO_TEMP_FOLDER), ) return None if pmLoadResult.firstTime: pm.setDefaultLang(lang[0]) if pmLoadResult.loadError: QMessageBox.warning( None, tr(TR.PROFILES_PREFS_CORRUPT_TITLE), tr(TR.PROFILES_PREFS_FILE_IS_CORRUPT), ) if opts.profile: pm.openProfile(opts.profile) # i18n & backend backend = setupLangAndBackend(pm, app, opts.lang, pmLoadResult.firstTime) driver = pm.video_driver() if isLin and driver == VideoDriver.OpenGL: from aqt.utils import gfxDriverIsBroken if gfxDriverIsBroken(): pm.set_video_driver(driver.next()) QMessageBox.critical( None, tr(TR.QT_MISC_ERROR), tr(TR.QT_MISC_INCOMPATIBLE_VIDEO_DRIVER), ) sys.exit(1) # load the main window import aqt.main mw = aqt.main.AnkiQt(app, pm, backend, opts, args) if exec: app.exec() else: return app if PROFILE_CODE: write_profile_results() return None