source: orange/Orange/OrangeCanvas/main.py @ 11285:c0fc66213bfd

Revision 11285:c0fc66213bfd, 9.0 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Changed discovery to run from arbitrary entry points iterator/group.

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