source: orange/Orange/OrangeCanvas/main.py @ 11257:cb4e4ab85ce8

Revision 11257:cb4e4ab85ce8, 8.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Added colored/formated output for the stdout/err output, output thread safety.

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