source: orange/Orange/OrangeCanvas/main.py @ 11532:68ae91b09f62

Revision 11532:68ae91b09f62, 9.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 11 months ago (diff)

Set the application version from the package info and display it in the splash screen.

Line 
1"""
2Orange Canvas main entry point
3
4"""
5
6import os
7import sys
8import gc
9import re
10import logging
11import optparse
12import cPickle
13from contextlib import nested
14
15import pkg_resources
16
17from PyQt4.QtGui import QFont, QColor
18from PyQt4.QtCore import Qt, QDir
19
20from Orange import OrangeCanvas
21from Orange.OrangeCanvas.application.application import CanvasApplication
22from Orange.OrangeCanvas.application.canvasmain import CanvasMainWindow
23from Orange.OrangeCanvas.application.outputview import TextStream, ExceptHook
24
25from Orange.OrangeCanvas.gui.splashscreen import SplashScreen
26from Orange.OrangeCanvas.config import cache_dir
27from Orange.OrangeCanvas import config
28from Orange.OrangeCanvas.utils.redirect import redirect_stdout, redirect_stderr
29from Orange.OrangeCanvas.utils.qtcompat import QSettings
30
31from Orange.OrangeCanvas.registry import qt
32from Orange.OrangeCanvas.registry import WidgetRegistry, set_global_registry
33from Orange.OrangeCanvas.registry import cache
34
35log = logging.getLogger(__name__)
36
37
38def running_in_ipython():
39    try:
40        __IPYTHON__
41        return True
42    except NameError:
43        return False
44
45
46def fix_win_pythonw_std_stream():
47    """
48    On windows when running without a console (using pythonw.exe) the
49    std[err|out] file descriptors are invalid and start throwing exceptions
50    when their buffer is flushed (`http://bugs.python.org/issue706263`_)
51
52    """
53    if sys.platform == "win32" and \
54            os.path.basename(sys.executable) == "pythonw.exe":
55        if sys.stdout.fileno() < 0:
56            sys.stdout = open(os.devnull, "wb")
57        if sys.stderr.fileno() < 0:
58            sys.stderr = open(os.devnull, "wb")
59
60
61def main(argv=None):
62    if argv is None:
63        argv = sys.argv[1:]
64
65    usage = "usage: %prog [options] [scheme_file]"
66    parser = optparse.OptionParser(usage=usage)
67
68    parser.add_option("--no-discovery",
69                      action="store_true",
70                      help="Don't run widget discovery "
71                           "(use full cache instead)")
72
73    parser.add_option("--force-discovery",
74                      action="store_true",
75                      help="Force full widget discovery "
76                           "(invalidate cache)")
77    parser.add_option("--no-welcome",
78                      action="store_true",
79                      help="Don't show welcome dialog.")
80    parser.add_option("--no-splash",
81                      action="store_true",
82                      help="Don't show splash screen.")
83    parser.add_option("-l", "--log-level",
84                      help="Logging level (0, 1, 2, 3, 4)",
85                      type="int", default=1)
86    parser.add_option("--no-redirect",
87                      action="store_true",
88                      help="Do not redirect stdout/err to canvas output view.")
89    parser.add_option("--style",
90                      help="QStyle to use",
91                      type="str", default=None)
92    parser.add_option("--stylesheet",
93                      help="Application level CSS style sheet to use",
94                      type="str", default="orange.qss")
95    parser.add_option("--qt",
96                      help="Additional arguments for QApplication",
97                      type="str", default=None)
98
99    (options, args) = parser.parse_args(argv)
100
101    levels = [logging.CRITICAL,
102              logging.ERROR,
103              logging.WARN,
104              logging.INFO,
105              logging.DEBUG]
106
107    logging.basicConfig(level=levels[options.log_level])
108
109    fix_win_pythonw_std_stream()
110
111    log.info("Starting 'Orange Canvas' application.")
112
113    qt_argv = ["orange-canvas"]
114
115    if options.style is not None:
116        qt_argv += ["-style", options.style]
117
118    if options.qt is not None:
119        qt_argv += options.qt.split()
120
121    log.debug("Starting CanvasApplicaiton with argv = %r.", qt_argv)
122    app = CanvasApplication(qt_argv)
123
124    # intercept any QFileOpenEvent requests until the main window is
125    # fully initialized
126    open_requests = []
127    app.fileOpenRequest.connect(open_requests.append)
128
129    # Note: config.init must be called after the QApplication constructor
130    config.init()
131    settings = QSettings()
132
133    stylesheet = options.stylesheet
134    stylesheet_string = None
135
136    if stylesheet != "none":
137        if os.path.isfile(stylesheet):
138            stylesheet_string = open(stylesheet, "rb").read()
139        else:
140            if not os.path.splitext(stylesheet)[1]:
141                # no extension
142                stylesheet = os.path.extsep.join([stylesheet, "qss"])
143
144            pkg_name = OrangeCanvas.__name__
145            resource = "styles/" + stylesheet
146
147            if pkg_resources.resource_exists(pkg_name, resource):
148                stylesheet_string = \
149                    pkg_resources.resource_string(pkg_name, resource)
150
151                base = pkg_resources.resource_filename(pkg_name, "styles")
152
153                pattern = re.compile(
154                    r"^\s@([a-zA-Z0-9_]+?)\s*:\s*([a-zA-Z0-9_/]+?);\s*$",
155                    flags=re.MULTILINE
156                )
157
158                matches = pattern.findall(stylesheet_string)
159
160                for prefix, search_path in matches:
161                    QDir.addSearchPath(prefix, os.path.join(base, search_path))
162                    log.info("Adding search path %r for prefix, %r",
163                             search_path, prefix)
164
165                stylesheet_string = pattern.sub("", stylesheet_string)
166
167            else:
168                log.info("%r style sheet not found.", stylesheet)
169
170    if stylesheet_string is not None:
171        app.setStyleSheet(stylesheet_string)
172
173    # Add the default canvas_icons search path
174    dirpath = os.path.abspath(os.path.dirname(OrangeCanvas.__file__))
175    QDir.addSearchPath("canvas_icons", os.path.join(dirpath, "icons"))
176
177    canvas_window = CanvasMainWindow()
178
179    if not options.force_discovery:
180        reg_cache = cache.registry_cache()
181    else:
182        reg_cache = None
183
184    widget_discovery = qt.QtWidgetDiscovery(cached_descriptions=reg_cache)
185
186    widget_registry = qt.QtWidgetRegistry()
187
188    widget_discovery.found_category.connect(
189        widget_registry.register_category
190    )
191    widget_discovery.found_widget.connect(
192        widget_registry.register_widget
193    )
194
195    want_splash = \
196        settings.value("startup/show-splash-screen", True, type=bool) and \
197        not options.no_splash
198
199    if want_splash:
200        pm, rect = config.splash_screen()
201        splash_screen = SplashScreen(pixmap=pm, textRect=rect)
202        splash_screen.setFont(QFont("Helvetica", 12))
203        color = QColor("#FFD39F")
204
205        def show_message(message):
206            splash_screen.showMessage(message, color=color)
207
208        widget_discovery.discovery_start.connect(splash_screen.show)
209        widget_discovery.discovery_process.connect(show_message)
210        widget_discovery.discovery_finished.connect(splash_screen.hide)
211
212    log.info("Running widget discovery process.")
213
214    cache_filename = os.path.join(cache_dir(), "widget-registry.pck")
215    if options.no_discovery:
216        widget_registry = cPickle.load(open(cache_filename, "rb"))
217        widget_registry = qt.QtWidgetRegistry(widget_registry)
218    else:
219        widget_discovery.run(config.widgets_entry_points())
220        # Store cached descriptions
221        cache.save_registry_cache(widget_discovery.cached_descriptions)
222        cPickle.dump(WidgetRegistry(widget_registry),
223                     open(cache_filename, "wb"))
224    set_global_registry(widget_registry)
225    canvas_window.set_widget_registry(widget_registry)
226    canvas_window.show()
227
228    want_welcome = \
229        settings.value("startup/show-welcome-screen", True, type=bool) \
230        and not options.no_welcome
231
232    canvas_window.raise_()
233
234    if want_welcome and not args and not open_requests:
235        # Process events to make sure the canvas_window layout has
236        # a chance to activate (the welcome dialog is modal and will
237        # block the event queue)
238        app.processEvents()
239        canvas_window.welcome_dialog()
240
241    elif args:
242        log.info("Loading a scheme from the command line argument %r",
243                 args[0])
244        canvas_window.load_scheme(args[0])
245    elif open_requests:
246        log.info("Loading a scheme from an `QFileOpenEvent` for %r",
247                 open_requests[-1])
248        canvas_window.load_scheme(open_requests[-1].toLocalFile())
249
250    app.fileOpenRequest.connect(canvas_window.open_scheme_file)
251
252    stdout_redirect = \
253        settings.value("output/redirect-stdout", True, type=bool)
254
255    stderr_redirect = \
256        settings.value("output/redirect-stderr", True, type=bool)
257
258    # cmd line option overrides settings / no redirect is possible
259    # under ipython
260    if options.no_redirect or running_in_ipython():
261        stderr_redirect = stdout_redirect = False
262
263    output_view = canvas_window.output_view()
264
265    if stdout_redirect:
266        stdout = TextStream()
267        stdout.stream.connect(output_view.write)
268        # also connect to original fd
269        stdout.stream.connect(sys.stdout.write)
270    else:
271        stdout = sys.stdout
272
273    if stderr_redirect:
274        error_writer = output_view.formated(color=Qt.red)
275        stderr = TextStream()
276        stderr.stream.connect(error_writer.write)
277        # also connect to original fd
278        stderr.stream.connect(sys.stderr.write)
279    else:
280        stderr = sys.stderr
281
282    if stderr_redirect:
283        sys.excepthook = ExceptHook()
284        sys.excepthook.handledException.connect(output_view.parent().show)
285
286    with nested(redirect_stdout(stdout), redirect_stderr(stderr)):
287        log.info("Entering main event loop.")
288        try:
289            status = app.exec_()
290        except BaseException:
291            log.error("Error in main event loop.", exc_info=True)
292
293    canvas_window.deleteLater()
294    app.processEvents()
295    app.flush()
296    del canvas_window
297
298    # Collect any cycles before deleting the QApplication instance
299    gc.collect()
300
301    del app
302    return status
303
304
305if __name__ == "__main__":
306    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.