source: orange/Orange/OrangeCanvas/main.py @ 11596:c84e54594473

Revision 11596:c84e54594473, 10.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 10 months ago (diff)

Set the canvas style sheet on the CanvasMainWindow only.

This prevents any accidental/unwanted styling of other widgets (OWWidget).

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