source: orange/Orange/OrangeCanvas/main.py @ 11522:425d83b20b96

Revision 11522:425d83b20b96, 10.0 KB checked in by Ales Erjavec <ales.erjavec@…>, 11 months ago (diff)

Handle QFileOpenEvent request events.

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, 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
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 = QPixmap(pkg_resources.resource_filename(
201                        __name__, "icons/orange-splash-screen.png")
202                     )
203        # Text rectangle in which to fit the message.
204        rect = QRect(88, 193, 200, 20)
205        splash_screen = SplashScreen(pixmap=pm, textRect=rect)
206        splash_screen.setFont(QFont("Helvetica", 12))
207        color = QColor("#FFD39F")
208
209        def show_message(message):
210            splash_screen.showMessage(message, color=color)
211
212        widget_discovery.discovery_start.connect(splash_screen.show)
213        widget_discovery.discovery_process.connect(show_message)
214        widget_discovery.discovery_finished.connect(splash_screen.hide)
215
216    log.info("Running widget discovery process.")
217
218    cache_filename = os.path.join(cache_dir(), "widget-registry.pck")
219    if options.no_discovery:
220        widget_registry = cPickle.load(open(cache_filename, "rb"))
221        widget_registry = qt.QtWidgetRegistry(widget_registry)
222    else:
223        widget_discovery.run(config.widgets_entry_points())
224        # Store cached descriptions
225        cache.save_registry_cache(widget_discovery.cached_descriptions)
226        cPickle.dump(WidgetRegistry(widget_registry),
227                     open(cache_filename, "wb"))
228    set_global_registry(widget_registry)
229    canvas_window.set_widget_registry(widget_registry)
230    canvas_window.show()
231
232    want_welcome = \
233        settings.value("startup/show-welcome-screen", True, type=bool) \
234        and not options.no_welcome
235
236    canvas_window.raise_()
237
238    if want_welcome and not args and not open_requests:
239        # Process events to make sure the canvas_window layout has
240        # a chance to activate (the welcome dialog is modal and will
241        # block the event queue)
242        app.processEvents()
243        canvas_window.welcome_dialog()
244
245    elif args:
246        log.info("Loading a scheme from the command line argument %r",
247                 args[0])
248        canvas_window.load_scheme(args[0])
249    elif open_requests:
250        log.info("Loading a scheme from an `QFileOpenEvent` for %r",
251                 open_requests[-1])
252        canvas_window.load_scheme(open_requests[-1].toLocalFile())
253
254    app.fileOpenRequest.connect(canvas_window.open_scheme_file)
255
256    stdout_redirect = \
257        settings.value("output/redirect-stdout", True, type=bool)
258
259    stderr_redirect = \
260        settings.value("output/redirect-stderr", True, type=bool)
261
262    # cmd line option overrides settings / no redirect is possible
263    # under ipython
264    if options.no_redirect or running_in_ipython():
265        stderr_redirect = stdout_redirect = False
266
267    output_view = canvas_window.output_view()
268
269    if stdout_redirect:
270        stdout = TextStream()
271        stdout.stream.connect(output_view.write)
272        # also connect to original fd
273        stdout.stream.connect(sys.stdout.write)
274    else:
275        stdout = sys.stdout
276
277    if stderr_redirect:
278        error_writer = output_view.formated(color=Qt.red)
279        stderr = TextStream()
280        stderr.stream.connect(error_writer.write)
281        # also connect to original fd
282        stderr.stream.connect(sys.stderr.write)
283    else:
284        stderr = sys.stderr
285
286    if stderr_redirect:
287        sys.excepthook = ExceptHook()
288        sys.excepthook.handledException.connect(output_view.parent().show)
289
290    with nested(redirect_stdout(stdout), redirect_stderr(stderr)):
291        log.info("Entering main event loop.")
292        try:
293            status = app.exec_()
294        except BaseException:
295            log.error("Error in main event loop.", exc_info=True)
296
297    canvas_window.deleteLater()
298    app.processEvents()
299    app.flush()
300    del canvas_window
301
302    # Collect any cycles before deleting the QApplication instance
303    gc.collect()
304
305    del app
306    return status
307
308
309if __name__ == "__main__":
310    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.