Ejemplo n.º 1
0
def _run(argv=None, exec=True):
    """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 version '{appVersion}'")
        return

    if PROFILE_CODE:
        import cProfile

        profiler = cProfile.Profile()
        profiler.enable()

    # profile manager
    pm = None
    try:
        pm = ProfileManager(opts.base)
        pmLoadResult = pm.setupMeta()
    except AnkiRestart as error:
        if error.exitcode:
            sys.exit(error.exitcode)
        return
    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

    if not pm:
        QMessageBox.critical(
            None,
            "Error",
            """\
Anki could not create its data folder. Please see the File Locations \
section of the manual, and ensure that location is not read-only.""",
        )
        return

    # 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 proxy_bypass, getproxies

    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,
            "Error",
            """\
No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
environment points to a valid, writable folder.""",
        )
        return

    if pmLoadResult.firstTime:
        pm.setDefaultLang()

    if pmLoadResult.loadError:
        QMessageBox.warning(
            None,
            "Preferences Corrupt",
            """Anki's prefs21.db file was corrupt and has been recreated. If you were using multiple \
    profiles, please add them back using the same names to recover your cards.""",
        )

    if opts.profile:
        pm.openProfile(opts.profile)

    # i18n & backend
    backend = setupLangAndBackend(pm, app, opts.lang)

    if isLin and pm.glMode() == "auto":
        from aqt.utils import gfxDriverIsBroken

        if gfxDriverIsBroken():
            pm.nextGlMode()
            QMessageBox.critical(
                None,
                "Error",
                "Your video driver is incompatible. Please start Anki again, and Anki will switch to a slower, more compatible mode.",
            )
            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()
Ejemplo n.º 2
0
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

    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("https://github.com/ankitects/anki/issues/1767")
        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)

    # fix an issue on Windows, where Ctrl+Alt shortcuts are triggered by AltGr,
    # preventing users from typing things like "@" through AltGr+Q on a German
    # keyboard.
    if is_win 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 is_mac:
        app.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus)

    # disable help button in title bar on qt versions that support it
    if is_win and qtmajor == 5 and qtminor >= 10:
        QApplication.setAttribute(
            Qt.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 is_lin 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:
        print("Starting main loop...")
        app.exec()
    else:
        return app

    if PROFILE_CODE:
        write_profile_results()

    return None
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
def _run(argv=None, exec=True):
    """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

    if argv is None:
        argv = sys.argv

    # parse args
    opts, args = parseArgs(argv)

    # profile manager
    from aqt.profiles import ProfileManager
    pm = ProfileManager(opts.base)

    # gl workarounds
    setupGL(pm)

    # opt in to full hidpi support?
    if not os.environ.get("ANKI_NOHIGHDPI"):
        QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)

    # Opt into software rendering. Useful for buggy systems.
    if os.environ.get("ANKI_SOFTWAREOPENGL"):
        QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)

    # 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

    # disable icons on mac; this must be done before window created
    if isMac:
        app.setAttribute(Qt.AA_DontShowIconsInMenus)

    # proxy configured?
    from urllib.request import proxy_bypass, getproxies
    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"):
            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, "Error", """\
No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
environment points to a valid, writable folder.""")
        return

    pm.setupMeta()

    if opts.profile:
        pm.openProfile(opts.profile)

    # i18n
    setupLang(pm, app, opts.lang)

    if isLin and pm.glMode() == "auto":
        from aqt.utils import gfxDriverIsBroken
        if gfxDriverIsBroken():
            pm.nextGlMode()
            QMessageBox.critical(
                None, "Error",
                "Your video driver is incompatible. Please start Anki again, and Anki will switch to a slower, more compatible mode."
            )
            sys.exit(1)

    # load the main window
    import aqt.main
    mw = aqt.main.AnkiQt(app, pm, opts, args)
    if exec:
        app.exec()
    else:
        return app
Ejemplo n.º 5
0
def _run(argv=None, exec=True):
    """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

    if argv is None:
        argv = sys.argv

    # parse args
    opts, args = parseArgs(argv)
    opts.base = opts.base or ""
    opts.profile = opts.profile or ""

    # profile manager
    from aqt.profiles import ProfileManager
    pm = ProfileManager(opts.base)

    # gl workarounds
    setupGL(pm)

    # opt in to full hidpi support?
    if not os.environ.get("ANKI_NOHIGHDPI"):
        QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)

    # create the app
    app = AnkiApp(argv)
    QCoreApplication.setApplicationName("Anki")
    if app.secondInstance():
        # we've signaled the primary instance, so we should close
        return

    # disable icons on mac; this must be done before window created
    if isMac:
        app.setAttribute(Qt.AA_DontShowIconsInMenus)

    # we must have a usable temp dir
    try:
        tempfile.gettempdir()
    except:
        QMessageBox.critical(
            None, "Error", """\
No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
environment points to a valid, writable folder.""")
        return

    pm.setupMeta()

    if opts.profile:
        pm.openProfile(opts.profile)

    # i18n
    setupLang(pm, app, opts.lang)

    # remaining pm init
    pm.ensureProfile()

    # load the main window
    import aqt.main
    mw = aqt.main.AnkiQt(app, pm, opts, args)
    if exec:
        app.exec()
    else:
        return app
Ejemplo n.º 6
0
def _run(argv=None, exec=True):
    """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

    if argv is None:
        argv = sys.argv

    # parse args
    opts, args = parseArgs(argv)
    opts.base = opts.base or ""
    opts.profile = opts.profile or ""

    # profile manager
    from aqt.profiles import ProfileManager
    pm = ProfileManager(opts.base)

    # gl workarounds
    setupGL(pm)

    # opt in to full hidpi support?
    if not os.environ.get("ANKI_NOHIGHDPI"):
        QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)

    # create the app
    app = AnkiApp(argv)
    QCoreApplication.setApplicationName("Anki")
    if app.secondInstance():
        # we've signaled the primary instance, so we should close
        return

    # disable icons on mac; this must be done before window created
    if isMac:
        app.setAttribute(Qt.AA_DontShowIconsInMenus)

    # proxy configured?
    from urllib.request import proxy_bypass, getproxies
    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"):
            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, "Error", """\
No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
environment points to a valid, writable folder.""")
        return

    pm.setupMeta()

    if opts.profile:
        pm.openProfile(opts.profile)

    # i18n
    setupLang(pm, app, opts.lang)

    # remaining pm init
    pm.ensureProfile()

    # load the main window
    import aqt.main
    mw = aqt.main.AnkiQt(app, pm, opts, args)
    if exec:
        app.exec()
    else:
        return app