|
1 #!/usr/bin/env python3 |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # Copyright (c) 2002 - 2014 Detlev Offenbach <detlev@die-offenbachs.de> |
|
5 # |
|
6 |
|
7 """ |
|
8 Eric5 Python IDE. |
|
9 |
|
10 This is the main Python script that performs the necessary initialization |
|
11 of the IDE and starts the Qt event loop. |
|
12 """ |
|
13 |
|
14 from __future__ import unicode_literals |
|
15 |
|
16 try: # Only for Py2 |
|
17 import StringIO as io # __IGNORE_EXCEPTION__ |
|
18 import Utilities.compatibility_fixes # __IGNORE_WARNING__ |
|
19 except ImportError: |
|
20 import io # __IGNORE_WARNING__ |
|
21 basestring = str |
|
22 |
|
23 try: |
|
24 import sip |
|
25 sip.setdestroyonexit(False) |
|
26 except AttributeError: |
|
27 pass |
|
28 |
|
29 |
|
30 import sys |
|
31 import os |
|
32 import traceback |
|
33 import time |
|
34 import logging |
|
35 |
|
36 from PyQt5.QtCore import qWarning, QLibraryInfo, QTimer, QCoreApplication |
|
37 |
|
38 # some global variables needed to start the application |
|
39 args = None |
|
40 mainWindow = None |
|
41 splash = None |
|
42 |
|
43 # generate list of arguments to be remembered for a restart |
|
44 restartArgsList = ["--nosplash", "--plugin", "--debug", "--config"] |
|
45 restartArgs = [arg for arg in sys.argv[1:] |
|
46 if arg.split("=", 1)[0] in restartArgsList] |
|
47 |
|
48 if "--debug" in sys.argv: |
|
49 del sys.argv[sys.argv.index("--debug")] |
|
50 logging.basicConfig(level=logging.DEBUG) |
|
51 |
|
52 for arg in sys.argv: |
|
53 if arg.startswith("--config="): |
|
54 import Globals |
|
55 configDir = arg.replace("--config=", "") |
|
56 Globals.setConfigDir(configDir) |
|
57 sys.argv.remove(arg) |
|
58 break |
|
59 |
|
60 # make Third-Party package available as a packages repository |
|
61 sys.path.insert(2, os.path.join(os.path.dirname(__file__), |
|
62 "ThirdParty", "Pygments")) |
|
63 sys.path.insert(2, os.path.join(os.path.dirname(__file__), |
|
64 "ThirdParty", "Jasy")) |
|
65 |
|
66 from E5Gui.E5Application import E5Application |
|
67 |
|
68 |
|
69 def handleSingleApplication(ddindex): |
|
70 """ |
|
71 Global function to handle the single application mode. |
|
72 |
|
73 @param ddindex index of a '--' option in the options list |
|
74 """ |
|
75 from E5Gui.E5SingleApplication import E5SingleApplicationClient |
|
76 |
|
77 client = E5SingleApplicationClient() |
|
78 res = client.connect() |
|
79 if res > 0: |
|
80 if "--nosplash" in sys.argv and sys.argv.index("--nosplash") < ddindex: |
|
81 del sys.argv[sys.argv.index("--nosplash")] |
|
82 if "--noopen" in sys.argv and sys.argv.index("--noopen") < ddindex: |
|
83 del sys.argv[sys.argv.index("--noopen")] |
|
84 if "--debug" in sys.argv and sys.argv.index("--debug") < ddindex: |
|
85 del sys.argv[sys.argv.index("--debug")] |
|
86 for arg in sys.argv: |
|
87 if arg.startswith("--config="): |
|
88 sys.argv.remove(arg) |
|
89 break |
|
90 if len(sys.argv) > 1: |
|
91 client.processArgs(sys.argv[1:]) |
|
92 sys.exit(0) |
|
93 elif res < 0: |
|
94 print("eric5: {0}".format(client.errstr())) |
|
95 sys.exit(res) |
|
96 |
|
97 |
|
98 def excepthook(excType, excValue, tracebackobj): |
|
99 """ |
|
100 Global function to catch unhandled exceptions. |
|
101 |
|
102 @param excType exception type |
|
103 @param excValue exception value |
|
104 @param tracebackobj traceback object |
|
105 """ |
|
106 import xml.sax.saxutils |
|
107 from UI.Info import BugAddress |
|
108 import Utilities |
|
109 import Globals |
|
110 |
|
111 separator = '-' * 80 |
|
112 logFile = os.path.join(Globals.getConfigDir(), "eric5_error.log") |
|
113 notice = \ |
|
114 """An unhandled exception occurred. Please report the problem\n"""\ |
|
115 """using the error reporting dialog or via email to <{0}>.\n"""\ |
|
116 """A log has been written to "{1}".\n\nError information:\n""".format( |
|
117 BugAddress, logFile) |
|
118 timeString = time.strftime("%Y-%m-%d, %H:%M:%S") |
|
119 |
|
120 versionInfo = "\n{0}\n{1}".format( |
|
121 separator, Utilities.generateVersionInfo()) |
|
122 pluginVersionInfo = Utilities.generatePluginsVersionInfo() |
|
123 if pluginVersionInfo: |
|
124 versionInfo += "{0}\n{1}".format(separator, pluginVersionInfo) |
|
125 distroInfo = Utilities.generateDistroInfo() |
|
126 if distroInfo: |
|
127 versionInfo += "{0}\n{1}".format(separator, distroInfo) |
|
128 |
|
129 if isinstance(excType, basestring): |
|
130 tbinfo = tracebackobj |
|
131 else: |
|
132 tbinfofile = io.StringIO() |
|
133 traceback.print_tb(tracebackobj, None, tbinfofile) |
|
134 tbinfofile.seek(0) |
|
135 tbinfo = tbinfofile.read() |
|
136 errmsg = '{0}: \n{1}'.format(str(excType), str(excValue)) |
|
137 sections = [separator, timeString, separator, errmsg, separator, tbinfo] |
|
138 msg = '\n'.join(sections) |
|
139 try: |
|
140 f = open(logFile, "w", encoding="utf-8") |
|
141 f.write(msg) |
|
142 f.write(versionInfo) |
|
143 f.close() |
|
144 except IOError: |
|
145 pass |
|
146 |
|
147 warning = str(notice) + str(msg) + str(versionInfo) |
|
148 # Escape &<> otherwise it's not visible in the error dialog |
|
149 warning = xml.sax.saxutils.escape(warning) |
|
150 qWarning(warning) |
|
151 |
|
152 |
|
153 def uiStartUp(): |
|
154 """ |
|
155 Global function to finalize the start up of the main UI. |
|
156 |
|
157 Note: It is activated by a zero timeout single-shot timer. |
|
158 """ |
|
159 global args, mainWindow, splash |
|
160 |
|
161 if splash: |
|
162 splash.finish(mainWindow) |
|
163 del splash |
|
164 |
|
165 mainWindow.checkForErrorLog() |
|
166 mainWindow.processArgs(args) |
|
167 mainWindow.checkProjectsWorkspace() |
|
168 mainWindow.checkConfigurationStatus() |
|
169 mainWindow.performVersionCheck(False) |
|
170 mainWindow.checkPluginUpdatesAvailable() |
|
171 mainWindow.autoConnectIrc() |
|
172 |
|
173 |
|
174 def main(): |
|
175 """ |
|
176 Main entry point into the application. |
|
177 |
|
178 @exception Exception re-raised for any exception occurring in the main |
|
179 program logic |
|
180 """ |
|
181 from Globals import AppInfo |
|
182 import Globals |
|
183 |
|
184 global args, mainWindow, splash, restartArgs |
|
185 |
|
186 sys.excepthook = excepthook |
|
187 |
|
188 options = [ |
|
189 ("--config=configDir", |
|
190 "use the given directory as the one containing the config files"), |
|
191 ("--debug", "activate debugging output to the console"), |
|
192 ("--nosplash", "don't show the splash screen"), |
|
193 ("--noopen", |
|
194 "don't open anything at startup except that given in command"), |
|
195 ("--plugin=plugin-file", |
|
196 "load the given plugin file (plugin development)"), |
|
197 ("--start-file", "load the most recently opened file"), |
|
198 ("--start-multi", "load the most recently opened multi-project"), |
|
199 ("--start-project", "load the most recently opened project"), |
|
200 ("--start-session", "load the global session file"), |
|
201 ("--", |
|
202 "indicate that there are options for the program to be debugged"), |
|
203 ("", |
|
204 "(everything after that is considered arguments for this program)") |
|
205 ] |
|
206 appinfo = AppInfo.makeAppInfo(sys.argv, |
|
207 "Eric5", |
|
208 "[project | files... [--] [debug-options]]", |
|
209 "A Python IDE", |
|
210 options) |
|
211 |
|
212 if not Globals.checkBlacklistedVersions(): |
|
213 sys.exit(100) |
|
214 |
|
215 app = E5Application(sys.argv) |
|
216 |
|
217 import Utilities |
|
218 from Toolbox import Startup |
|
219 |
|
220 ddindex = Startup.handleArgs(sys.argv, appinfo) |
|
221 |
|
222 logging.debug("Importing Preferences") |
|
223 import Preferences |
|
224 |
|
225 if Preferences.getUI("SingleApplicationMode"): |
|
226 handleSingleApplication(ddindex) |
|
227 |
|
228 # set the library paths for plugins |
|
229 Startup.setLibraryPaths() |
|
230 |
|
231 # set the search path for icons |
|
232 Startup.initializeResourceSearchPath() |
|
233 |
|
234 # generate and show a splash window, if not suppressed |
|
235 from UI.SplashScreen import SplashScreen, NoneSplashScreen |
|
236 if "--nosplash" in sys.argv and sys.argv.index("--nosplash") < ddindex: |
|
237 del sys.argv[sys.argv.index("--nosplash")] |
|
238 splash = NoneSplashScreen() |
|
239 elif not Preferences.getUI("ShowSplash"): |
|
240 splash = NoneSplashScreen() |
|
241 else: |
|
242 splash = SplashScreen() |
|
243 |
|
244 # modify the executable search path for the PyQt5 installer |
|
245 if Globals.isWindowsPlatform(): |
|
246 pyqtDataDir = Globals.getPyQt5ModulesDirectory() |
|
247 if os.path.exists(os.path.join(pyqtDataDir, "bin")): |
|
248 path = os.path.join(pyqtDataDir, "bin") + \ |
|
249 os.pathsep + os.environ["PATH"] |
|
250 else: |
|
251 path = pyqtDataDir + os.pathsep + os.environ["PATH"] |
|
252 os.environ["PATH"] = path |
|
253 |
|
254 pluginFile = None |
|
255 noopen = False |
|
256 if "--noopen" in sys.argv and sys.argv.index("--noopen") < ddindex: |
|
257 del sys.argv[sys.argv.index("--noopen")] |
|
258 noopen = True |
|
259 for arg in sys.argv: |
|
260 if arg.startswith("--plugin=") and sys.argv.index(arg) < ddindex: |
|
261 # extract the plugin development option |
|
262 pluginFile = arg.replace("--plugin=", "").replace('"', "") |
|
263 sys.argv.remove(arg) |
|
264 pluginFile = os.path.expanduser(pluginFile) |
|
265 pluginFile = Utilities.normabspath(pluginFile) |
|
266 break |
|
267 |
|
268 # is there a set of filenames or options on the command line, |
|
269 # if so, pass them to the UI |
|
270 if len(sys.argv) > 1: |
|
271 args = sys.argv[1:] |
|
272 |
|
273 # get the Qt4 translations directory |
|
274 qt4TransDir = Preferences.getQt4TranslationsDir() |
|
275 if not qt4TransDir: |
|
276 qt4TransDir = QLibraryInfo.location(QLibraryInfo.TranslationsPath) |
|
277 |
|
278 # Load translation files and install them |
|
279 loc = Startup.loadTranslators(qt4TransDir, app, ("qscintilla",)) |
|
280 |
|
281 splash.showMessage(QCoreApplication.translate("eric5", "Starting...")) |
|
282 # We can only import these after creating the E5Application because they |
|
283 # make Qt calls that need the E5Application to exist. |
|
284 from UI.UserInterface import UserInterface |
|
285 |
|
286 splash.showMessage( |
|
287 QCoreApplication.translate("eric5", "Generating Main Window...")) |
|
288 try: |
|
289 mainWindow = UserInterface(app, loc, splash, pluginFile, noopen, |
|
290 restartArgs) |
|
291 app.lastWindowClosed.connect(app.quit) |
|
292 mainWindow.show() |
|
293 |
|
294 QTimer.singleShot(0, uiStartUp) |
|
295 |
|
296 # generate a graphical error handler |
|
297 from E5Gui import E5ErrorMessage |
|
298 eMsg = E5ErrorMessage.qtHandler() |
|
299 eMsg.setMinimumSize(600, 400) |
|
300 |
|
301 # start the event loop |
|
302 res = app.exec_() |
|
303 logging.debug("Shutting down, result {0:d}".format(res)) |
|
304 logging.shutdown() |
|
305 sys.exit(res) |
|
306 except Exception as err: |
|
307 raise err |
|
308 |
|
309 if __name__ == '__main__': |
|
310 main() |