src/eric7/eric7_ide.py

branch
eric7
changeset 9377
b9c8dc3b7da1
parent 9348
f61d71d95cb1
child 9399
fcf9b48d8a0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/eric7_ide.py	Sun Oct 02 11:29:11 2022 +0200
@@ -0,0 +1,434 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+eric Python IDE.
+
+This is the main Python script that performs the necessary initialization
+of the IDE and starts the Qt event loop.
+"""
+
+import contextlib
+import io
+import logging
+import multiprocessing
+import os
+import sys
+import traceback
+import time
+
+originalPathString = os.getenv("PATH")
+
+# generate list of arguments to be remembered for a restart
+restartArgsList = [
+    "--no-splash",
+    "--plugin",
+    "--debug",
+    "--config",
+    "--settings",
+    "--disable-crash",
+    "--disable-plugin",
+]
+restartArgs = [arg for arg in sys.argv[1:] if arg.split("=", 1)[0] in restartArgsList]
+
+sys.path.insert(1, os.path.dirname(__file__))
+
+try:
+    from PyQt6.QtCore import qWarning, QLibraryInfo, QTimer, QCoreApplication
+except ImportError:
+    try:
+        from tkinter import messagebox
+    except ImportError:
+        sys.exit(100)
+    messagebox.showerror(
+        "eric7 Error",
+        "PyQt could not be imported. Please make sure"
+        " it is installed and accessible.",
+    )
+    sys.exit(100)
+
+try:
+    from PyQt6 import QtWebEngineWidgets  # __IGNORE_WARNING__ __IGNORE_EXCEPTION__
+    from PyQt6.QtWebEngineCore import QWebEngineUrlScheme
+
+    WEBENGINE_AVAILABLE = True
+except ImportError:
+    WEBENGINE_AVAILABLE = False
+
+# some global variables needed to start the application
+args = None
+mainWindow = None
+splash = None
+inMainLoop = False
+app = None
+
+if "--debug" in sys.argv:
+    del sys.argv[sys.argv.index("--debug")]
+    logging.basicConfig(level=logging.DEBUG)
+
+for arg in sys.argv[:]:
+    if arg.startswith("--config="):
+        import Globals
+
+        configDir = arg.replace("--config=", "")
+        Globals.setConfigDir(configDir)
+        sys.argv.remove(arg)
+    elif arg.startswith("--settings="):
+        from PyQt6.QtCore import QSettings
+
+        settingsDir = os.path.expanduser(arg.replace("--settings=", ""))
+        if not os.path.isdir(settingsDir):
+            os.makedirs(settingsDir)
+        QSettings.setPath(
+            QSettings.Format.IniFormat, QSettings.Scope.UserScope, settingsDir
+        )
+        sys.argv.remove(arg)
+
+# make Third-Party package available as a packages repository
+sys.path.insert(2, os.path.join(os.path.dirname(__file__), "ThirdParty", "Jasy"))
+sys.path.insert(2, os.path.join(os.path.dirname(__file__), "DebugClients", "Python"))
+
+from EricWidgets.EricApplication import EricApplication
+
+
+def handleSingleApplication(ddindex):
+    """
+    Global function to handle the single application mode.
+
+    @param ddindex index of a '--' option in the options list
+    """
+    from EricWidgets.EricSingleApplication import EricSingleApplicationClient
+
+    client = EricSingleApplicationClient()
+    res = client.connect()
+    if res > 0:
+        if "--no-splash" in sys.argv and sys.argv.index("--no-splash") < ddindex:
+            sys.argv.remove("--no-splash")
+            ddindex -= 1
+        if "--no-open" in sys.argv and sys.argv.index("--no-open") < ddindex:
+            sys.argv.remove("--no-open")
+            ddindex -= 1
+        if "--no-crash" in sys.argv and sys.argv.index("--no-crash") < ddindex:
+            sys.argv.remove("--no-crash")
+        if (
+            "--disable-crash" in sys.argv
+            and sys.argv.index("--disable-crash") < ddindex
+        ):
+            sys.argv.remove("--disable-crash")
+            ddindex -= 1
+        if "--debug" in sys.argv and sys.argv.index("--debug") < ddindex:
+            sys.argv.remove("--debug")
+            ddindex -= 1
+        for arg in sys.argv:
+            if arg.startswith("--config=") and sys.argv.index(arg) < ddindex:
+                sys.argv.remove(arg)
+                ddindex -= 1
+                break
+        for arg in sys.argv:
+            if arg.startswith("--plugin=") and sys.argv.index(arg) < ddindex:
+                sys.argv.remove(arg)
+                ddindex -= 1
+                break
+        for arg in sys.argv[:]:
+            if arg.startswith("--disable-plugin=") and sys.argv.index(arg) < ddindex:
+                sys.argv.remove(arg)
+                ddindex -= 1
+
+        if len(sys.argv) > 1:
+            client.processArgs(sys.argv[1:])
+        sys.exit(0)
+    elif res < 0:
+        print("eric7: {0}".format(client.errstr()))
+        # __IGNORE_WARNING_M801__
+        sys.exit(res)
+
+
+def excepthook(excType, excValue, tracebackobj):
+    """
+    Global function to catch unhandled exceptions.
+
+    @param excType exception type
+    @param excValue exception value
+    @param tracebackobj traceback object
+    """
+    from UI.Info import BugAddress
+    import Utilities
+    import Globals
+
+    # Workaround for a strange issue with QScintilla
+    if str(excValue) == "unable to convert a QVariant back to a Python object":
+        return
+
+    separator = "-" * 80
+    logFile = os.path.join(Globals.getConfigDir(), "eric7_error.log")
+    notice = (
+        """An unhandled exception occurred. Please report the problem\n"""
+        """using the error reporting dialog or via email to <{0}>.\n"""
+        """A log has been written to "{1}".\n\nError information:\n""".format(
+            BugAddress, logFile
+        )
+    )
+    timeString = time.strftime("%Y-%m-%d, %H:%M:%S")
+
+    versionInfo = "\n{0}\n{1}".format(separator, Utilities.generateVersionInfo())
+    pluginVersionInfo = Utilities.generatePluginsVersionInfo()
+    if pluginVersionInfo:
+        versionInfo += "\n{0}\n{1}".format(separator, pluginVersionInfo)
+    distroInfo = Utilities.generateDistroInfo()
+    if distroInfo:
+        versionInfo += "\n{0}\n{1}".format(separator, distroInfo)
+
+    if isinstance(excType, str):
+        tbinfo = tracebackobj
+    else:
+        tbinfofile = io.StringIO()
+        traceback.print_tb(tracebackobj, None, tbinfofile)
+        tbinfofile.seek(0)
+        tbinfo = tbinfofile.read()
+    errmsg = "{0}: \n{1}".format(str(excType), str(excValue))
+    sections = ["", separator, timeString, separator, errmsg, separator, tbinfo]
+    msg = "\n".join(sections)
+    with contextlib.suppress(OSError), open(logFile, "w", encoding="utf-8") as f:
+        f.write(msg)
+        f.write(versionInfo)
+
+    if inMainLoop:
+        warning = notice + msg + versionInfo
+        # Escape &<> otherwise it's not visible in the error dialog
+        warning = (
+            warning.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
+        )
+        qWarning(warning)
+    else:
+        warning = notice + msg + versionInfo
+        print(warning)  # __IGNORE_WARNING_M801__
+
+
+def uiStartUp():
+    """
+    Global function to finalize the start up of the main UI.
+
+    Note: It is activated by a zero timeout single-shot timer.
+    """
+    global args, mainWindow, splash
+
+    if splash:
+        splash.finish(mainWindow)
+        del splash
+
+    mainWindow.checkForErrorLog()
+    mainWindow.processArgs(args)
+    mainWindow.processInstallInfoFile()
+    mainWindow.checkProjectsWorkspace()
+    mainWindow.checkConfigurationStatus()
+    mainWindow.performVersionCheck()
+    mainWindow.checkPluginUpdatesAvailable()
+    mainWindow.autoConnectIrc()
+
+
+def main():
+    """
+    Main entry point into the application.
+    """
+    from Globals import AppInfo
+    import Globals
+
+    global app, args, mainWindow, splash, restartArgs, inMainLoop
+
+    sys.excepthook = excepthook
+    multiprocessing.set_start_method("spawn")
+
+    from PyQt6.QtGui import QGuiApplication
+
+    QGuiApplication.setDesktopFileName("eric7.desktop")
+
+    options = [
+        (
+            "--config=configDir",
+            "use the given directory as the one containing the config files",
+        ),
+        ("--debug", "activate debugging output to the console"),
+        ("--no-splash", "don't show the splash screen"),
+        ("--no-open", "don't open anything at startup except that given in command"),
+        ("--no-crash", "don't check for a crash session file on startup"),
+        ("--disable-crash", "disable the support for crash sessions"),
+        (
+            "--disable-plugin=<plug-in name>",
+            "disable the given plug-in (may be repeated)",
+        ),
+        ("--plugin=plugin-file", "load the given plugin file (plugin development)"),
+        (
+            "--settings=settingsDir",
+            "use the given directory to store the settings files",
+        ),
+        ("--small-screen", "adjust the interface for screens smaller than FHD"),
+        ("--start-file", "load the most recently opened file"),
+        ("--start-multi", "load the most recently opened multi-project"),
+        ("--start-project", "load the most recently opened project"),
+        ("--start-session", "load the global session file"),
+        ("--", "indicate that there are options for the program to be debugged"),
+        ("", "(everything after that is considered arguments for this program)"),
+    ]
+    appinfo = AppInfo.makeAppInfo(
+        sys.argv,
+        "Eric7",
+        "[project | files... [--] [debug-options]]",
+        "A Python IDE",
+        options,
+    )
+
+    if "__PYVENV_LAUNCHER__" in os.environ:
+        del os.environ["__PYVENV_LAUNCHER__"]
+
+    # make sure our executable directory (i.e. that of the used Python
+    # interpreter) is included in the executable search path
+    pathList = os.environ["PATH"].split(os.pathsep)
+    exeDir = os.path.dirname(sys.executable)
+    if exeDir not in pathList:
+        pathList.insert(0, exeDir)
+    os.environ["PATH"] = os.pathsep.join(pathList)
+
+    from Toolbox import Startup
+
+    # set the library paths for plugins
+    Startup.setLibraryPaths()
+
+    if WEBENGINE_AVAILABLE:
+        scheme = QWebEngineUrlScheme(b"qthelp")
+        scheme.setSyntax(QWebEngineUrlScheme.Syntax.Path)
+        scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme)
+        QWebEngineUrlScheme.registerScheme(scheme)
+
+    app = EricApplication(sys.argv)
+    ddindex = Startup.handleArgs(sys.argv, appinfo)
+
+    logging.debug("Importing Preferences")
+    import Preferences
+
+    if Preferences.getUI("SingleApplicationMode"):
+        handleSingleApplication(ddindex)
+
+    # set the application style sheet
+    app.setStyleSheetFile(Preferences.getUI("StyleSheet"))
+
+    # set the search path for icons
+    Startup.initializeResourceSearchPath(app)
+
+    # generate and show a splash window, if not suppressed
+    from UI.SplashScreen import SplashScreen, NoneSplashScreen
+
+    if "--no-splash" in sys.argv and sys.argv.index("--no-splash") < ddindex:
+        sys.argv.remove("--no-splash")
+        ddindex -= 1
+        splash = NoneSplashScreen()
+    elif not Preferences.getUI("ShowSplash"):
+        splash = NoneSplashScreen()
+    else:
+        splash = SplashScreen()
+    QCoreApplication.processEvents()
+
+    # modify the executable search path for the PyQt5 installer
+    if Globals.isWindowsPlatform():
+        pyqtDataDir = Globals.getPyQt6ModulesDirectory()
+        if os.path.exists(os.path.join(pyqtDataDir, "bin")):
+            path = os.path.join(pyqtDataDir, "bin")
+        else:
+            path = pyqtDataDir
+        os.environ["PATH"] = path + os.pathsep + os.environ["PATH"]
+
+    pluginFile = None
+    noopen = False
+    nocrash = False
+    disablecrash = False
+    disabledPlugins = []
+    if "--no-open" in sys.argv and sys.argv.index("--no-open") < ddindex:
+        sys.argv.remove("--no-open")
+        ddindex -= 1
+        noopen = True
+    if "--no-crash" in sys.argv and sys.argv.index("--no-crash") < ddindex:
+        sys.argv.remove("--no-crash")
+        ddindex -= 1
+        nocrash = True
+    if "--disable-crash" in sys.argv and sys.argv.index("--disable-crash") < ddindex:
+        sys.argv.remove("--disable-crash")
+        ddindex -= 1
+        disablecrash = True
+    for arg in sys.argv[:]:
+        if arg.startswith("--disable-plugin=") and sys.argv.index(arg) < ddindex:
+            # extract the plug-in name
+            pluginName = arg.replace("--disable-plugin=", "")
+            sys.argv.remove(arg)
+            ddindex -= 1
+            disabledPlugins.append(pluginName)
+    for arg in sys.argv:
+        if arg.startswith("--plugin=") and sys.argv.index(arg) < ddindex:
+            # extract the plugin development option
+            pluginFile = arg.replace("--plugin=", "").replace('"', "")
+            sys.argv.remove(arg)
+            ddindex -= 1
+            pluginFile = os.path.expanduser(pluginFile)
+            pluginFile = os.path.abspath(pluginFile)
+            break
+
+    # is there a set of filenames or options on the command line,
+    # if so, pass them to the UI
+    if len(sys.argv) > 1:
+        args = sys.argv[1:]
+
+    # get the Qt translations directory
+    qtTransDir = Preferences.getQtTranslationsDir()
+    if not qtTransDir:
+        qtTransDir = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)
+
+    # Load translation files and install them
+    loc = Startup.loadTranslators(qtTransDir, app, ("qscintilla",))
+
+    # Initialize SSL stuff
+    from EricNetwork.EricSslUtilities import initSSL
+
+    initSSL()
+
+    splash.showMessage(QCoreApplication.translate("eric7_ide", "Starting..."))
+    # We can only import these after creating the EricApplication because they
+    # make Qt calls that need the EricApplication to exist.
+    from UI.UserInterface import UserInterface
+
+    splash.showMessage(
+        QCoreApplication.translate("eric7_ide", "Generating Main Window...")
+    )
+    mainWindow = UserInterface(
+        app,
+        loc,
+        splash,
+        pluginFile,
+        disabledPlugins,
+        noopen,
+        nocrash,
+        disablecrash,
+        restartArgs,
+        originalPathString,
+    )
+    app.lastWindowClosed.connect(app.quit)
+    mainWindow.show()
+
+    QTimer.singleShot(0, uiStartUp)
+
+    # generate a graphical error handler
+    from EricWidgets import EricErrorMessage
+
+    eMsg = EricErrorMessage.qtHandler()
+    eMsg.setMinimumSize(600, 400)
+
+    # start the event loop
+    inMainLoop = True
+    res = app.exec()
+    logging.debug("Shutting down, result %d", res)
+    logging.shutdown()
+    sys.exit(res)
+
+
+if __name__ == "__main__":
+    main()

eric ide

mercurial