28
28
"""
29
29
30
30
import atexit
31
+ import sys
31
32
import os
32
33
import platform
33
- import sys
34
34
import traceback
35
35
import json
36
36
40
40
from PyQt5 .QtGui import QPalette , QColor , QFontDatabase , QFont
41
41
from PyQt5 .QtWidgets import QApplication , QStyleFactory , QMessageBox
42
42
43
- try :
44
- # This apparently has to be done before loading QtQuick
45
- # (via QtWebEgine) AND before creating the QApplication instance
46
- QApplication .setAttribute (Qt .AA_ShareOpenGLContexts )
47
- from OpenGL import GL # noqa
48
- except (ImportError , AttributeError ):
49
- pass
50
-
51
- try :
52
- # QtWebEngineWidgets must be loaded prior to creating a QApplication
53
- # But on systems with only WebKit, this will fail (and we ignore the failure)
54
- from PyQt5 .QtWebEngineWidgets import QWebEngineView
55
- except ImportError :
56
- pass
57
43
58
44
def get_app ():
59
45
""" Get the current QApplication instance of OpenShot """
@@ -64,12 +50,26 @@ def get_settings():
64
50
"""Get a reference to the app's settings object"""
65
51
return get_app ().get_settings ()
66
52
67
- try :
68
- # Enable High-DPI resolutions
69
- QApplication .setAttribute (Qt .AA_EnableHighDpiScaling )
70
- except AttributeError :
71
- pass # Quietly fail for older Qt5 versions
72
53
54
+ class StartupError :
55
+ """ Store and later display an error encountered during setup"""
56
+ levels = {
57
+ "warning" : QMessageBox .warning ,
58
+ "error" : QMessageBox .critical ,
59
+ }
60
+
61
+ def __init__ (self , title = "" , message = "" , level = "warning" ):
62
+ """Create an error message object, populated with details"""
63
+ self .title = title
64
+ self .message = message
65
+ self .level = level
66
+
67
+ def show (self ):
68
+ """Display the stored error message"""
69
+ box_call = self .levels [self .level ]
70
+ box_call (None , self .title , self .message )
71
+ if self .level == "error" :
72
+ sys .exit ()
73
73
74
74
75
75
class OpenShotApp (QApplication ):
@@ -78,42 +78,71 @@ class OpenShotApp(QApplication):
78
78
79
79
def __init__ (self , * args , mode = None ):
80
80
QApplication .__init__ (self , * args )
81
+ self .mode = mode or "normal"
82
+ self .args = list (* args )
83
+ self .errors = []
81
84
82
85
try :
83
86
# Import modules
84
87
from classes import info
85
88
from classes .logger import log , reroute_output
86
89
87
90
# Log the session's start
88
- import time
89
- log .info ("------------------------------------------------" )
90
- log .info (time .asctime ().center (48 ))
91
- log .info ('Starting new session' .center (48 ))
92
-
93
- from classes import (
94
- settings , project_data , updates , language , ui_util ,
95
- logger_libopenshot
96
- )
91
+ if mode != "unittest" :
92
+ import time
93
+ log .info ("-" * 48 )
94
+ log .info (time .asctime ().center (48 ))
95
+ log .info ('Starting new session' .center (48 ))
96
+
97
+ log .debug ("Starting up in {} mode" .format (self .mode ))
98
+ log .debug ("Command line: {}" .format (self .args ))
99
+
100
+ from classes import settings , project_data , updates
97
101
import openshot
98
102
99
103
# Re-route stdout and stderr to logger
100
- reroute_output ()
104
+ if mode != "unittest" :
105
+ reroute_output ()
106
+
101
107
except ImportError as ex :
102
108
tb = traceback .format_exc ()
103
109
log .error ('OpenShotApp::Import Error' , exc_info = 1 )
104
- QMessageBox .warning (None , "Import Error" ,
105
- "Module: %(name)s\n \n %(tb)s" % {"name" : ex .name , "tb" : tb })
106
- # Stop launching and exit
110
+ self .errors .append (StartupError (
111
+ "Import Error" ,
112
+ "Module: %(name)s\n \n %(tb)s" % {"name" : ex .name , "tb" : tb },
113
+ level = "error" ))
114
+ # Stop launching
107
115
raise
108
116
except Exception :
109
117
log .error ('OpenShotApp::Init Error' , exc_info = 1 )
110
118
sys .exit ()
111
119
120
+ self .info = info
121
+
112
122
# Log some basic system info
123
+ self .log = log
124
+ self .show_environment (info , openshot )
125
+ if self .mode != "unittest" :
126
+ self .check_libopenshot_version (info , openshot )
127
+
128
+ # Init data objects
129
+ self .settings = settings .SettingStore (parent = self )
130
+ self .settings .load ()
131
+ self .project = project_data .ProjectDataStore ()
132
+ self .updates = updates .UpdateManager ()
133
+ # It is important that the project is the first listener if the key gets update
134
+ self .updates .add_listener (self .project )
135
+ self .updates .reset ()
136
+
137
+ # Set location of OpenShot program (for libopenshot)
138
+ openshot .Settings .Instance ().PATH_OPENSHOT_INSTALL = info .PATH
139
+
140
+ def show_environment (self , info , openshot ):
141
+ log = self .log
113
142
try :
114
- log .info ("------------------------------------------------" )
143
+ log .info ("-" * 48 )
115
144
log .info (("OpenShot (version %s)" % info .SETUP ['version' ]).center (48 ))
116
- log .info ("------------------------------------------------" )
145
+ log .info ("-" * 48 )
117
146
118
147
log .info ("openshot-qt version: %s" % info .VERSION )
119
148
log .info ("libopenshot version: %s" % openshot .OPENSHOT_VERSION_FULL )
@@ -135,60 +164,47 @@ def __init__(self, *args, mode=None):
135
164
except Exception :
136
165
log .debug ("Error displaying dependency/system details" , exc_info = 1 )
137
166
138
- # Setup application
139
- self .setApplicationName ('openshot' )
140
- self .setApplicationVersion (info .SETUP ['version' ])
141
- try :
142
- # Qt 5.7+ only
143
- self .setDesktopFile ("org.openshot.OpenShot" )
144
- except AttributeError :
145
- pass
146
-
147
- # Init settings
148
- self .settings = settings .SettingStore (parent = self )
149
- self .settings .load ()
150
-
151
167
# Init and attach exception handler
152
168
from classes import exceptions
153
169
sys .excepthook = exceptions .ExceptionHandler
154
170
155
- # Init translation system
156
- language .init_language ()
157
-
158
- # Detect minimum libopenshot version
171
+ def check_libopenshot_version (self , info , openshot ):
172
+ """Detect minimum libopenshot version"""
159
173
_ = self ._tr
160
- libopenshot_version = openshot .OPENSHOT_VERSION_FULL
161
- if mode != "unittest" and libopenshot_version < info .MINIMUM_LIBOPENSHOT_VERSION :
162
- QMessageBox . warning (
163
- None ,
164
- _ ( "Wrong Version of libopenshot Detected" ),
165
- _ ( "<b>Version %(minimum_version)s is required</b>, "
166
- "but %(current_version)s was detected. "
167
- "Please update libopenshot or download our latest installer." )
168
- % {
169
- "minimum_version" : info . MINIMUM_LIBOPENSHOT_VERSION ,
170
- "current_version " : libopenshot_version ,
171
- })
172
- # Stop launching and exit
173
- sys . exit ()
174
-
175
- # Set location of OpenShot program (for libopenshot)
176
- openshot . Settings . Instance (). PATH_OPENSHOT_INSTALL = info . PATH
177
-
178
- # Tests of project data loading/saving
179
- self . project = project_data . ProjectDataStore ()
174
+ ver = openshot .OPENSHOT_VERSION_FULL
175
+ min = info .MINIMUM_LIBOPENSHOT_VERSION
176
+ if ver >= min :
177
+ return True
178
+
179
+ self . errors . append ( StartupError (
180
+ _ ( "Wrong Version of libopenshot Detected" ),
181
+ _ ( "<b>Version %(minimum_version)s is required</b>, "
182
+ "but %(current_version)s was detected. "
183
+ "Please update libopenshot or download our latest installer." ) % {
184
+ "minimum_version " : min ,
185
+ "current_version" : ver ,
186
+ },
187
+ level = "error" ,
188
+ ))
189
+ raise RuntimeError (
190
+ "libopenshot version {} found, minimum is {}" . format ( ver , min ))
191
+
192
+ def gui ( self ):
193
+ from classes import language , ui_util , logger_libopenshot
180
194
181
- # Init Update Manager
182
- self .updates = updates .UpdateManager ()
195
+ _ = self ._tr
196
+ info = self .info
197
+ log = self .log
183
198
184
- # It is important that the project is the first listener if the key gets update
185
- self . updates . add_listener ( self . project )
199
+ # Init translation system
200
+ language . init_language ( )
186
201
187
202
# Load ui theme if not set by OS
188
203
ui_util .load_theme ()
189
204
190
205
# Test for permission issues (and display message if needed)
191
206
try :
207
+ log .debug ("Testing write access to user directory" )
192
208
# Create test paths
193
209
TEST_PATH_DIR = os .path .join (info .USER_PATH , 'PERMISSION' )
194
210
TEST_PATH_FILE = os .path .join (TEST_PATH_DIR , 'test.osp' )
@@ -201,15 +217,17 @@ def __init__(self, *args, mode=None):
201
217
os .rmdir (TEST_PATH_DIR )
202
218
except PermissionError as ex :
203
219
log .error ('Failed to create file %s' , TEST_PATH_FILE , exc_info = 1 )
204
- QMessageBox . warning (
205
- None , _ ("Permission Error" ),
206
- _ ("%(error)s. Please delete <b>%(path)s</b> and launch OpenShot again." % {
220
+ self . errors . append ( StartupError (
221
+ _ ("Permission Error" ),
222
+ _ ("%(error)s. Please delete <b>%(path)s</b> and launch OpenShot again." ) % {
207
223
"error" : str (ex ),
208
224
"path" : info .USER_PATH ,
209
- }))
210
- # Stop launching and exit
211
- raise
212
- sys .exit ()
225
+ },
226
+ level = "error" ,
227
+ ))
228
+
229
+ # Display any outstanding startup messages
230
+ self .show_errors ()
213
231
214
232
# Start libopenshot logging thread
215
233
self .logger_libopenshot = logger_libopenshot .LoggerLibOpenShot ()
@@ -219,6 +237,7 @@ def __init__(self, *args, mode=None):
219
237
self .context_menu_object = None
220
238
221
239
# Set Font for any theme
240
+ log .debug ("Loading UI theme" )
222
241
if self .settings .get ("theme" ) != "No Theme" :
223
242
# Load embedded font
224
243
try :
@@ -269,72 +288,77 @@ def __init__(self, *args, mode=None):
269
288
270
289
# Create main window
271
290
from windows .main_window import MainWindow
272
- self .window = MainWindow (mode = mode )
291
+ log .debug ("Creating main interface window" )
292
+ self .window = MainWindow (mode = self .mode )
273
293
274
- # Reset undo/redo history
275
- self .updates .reset ()
294
+ # Clear undo/redo history
276
295
self .window .updateStatusChanged (False , False )
277
296
278
297
# Connect our exit signals
279
298
self .aboutToQuit .connect (self .cleanup )
280
299
281
- log .info ('Process command-line arguments: %s' % args )
282
- if len (args [0 ]) == 2 :
283
- path = args [0 ][1 ]
284
- if ".osp" in path :
285
- # Auto load project passed as argument
286
- self .window .OpenProjectSignal .emit (path )
287
- else :
288
- # Apply the default settings and Auto import media file
289
- self .project .load ("" )
290
- self .window .filesView .add_file (path )
291
- else :
300
+ args = self .args
301
+ if len (args ) < 2 :
292
302
# Recover backup file (this can't happen until after the Main Window has completely loaded)
293
303
self .window .RecoverBackup .emit ()
304
+ return
305
+
306
+ log .info ('Process command-line arguments: %s' % args )
307
+
308
+ # Auto load project if passed as argument
309
+ if args [1 ].endswith (".osp" ):
310
+ self .window .OpenProjectSignal .emit (args [1 ])
311
+ return
312
+
313
+ # Start a new project and auto import any media files
314
+ self .project .load ("" )
315
+ for arg in args [1 :]:
316
+ self .window .filesView .add_file (arg )
294
317
295
318
def settings_load_error (self , filepath = None ):
296
319
"""Use QMessageBox to warn the user of a settings load issue"""
297
320
_ = self ._tr
298
- QMessageBox .warning (
299
- None ,
321
+ self .errors .append (StartupError (
300
322
_ ("Settings Error" ),
301
- _ ("Error loading settings file: %(file_path)s. Settings will be reset." )
302
- % {"file_path" : filepath }
303
- )
323
+ _ ("Error loading settings file: %(file_path)s. Settings will be reset." ) % {
324
+ "file_path" : filepath
325
+ },
326
+ level = "warning" ,
327
+ ))
304
328
305
329
def get_settings (self ):
306
330
if not hasattr (self , "settings" ):
307
331
return None
308
332
return self .settings
309
333
334
+ def show_errors (self ):
335
+ count = len (self .errors )
336
+ if count > 0 :
337
+ self .log .warning ("Displaying {} startup messages" .format (count ))
338
+ for error in self .errors :
339
+ error .show ()
340
+
310
341
def _tr (self , message ):
311
342
return self .translate ("" , message )
312
343
313
- # Start event loop
314
- def run (self ):
315
- """ Start the primary Qt event loop for the interface """
316
- return self .exec_ ()
317
-
318
344
@pyqtSlot ()
319
345
def cleanup (self ):
320
346
"""aboutToQuit signal handler for application exit"""
321
347
try :
322
- from classes .logger import log
323
348
self .settings .save ()
324
349
except Exception :
325
- log .error ("Couldn't save user settings on exit." , exc_info = 1 )
350
+ self . log .error ("Couldn't save user settings on exit." , exc_info = 1 )
326
351
327
352
328
- # Log the session's end
329
353
@atexit .register
330
354
def onLogTheEnd ():
331
355
""" Log when the primary Qt event loop ends """
332
356
try :
333
357
from classes .logger import log
334
358
import time
335
- log .info (' OpenShot\ ' s session ended' .center (48 ))
359
+ log .info (" OpenShot's session ended" .center (48 ))
336
360
log .info (time .asctime ().center (48 ))
337
- log .info ("================================================" )
361
+ log .info ("=" * 48 )
338
362
except Exception :
339
- from classes . logger import log
363
+ from logging import log
340
364
log .debug ('Failed to write session ended log' )
0 commit comments