source: orange/Orange/OrangeCanvas/application/canvasmain.py @ 11257:cb4e4ab85ce8

Revision 11257:cb4e4ab85ce8, 51.5 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 Window
3
4"""
5import os
6import sys
7import logging
8import operator
9from functools import partial
10
11import pkg_resources
12
13from PyQt4.QtGui import (
14    QMainWindow, QWidget, QAction, QActionGroup, QMenu, QMenuBar, QDialog,
15    QFileDialog, QMessageBox, QVBoxLayout, QSizePolicy, QColor, QKeySequence,
16    QIcon, QToolBar, QToolButton, QDockWidget, QDesktopServices,
17    QApplication
18)
19
20from PyQt4.QtCore import (
21    Qt, QEvent, QSize, QUrl, QSettings, QTimer, QFile
22)
23
24from PyQt4.QtCore import pyqtProperty as Property
25
26
27from ..gui.dropshadow import DropShadowFrame
28from ..gui.dock import CollapsibleDockWidget
29from ..gui.quickhelp import QuickHelpTipEvent
30from ..gui.utils import message_critical, message_question, message_information
31
32from .canvastooldock import CanvasToolDock, QuickCategoryToolbar
33from .aboutdialog import AboutDialog
34from .schemeinfo import SchemeInfoDialog
35from .outputview import OutputView
36from .settings import UserSettingsDialog
37from ..document.schemeedit import SchemeEditWidget
38
39from ..scheme import widgetsscheme
40
41from . import welcomedialog
42from ..preview import previewdialog, previewmodel
43
44from .. import config
45
46from . import tutorials
47
48log = logging.getLogger(__name__)
49
50# TODO: Orange Version in the base link
51
52BASE_LINK = "http://orange.biolab.si/"
53
54LINKS = \
55    {"start-using": BASE_LINK + "start-using/",
56     "tutorial": BASE_LINK + "tutorial/",
57     "reference": BASE_LINK + "doc/"
58     }
59
60
61def style_icons(widget, standard_pixmap):
62    """Return the Qt standard pixmap icon.
63    """
64    return QIcon(widget.style().standardPixmap(standard_pixmap))
65
66
67def canvas_icons(name):
68    """Return the named canvas icon.
69    """
70    icon_file = QFile("canvas_icons:" + name)
71    if icon_file.exists():
72        return QIcon("canvas_icons:" + name)
73    else:
74        return QIcon(pkg_resources.resource_filename(
75                      config.__name__,
76                      os.path.join("icons", name))
77                     )
78
79
80class FakeToolBar(QToolBar):
81    """A Toolbar with no contents (used to reserve top and bottom margins
82    on the main window).
83
84    """
85    def __init__(self, *args, **kwargs):
86        QToolBar.__init__(self, *args, **kwargs)
87        self.setFloatable(False)
88        self.setMovable(False)
89
90        # Don't show the tool bar action in the main window's
91        # context menu.
92        self.toggleViewAction().setVisible(False)
93
94    def paintEvent(self, event):
95        # Do nothing.
96        pass
97
98
99class CanvasMainWindow(QMainWindow):
100    SETTINGS_VERSION = 2
101
102    def __init__(self, *args):
103        QMainWindow.__init__(self, *args)
104
105        self.__scheme_margins_enabled = True
106        self.__document_title = "untitled"
107
108        self.widget_registry = None
109        self.last_scheme_dir = None
110
111        self.recent_schemes = config.recent_schemes()
112
113        self.setup_actions()
114        self.setup_ui()
115        self.setup_menu()
116
117        self.restore()
118
119        self.resize(800, 600)
120
121    def setup_ui(self):
122        """Setup main canvas ui
123        """
124
125        log.info("Setting up Canvas main window.")
126
127        # Two dummy tool bars to reserve space
128        self.__dummy_top_toolbar = FakeToolBar(
129                            objectName="__dummy_top_toolbar")
130        self.__dummy_bottom_toolbar = FakeToolBar(
131                            objectName="__dummy_bottom_toolbar")
132
133        self.__dummy_top_toolbar.setFixedHeight(20)
134        self.__dummy_bottom_toolbar.setFixedHeight(20)
135
136        self.addToolBar(Qt.TopToolBarArea, self.__dummy_top_toolbar)
137        self.addToolBar(Qt.BottomToolBarArea, self.__dummy_bottom_toolbar)
138
139        self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
140        self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea)
141
142        # Create an empty initial scheme inside a container with fixed
143        # margins.
144        w = QWidget()
145        w.setLayout(QVBoxLayout())
146        w.layout().setContentsMargins(20, 0, 10, 0)
147
148        self.scheme_widget = SchemeEditWidget()
149        self.scheme_widget.setScheme(widgetsscheme.WidgetsScheme())
150
151        w.layout().addWidget(self.scheme_widget)
152
153        self.setCentralWidget(w)
154
155        # Drop shadow around the scheme document
156        frame = DropShadowFrame(radius=15)
157        frame.setColor(QColor(0, 0, 0, 100))
158        frame.setWidget(self.scheme_widget)
159
160        # Main window title and title icon.
161        self.set_document_title(self.scheme_widget.scheme().title)
162        self.scheme_widget.titleChanged.connect(self.set_document_title)
163        self.scheme_widget.modificationChanged.connect(self.setWindowModified)
164
165        self.setWindowIcon(canvas_icons("Get Started.svg"))
166
167        # QMainWindow's Dock widget
168        self.dock_widget = CollapsibleDockWidget(objectName="main-area-dock")
169        self.dock_widget.setFeatures(QDockWidget.DockWidgetMovable | \
170                                     QDockWidget.DockWidgetClosable)
171
172        self.dock_widget.setAllowedAreas(Qt.LeftDockWidgetArea | \
173                                         Qt.RightDockWidgetArea)
174
175        # Main canvas tool dock (with widget toolbox, common actions.
176        # This is the widget that is shown when the dock is expanded.
177        canvas_tool_dock = CanvasToolDock(objectName="canvas-tool-dock")
178        canvas_tool_dock.setSizePolicy(QSizePolicy.Fixed,
179                                       QSizePolicy.MinimumExpanding)
180
181        # Bottom tool bar
182        self.canvas_toolbar = canvas_tool_dock.toolbar
183        self.canvas_toolbar.setIconSize(QSize(25, 25))
184        self.canvas_toolbar.setFixedHeight(28)
185        self.canvas_toolbar.layout().setSpacing(1)
186
187        # Widgets tool box
188        self.widgets_tool_box = canvas_tool_dock.toolbox
189        self.widgets_tool_box.setObjectName("canvas-toolbox")
190        self.widgets_tool_box.setTabButtonHeight(30)
191        self.widgets_tool_box.setTabIconSize(QSize(26, 26))
192        self.widgets_tool_box.setButtonSize(QSize(64, 84))
193        self.widgets_tool_box.setIconSize(QSize(48, 48))
194
195        self.widgets_tool_box.triggered.connect(
196            self.on_tool_box_widget_activated
197        )
198
199        self.dock_help = canvas_tool_dock.help
200        self.dock_help.setMaximumHeight(150)
201        self.dock_help.document().setDefaultStyleSheet("h3 {color: orange;}")
202
203        self.dock_help_action = canvas_tool_dock.toogleQuickHelpAction()
204        self.dock_help_action.setText(self.tr("Show Help"))
205        self.dock_help_action.setIcon(canvas_icons("Info.svg"))
206
207        self.canvas_tool_dock = canvas_tool_dock
208
209        # Dock contents when collapsed (a quick category tool bar, ...)
210        dock2 = QWidget(objectName="canvas-quick-dock")
211        dock2.setLayout(QVBoxLayout())
212        dock2.layout().setContentsMargins(0, 0, 0, 0)
213        dock2.layout().setSpacing(0)
214        dock2.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)
215
216        self.quick_category = QuickCategoryToolbar()
217        self.quick_category.setButtonSize(QSize(38, 30))
218        self.quick_category.actionTriggered.connect(
219            self.on_quick_category_action
220        )
221
222        tool_actions = self.current_document().toolbarActions()
223
224        (self.canvas_zoom_action, self.canvas_align_to_grid_action,
225         self.canvas_text_action, self.canvas_arrow_action,) = tool_actions
226
227        self.canvas_zoom_action.setIcon(canvas_icons("Search.svg"))
228        self.canvas_align_to_grid_action.setIcon(canvas_icons("Grid.svg"))
229        self.canvas_text_action.setIcon(canvas_icons("Text Size.svg"))
230        self.canvas_arrow_action.setIcon(canvas_icons("Arrow.svg"))
231
232        dock_actions = [self.show_properties_action] + \
233                       tool_actions + \
234                       [self.freeze_action,
235                        self.dock_help_action]
236
237        # Tool bar in the collapsed dock state (has the same actions as
238        # the tool bar in the CanvasToolDock
239        actions_toolbar = QToolBar(orientation=Qt.Vertical)
240        actions_toolbar.setFixedWidth(38)
241        actions_toolbar.layout().setSpacing(0)
242
243        actions_toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
244
245        for action in dock_actions:
246            self.canvas_toolbar.addAction(action)
247            button = self.canvas_toolbar.widgetForAction(action)
248            button.setPopupMode(QToolButton.DelayedPopup)
249
250            actions_toolbar.addAction(action)
251            button = actions_toolbar.widgetForAction(action)
252            button.setFixedSize(38, 30)
253            button.setPopupMode(QToolButton.DelayedPopup)
254
255        dock2.layout().addWidget(self.quick_category)
256        dock2.layout().addWidget(actions_toolbar)
257
258        self.dock_widget.setAnimationEnabled(False)
259        self.dock_widget.setExpandedWidget(self.canvas_tool_dock)
260        self.dock_widget.setCollapsedWidget(dock2)
261        self.dock_widget.setExpanded(True)
262
263        self.addDockWidget(Qt.RightDockWidgetArea, self.dock_widget)
264        self.dock_widget.dockLocationChanged.connect(
265            self._on_dock_location_changed
266        )
267
268        self.output_dock = QDockWidget(self.tr("Output"),
269                                       objectName="output-dock")
270        self.output_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
271
272        self.addDockWidget(Qt.BottomDockWidgetArea, self.output_dock)
273        self.output_dock.setFloating(True)
274
275        self.output_dock.hide()
276
277        output_view = OutputView()
278        self.output_dock.setWidget(output_view)
279
280        self.setMinimumSize(600, 500)
281
282    def setup_actions(self):
283        """Initialize main window actions.
284        """
285
286        self.new_action = \
287            QAction(self.tr("New"), self,
288                    objectName="action-new",
289                    toolTip=self.tr("Open a new scheme."),
290                    triggered=self.new_scheme,
291                    shortcut=QKeySequence.New,
292                    icon=canvas_icons("New.svg")
293                    )
294
295        self.open_action = \
296            QAction(self.tr("Open"), self,
297                    objectName="action-open",
298                    toolTip=self.tr("Open a scheme."),
299                    triggered=self.open_scheme,
300                    shortcut=QKeySequence.Open,
301                    icon=canvas_icons("Open.svg")
302                    )
303
304        self.save_action = \
305            QAction(self.tr("Save"), self,
306                    objectName="action-save",
307                    toolTip=self.tr("Save current scheme."),
308                    triggered=self.save_scheme,
309                    shortcut=QKeySequence.Save,
310                    )
311
312        self.save_as_action = \
313            QAction(self.tr("Save As ..."), self,
314                    objectName="action-save-as",
315                    toolTip=self.tr("Save current scheme as."),
316                    triggered=self.save_scheme_as,
317                    shortcut=QKeySequence.SaveAs,
318                    )
319
320        self.quit_action = \
321            QAction(self.tr("Quit"), self,
322                    objectName="quit-action",
323                    toolTip=self.tr("Quit Orange Canvas."),
324                    triggered=self.quit,
325                    menuRole=QAction.QuitRole,
326                    shortcut=QKeySequence.Quit,
327                    )
328
329        self.welcome_action = \
330            QAction(self.tr("Welcome"), self,
331                    objectName="welcome-action",
332                    toolTip=self.tr("Show welcome screen."),
333                    triggered=self.welcome_dialog,
334                    )
335
336        self.get_started_action = \
337            QAction(self.tr("Get Started"), self,
338                    objectName="get-started-action",
339                    toolTip=self.tr("View a 'Getting Started' video."),
340                    triggered=self.get_started,
341                    icon=canvas_icons("Get Started.svg")
342                    )
343
344        self.tutorials_action = \
345            QAction(self.tr("Tutorials"), self,
346                    objectName="tutorial-action",
347                    toolTip=self.tr("Browse tutorials."),
348                    triggered=self.tutorial_scheme,
349                    icon=canvas_icons("Tutorials.svg")
350                    )
351
352        self.documentation_action = \
353            QAction(self.tr("Documentation"), self,
354                    objectName="documentation-action",
355                    toolTip=self.tr("View reference documentation."),
356                    triggered=self.documentation,
357                    icon=canvas_icons("Documentation.svg")
358                    )
359
360        self.about_action = \
361            QAction(self.tr("About"), self,
362                    objectName="about-action",
363                    toolTip=self.tr("Show about dialog."),
364                    triggered=self.open_about,
365                    menuRole=QAction.AboutRole,
366                    )
367
368        # Action group for for recent scheme actions
369        self.recent_scheme_action_group = \
370            QActionGroup(self, exclusive=False,
371                         objectName="recent-action-group",
372                         triggered=self._on_recent_scheme_action)
373
374        self.recent_action = \
375            QAction(self.tr("Browse Recent"), self,
376                    objectName="recent-action",
377                    toolTip=self.tr("Browse and open a recent scheme."),
378                    triggered=self.recent_scheme,
379                    shortcut=QKeySequence(Qt.ControlModifier | \
380                                          (Qt.ShiftModifier | Qt.Key_R)),
381                    icon=canvas_icons("Recent.svg")
382                    )
383
384        self.reload_last_action = \
385            QAction(self.tr("Reload Last Scheme"), self,
386                    objectName="reload-last-action",
387                    toolTip=self.tr("Reload last open scheme."),
388                    triggered=self.reload_last,
389                    shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_R)
390                    )
391
392        self.clear_recent_action = \
393            QAction(self.tr("Clear Menu"), self,
394                    objectName="clear-recent-menu-action",
395                    toolTip=self.tr("Clear recent menu."),
396                    triggered=self.clear_recent_schemes
397                    )
398
399        self.show_properties_action = \
400            QAction(self.tr("Show Properties"), self,
401                    objectName="show-properties-action",
402                    toolTip=self.tr("Show scheme properties."),
403                    triggered=self.show_scheme_properties,
404                    shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_I),
405                    icon=canvas_icons("Document Info.svg")
406                    )
407
408        self.canvas_settings_action = \
409            QAction(self.tr("Settings"), self,
410                    objectName="canvas-settings-action",
411                    toolTip=self.tr("Set application settings."),
412                    triggered=self.open_canvas_settings,
413                    menuRole=QAction.PreferencesRole,
414                    shortcut=QKeySequence.Preferences
415                    )
416
417        self.show_output_action = \
418            QAction(self.tr("Show Output View"), self,
419                    toolTip=self.tr("Show application output."),
420                    triggered=self.show_output_view,
421                    )
422
423        if sys.platform == "darwin":
424            # Actions for native Mac OSX look and feel.
425            self.minimize_action = \
426                QAction(self.tr("Minimize"), self,
427                        triggered=self.showMinimized,
428                        shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_M)
429                        )
430
431            self.zoom_action = \
432                QAction(self.tr("Zoom"), self,
433                        objectName="application-zoom",
434                        triggered=self.toggleMaximized,
435                        )
436
437        self.freeze_action = \
438            QAction(self.tr("Freeze"), self,
439                    objectName="signal-freeze-action",
440                    checkable=True,
441                    toolTip=self.tr("Freeze signal propagation."),
442                    triggered=self.set_signal_freeze,
443                    icon=canvas_icons("Pause.svg")
444                    )
445
446        # Gets assigned in setup_ui (the action is defined in CanvasToolDock)
447        # TODO: This is bad (should be moved here).
448        self.dock_help_action = None
449
450        self.toogle_margins_action = \
451            QAction(self.tr("Show Scheme Margins"), self,
452                    checkable=True,
453                    checked=True,
454                    toolTip=self.tr("Show margins around the scheme view."),
455                    toggled=self.set_scheme_margins_enabled
456                    )
457
458    def setup_menu(self):
459        menu_bar = QMenuBar()
460
461        # File menu
462        file_menu = QMenu(self.tr("&File"), menu_bar)
463        file_menu.addAction(self.new_action)
464        file_menu.addAction(self.open_action)
465        file_menu.addAction(self.reload_last_action)
466
467        # File -> Open Recent submenu
468        self.recent_menu = QMenu(self.tr("Open Recent"), file_menu)
469        file_menu.addMenu(self.recent_menu)
470        file_menu.addSeparator()
471        file_menu.addAction(self.save_action)
472        file_menu.addAction(self.save_as_action)
473        file_menu.addSeparator()
474        file_menu.addAction(self.show_properties_action)
475        file_menu.addAction(self.quit_action)
476
477        self.recent_menu.addAction(self.recent_action)
478
479        # Store the reference to separator for inserting recent
480        # schemes into the menu in `add_recent_scheme`.
481        self.recent_menu_begin = self.recent_menu.addSeparator()
482
483        # Add recent items.
484        for title, filename in self.recent_schemes:
485            action = QAction(title or self.tr("untitled"), self,
486                             toolTip=filename)
487
488            action.setData(filename)
489            self.recent_menu.addAction(action)
490            self.recent_scheme_action_group.addAction(action)
491
492        self.recent_menu.addSeparator()
493        self.recent_menu.addAction(self.clear_recent_action)
494        menu_bar.addMenu(file_menu)
495
496        editor_menus = self.scheme_widget.menuBarActions()
497
498        # WARNING: Hard coded order, should lookup the action text
499        # and determine the proper order
500        self.edit_menu = editor_menus[0].menu()
501        self.widget_menu = editor_menus[1].menu()
502
503        # Edit menu
504        menu_bar.addMenu(self.edit_menu)
505
506        # View menu
507        self.view_menu = QMenu(self.tr("&View"), self)
508        self.toolbox_menu = QMenu(self.tr("Widget Toolbox Style"),
509                                  self.view_menu)
510        self.toolbox_menu_group = \
511            QActionGroup(self, objectName="toolbox-menu-group")
512
513        a1 = self.toolbox_menu.addAction(self.tr("Tool Box"))
514        a2 = self.toolbox_menu.addAction(self.tr("Tool List"))
515        self.toolbox_menu_group.addAction(a1)
516        self.toolbox_menu_group.addAction(a2)
517
518        self.view_menu.addMenu(self.toolbox_menu)
519        self.view_menu.addSeparator()
520        self.view_menu.addAction(self.toogle_margins_action)
521        menu_bar.addMenu(self.view_menu)
522
523        # Options menu
524        self.options_menu = QMenu(self.tr("&Options"), self)
525        self.options_menu.addAction(self.show_output_action)
526#        self.options_menu.addAction("Add-ons")
527#        self.options_menu.addAction("Developers")
528#        self.options_menu.addAction("Run Discovery")
529#        self.options_menu.addAction("Show Canvas Log")
530#        self.options_menu.addAction("Attach Python Console")
531        self.options_menu.addSeparator()
532        self.options_menu.addAction(self.canvas_settings_action)
533
534        # Widget menu
535        menu_bar.addMenu(self.widget_menu)
536
537        if sys.platform == "darwin":
538            # Mac OS X native look and feel.
539            self.window_menu = QMenu(self.tr("Window"), self)
540            self.window_menu.addAction(self.minimize_action)
541            self.window_menu.addAction(self.zoom_action)
542            menu_bar.addMenu(self.window_menu)
543
544        menu_bar.addMenu(self.options_menu)
545
546        # Help menu.
547        self.help_menu = QMenu(self.tr("&Help"), self)
548        self.help_menu.addAction(self.about_action)
549        self.help_menu.addAction(self.welcome_action)
550        self.help_menu.addAction(self.tutorials_action)
551        self.help_menu.addAction(self.documentation_action)
552        menu_bar.addMenu(self.help_menu)
553
554        self.setMenuBar(menu_bar)
555
556    def restore(self):
557        """Restore the main window state from saved settings.
558        """
559        QSettings.setDefaultFormat(QSettings.IniFormat)
560        settings = QSettings()
561        settings.beginGroup("mainwindow")
562
563        self.dock_widget.setExpanded(
564            settings.value("canvasdock/expanded", True).toBool()
565        )
566
567        floatable = settings.value("toolbox-dock-floatable", False).toBool()
568        if floatable:
569            self.dock_widget.setFeatures(self.dock_widget.features() | \
570                                         QDockWidget.DockWidgetFloatable)
571
572        self.widgets_tool_box.setExclusive(
573            settings.value("toolbox-dock-exclusive", False).toBool()
574        )
575
576        self.toogle_margins_action.setChecked(
577            settings.value("scheme-margins-enabled", True).toBool()
578        )
579
580        self.last_scheme_dir = \
581            settings.value("last-scheme-dir", None).toPyObject()
582
583        if self.last_scheme_dir is not None and \
584                not os.path.exists(self.last_scheme_dir):
585            # if directory no longer exists reset the saved location.
586            self.last_scheme_dir = None
587
588        self.canvas_tool_dock.setQuickHelpVisible(
589            settings.value("quick-help/visible", True).toBool()
590        )
591
592    def set_document_title(self, title):
593        """Set the document title (and the main window title). If `title`
594        is an empty string a default 'untitled' placeholder will be used.
595
596        """
597        if self.__document_title != title:
598            self.__document_title = title
599
600            if not title:
601                # TODO: should the default name be platform specific
602                title = self.tr("untitled")
603
604            self.setWindowTitle(title + "[*]")
605
606    def document_title(self):
607        """Return the document title.
608        """
609        return self.__document_title
610
611    def set_widget_registry(self, widget_registry):
612        """Set widget registry.
613        """
614        if self.widget_registry is not None:
615            # Clear the dock widget and popup.
616            pass
617
618        self.widget_registry = widget_registry
619        self.widgets_tool_box.setModel(widget_registry.model())
620        self.quick_category.setModel(widget_registry.model())
621
622        self.scheme_widget.setRegistry(widget_registry)
623
624        # Restore possibly saved widget toolbox tab states
625        settings = QSettings()
626        state = settings.value("mainwindow/widgettoolbox/state",
627                                defaultValue=None)
628        state = state.toPyObject()
629        if state:
630            self.widgets_tool_box.restoreState(state)
631
632    def set_quick_help_text(self, text):
633        self.canvas_tool_dock.help.setText(text)
634
635    def current_document(self):
636        return self.scheme_widget
637
638    def on_tool_box_widget_activated(self, action):
639        """A widget action in the widget toolbox has been activated.
640        """
641        widget_desc = action.data().toPyObject()
642        if widget_desc:
643            scheme_widget = self.current_document()
644            if scheme_widget:
645                scheme_widget.createNewNode(widget_desc)
646
647    def on_quick_category_action(self, action):
648        """The quick category menu action triggered.
649        """
650        category = action.text()
651        for i in range(self.widgets_tool_box.count()):
652            cat_act = self.widgets_tool_box.tabAction(i)
653            if cat_act.text() == category:
654                if not cat_act.isChecked():
655                    # Trigger the action to expand the tool grid contained
656                    # within.
657                    cat_act.trigger()
658
659            else:
660                if cat_act.isChecked():
661                    # Trigger the action to hide the tool grid contained
662                    # within.
663                    cat_act.trigger()
664
665        self.dock_widget.expand()
666
667    def set_scheme_margins_enabled(self, enabled):
668        """Enable/disable the margins around the scheme document.
669        """
670        if self.__scheme_margins_enabled != enabled:
671            self.__scheme_margins_enabled = enabled
672            self.__update_scheme_margins()
673
674    def scheme_margins_enabled(self):
675        return self.__scheme_margins_enabled
676
677    scheme_margins_enabled = Property(bool,
678                                      fget=scheme_margins_enabled,
679                                      fset=set_scheme_margins_enabled)
680
681    def __update_scheme_margins(self):
682        """Update the margins around the scheme document.
683        """
684        enabled = self.__scheme_margins_enabled
685        self.__dummy_top_toolbar.setVisible(enabled)
686        self.__dummy_bottom_toolbar.setVisible(enabled)
687        central = self.centralWidget()
688
689        margin = 20 if enabled else 0
690
691        if self.dockWidgetArea(self.dock_widget) == Qt.LeftDockWidgetArea:
692            margins = (margin / 2, 0, margin, 0)
693        else:
694            margins = (margin, 0, margin / 2, 0)
695
696        central.layout().setContentsMargins(*margins)
697
698    #################
699    # Action handlers
700    #################
701    def new_scheme(self):
702        """New scheme. Return QDialog.Rejected if the user canceled
703        the operation and QDialog.Accepted otherwise.
704
705        """
706        document = self.current_document()
707        if document.isModifiedStrict():
708            # Ask for save changes
709            if self.ask_save_changes() == QDialog.Rejected:
710                return QDialog.Rejected
711
712        new_scheme = widgetsscheme.WidgetsScheme()
713
714        settings = QSettings()
715        show = settings.value("schemeinfo/show-at-new-scheme", True).toBool()
716
717        if show:
718            status = self.show_scheme_properties_for(
719                new_scheme, self.tr("New Scheme")
720            )
721
722            if status == QDialog.Rejected:
723                return QDialog.Rejected
724
725        self.set_new_scheme(new_scheme)
726
727        return QDialog.Accepted
728
729    def open_scheme(self):
730        """Open a new scheme. Return QDialog.Rejected if the user canceled
731        the operation and QDialog.Accepted otherwise.
732
733        """
734        document = self.current_document()
735        if document.isModifiedStrict():
736            if self.ask_save_changes() == QDialog.Rejected:
737                return QDialog.Rejected
738
739        if self.last_scheme_dir is None:
740            # Get user 'Documents' folder
741            start_dir = QDesktopServices.storageLocation(
742                            QDesktopServices.DocumentsLocation)
743        else:
744            start_dir = self.last_scheme_dir
745
746        # TODO: Use a dialog instance and use 'addSidebarUrls' to
747        # set one or more extra sidebar locations where Schemes are stored.
748        # Also use setHistory
749        filename = QFileDialog.getOpenFileName(
750            self, self.tr("Open Orange Scheme File"),
751            start_dir, self.tr("Orange Scheme (*.ows)"),
752        )
753
754        if filename:
755            self.load_scheme(filename)
756            return QDialog.Accepted
757        else:
758            return QDialog.Rejected
759
760    def load_scheme(self, filename):
761        """Load a scheme from a file (`filename`) into the current
762        document updates the recent scheme list and the loaded scheme path
763        property.
764
765        """
766        filename = unicode(filename)
767        dirname = os.path.dirname(filename)
768
769        self.last_scheme_dir = dirname
770
771        new_scheme = self.new_scheme_from(filename)
772
773        self.set_new_scheme(new_scheme)
774
775        scheme_doc_widget = self.current_document()
776        scheme_doc_widget.setPath(filename)
777
778        self.add_recent_scheme(new_scheme.title, filename)
779
780    def new_scheme_from(self, filename):
781        """Create and return a new :class:`widgetsscheme.WidgetsScheme`
782        from a saved `filename`.
783
784        """
785        new_scheme = widgetsscheme.WidgetsScheme()
786        try:
787            new_scheme.load_from(open(filename, "rb"))
788        except Exception:
789            message_critical(
790                 self.tr("Could not load Orange Scheme file"),
791                 title=self.tr("Error"),
792                 informative_text=self.tr("An unexpected error occurred"),
793                 exc_info=True,
794                 parent=self)
795            return None
796
797        return new_scheme
798
799    def reload_last(self):
800        """Reload last opened scheme. Return QDialog.Rejected if the
801        user canceled the operation and QDialog.Accepted otherwise.
802
803        """
804        document = self.current_document()
805        if document.isModifiedStrict():
806            if self.ask_save_changes() == QDialog.Rejected:
807                return QDialog.Rejected
808
809        # TODO: Search for a temp backup scheme with per process
810        # locking.
811        if self.recent_schemes:
812            self.load_scheme(self.recent_schemes[0][1])
813
814        return QDialog.Accepted
815
816    def set_new_scheme(self, new_scheme):
817        """Set new_scheme as the current shown scheme.
818        """
819        scheme_doc = self.current_document()
820        old_scheme = scheme_doc.scheme()
821
822        manager = new_scheme.signal_manager
823        if self.freeze_action.isChecked():
824            manager.freeze().push()
825
826        scheme_doc.setScheme(new_scheme)
827
828        old_scheme.save_widget_settings()
829        old_scheme.deleteLater()
830
831    def ask_save_changes(self):
832        """Ask the user to save the changes to the current scheme.
833        Return QDialog.Accepted if the scheme was successfully saved
834        or the user selected to discard the changes. Otherwise return
835        QDialog.Rejected.
836
837        """
838        document = self.current_document()
839
840        selected = message_question(
841            self.tr("Do you want to save the changes you made to scheme %r?") \
842                    % document.scheme().title,
843            self.tr("Save Changes?"),
844            self.tr("If you do not save your changes will be lost"),
845            buttons=QMessageBox.Save | QMessageBox.Cancel | \
846                    QMessageBox.Discard,
847            default_button=QMessageBox.Save,
848            parent=self)
849
850        if selected == QMessageBox.Save:
851            return self.save_scheme()
852        elif selected == QMessageBox.Discard:
853            return QDialog.Accepted
854        elif selected == QMessageBox.Cancel:
855            return QDialog.Rejected
856
857    def save_scheme(self):
858        """Save the current scheme. If the scheme does not have an associated
859        path then prompt the user to select a scheme file. Return
860        QDialog.Accepted if the scheme was successfully saved and
861        QDialog.Rejected if the user canceled the file selection.
862
863        """
864        document = self.current_document()
865        curr_scheme = document.scheme()
866
867        if document.path():
868            curr_scheme.save_to(open(document.path(), "wb"))
869            document.setModified(False)
870            self.add_recent_scheme(curr_scheme.title, document.path())
871            return QDialog.Accepted
872        else:
873            return self.save_scheme_as()
874
875    def save_scheme_as(self):
876        """Save the current scheme by asking the user for a filename.
877        Return QFileDialog.Accepted if the scheme was saved successfully
878        and QFileDialog.Rejected if not.
879
880        """
881        document = self.current_document()
882        curr_scheme = document.scheme()
883
884        if document.path():
885            start_dir = document.path()
886        else:
887            if self.last_scheme_dir is not None:
888                start_dir = self.last_scheme_dir
889            else:
890                start_dir = QDesktopServices.storageLocation(
891                    QDesktopServices.DocumentsLocation
892                )
893
894            title = curr_scheme.title or "untitled"
895            start_dir = os.path.join(unicode(start_dir), title + ".ows")
896
897        filename = QFileDialog.getSaveFileName(
898            self, self.tr("Save Orange Scheme File"),
899            start_dir, self.tr("Orange Scheme (*.ows)")
900        )
901
902        if filename:
903            filename = unicode(filename)
904            dirname, basename = os.path.split(filename)
905            self.last_scheme_dir = dirname
906
907            try:
908                curr_scheme.save_to(open(filename, "wb"))
909            except Exception:
910                log.error("Error saving %r to %r", curr_scheme, filename,
911                          exc_info=True)
912                # Also show a message box
913                # TODO: should handle permission errors with a
914                # specialized messages.
915                message_critical(
916                     self.tr("An error occurred while trying to save the %r "
917                             "scheme to %r" % \
918                             (curr_scheme.title, basename)),
919                     title=self.tr("Error saving %r") % basename,
920                     exc_info=True,
921                     parent=self)
922                return QFileDialog.Rejected
923
924            document.setPath(filename)
925
926            document.setModified(False)
927            self.add_recent_scheme(curr_scheme.title, document.path())
928            return QFileDialog.Accepted
929        else:
930            return QFileDialog.Rejected
931
932    def get_started(self, *args):
933        """Show getting started video
934        """
935        url = QUrl(LINKS["start-using"])
936        QDesktopServices.openUrl(url)
937
938    def tutorial(self, *args):
939        """Show tutorial.
940        """
941        url = QUrl(LINKS["tutorial"])
942        QDesktopServices.openUrl(url)
943
944    def documentation(self, *args):
945        """Show reference documentation.
946        """
947        url = QUrl(LINKS["tutorial"])
948        QDesktopServices.openUrl(url)
949
950    def recent_scheme(self, *args):
951        """Browse recent schemes. Return QDialog.Rejected if the user
952        canceled the operation and QDialog.Accepted otherwise.
953
954        """
955        items = [previewmodel.PreviewItem(name=title, path=path)
956                 for title, path in self.recent_schemes]
957        model = previewmodel.PreviewModel(items=items)
958
959        dialog = previewdialog.PreviewDialog(self)
960        title = self.tr("Recent Schemes")
961        dialog.setWindowTitle(title)
962        template = ('<h3 style="font-size: 26px">\n'
963                    #'<img height="26" src="canvas_icons:Recent.svg">\n'
964                    '{0}\n'
965                    '</h3>')
966        dialog.setHeading(template.format(title))
967        dialog.setModel(model)
968
969        model.delayedScanUpdate()
970
971        status = dialog.exec_()
972
973        index = dialog.currentIndex()
974
975        dialog.deleteLater()
976
977        if status == QDialog.Accepted:
978            doc = self.current_document()
979            if doc.isModifiedStrict():
980                if self.ask_save_changes() == QDialog.Rejected:
981                    return QDialog.Rejected
982
983            selected = model.item(index)
984
985            self.load_scheme(unicode(selected.path()))
986
987        return status
988
989    def tutorial_scheme(self, *args):
990        """Browse a collection of tutorial schemes. Returns QDialog.Rejected
991        if the user canceled the dialog else loads the selected scheme into
992        the canvas and returns QDialog.Accepted.
993
994        """
995        tutors = tutorials.tutorials()
996        items = [previewmodel.PreviewItem(path=t.abspath()) for t in tutors]
997        model = previewmodel.PreviewModel(items=items)
998        dialog = previewdialog.PreviewDialog(self)
999        title = self.tr("Tutorials")
1000        dialog.setWindowTitle(title)
1001        template = ('<h3 style="font-size: 26px">\n'
1002                    #'<img height="26" src="canvas_icons:Tutorials.svg">\n'
1003                    '{0}\n'
1004                    '</h3>')
1005
1006        dialog.setHeading(template.format(title))
1007        dialog.setModel(model)
1008
1009        model.delayedScanUpdate()
1010        status = dialog.exec_()
1011        index = dialog.currentIndex()
1012
1013        dialog.deleteLater()
1014
1015        if status == QDialog.Accepted:
1016            doc = self.current_document()
1017            if doc.isModifiedStrict():
1018                if self.ask_save_changes() == QDialog.Rejected:
1019                    return QDialog.Rejected
1020
1021            selected = model.item(index)
1022
1023            new_scheme = self.new_scheme_from(unicode(selected.path()))
1024
1025            self.set_new_scheme(new_scheme)
1026
1027        return status
1028
1029    def welcome_dialog(self):
1030        """Show a modal welcome dialog for Orange Canvas.
1031        """
1032
1033        dialog = welcomedialog.WelcomeDialog(self)
1034        dialog.setWindowTitle(self.tr("Welcome to Orange Data Mining"))
1035
1036        def new_scheme():
1037            if self.new_scheme() == QDialog.Accepted:
1038                dialog.accept()
1039
1040        def open_scheme():
1041            if self.open_scheme() == QDialog.Accepted:
1042                dialog.accept()
1043
1044        def open_recent():
1045            if self.recent_scheme() == QDialog.Accepted:
1046                dialog.accept()
1047
1048        def tutorial():
1049            if self.tutorial_scheme() == QDialog.Accepted:
1050                dialog.accept()
1051
1052        new_action = \
1053            QAction(self.tr("New"), dialog,
1054                    toolTip=self.tr("Open a new scheme."),
1055                    triggered=new_scheme,
1056                    shortcut=QKeySequence.New,
1057                    icon=canvas_icons("New.svg")
1058                    )
1059
1060        open_action = \
1061            QAction(self.tr("Open"), dialog,
1062                    objectName="welcome-action-open",
1063                    toolTip=self.tr("Open a scheme."),
1064                    triggered=open_scheme,
1065                    shortcut=QKeySequence.Open,
1066                    icon=canvas_icons("Open.svg")
1067                    )
1068
1069        recent_action = \
1070            QAction(self.tr("Recent"), dialog,
1071                    objectName="welcome-recent-action",
1072                    toolTip=self.tr("Browse and open a recent scheme."),
1073                    triggered=open_recent,
1074                    shortcut=QKeySequence(Qt.ControlModifier | \
1075                                          (Qt.ShiftModifier | Qt.Key_R)),
1076                    icon=canvas_icons("Recent.svg")
1077                    )
1078
1079        tutorials_action = \
1080            QAction(self.tr("Tutorial"), dialog,
1081                    objectName="welcome-tutorial-action",
1082                    toolTip=self.tr("Browse tutorial schemes."),
1083                    triggered=tutorial,
1084                    icon=canvas_icons("Tutorials.svg")
1085                    )
1086
1087        top_row = [self.get_started_action, tutorials_action,
1088                   self.documentation_action]
1089
1090        self.new_action.triggered.connect(dialog.accept)
1091        bottom_row = [new_action, open_action, recent_action]
1092
1093        dialog.addRow(top_row, background="light-grass")
1094        dialog.addRow(bottom_row, background="light-orange")
1095
1096        settings = QSettings()
1097
1098        dialog.setShowAtStartup(
1099            settings.value("startup/show-welcome-screen", True).toBool()
1100        )
1101
1102        status = dialog.exec_()
1103
1104        settings.setValue("startup/show-welcome-screen",
1105                          dialog.showAtStartup())
1106
1107        dialog.deleteLater()
1108
1109        return status
1110
1111    def scheme_properties_dialog(self):
1112        """Return an empty `SchemeInfo` dialog instance.
1113        """
1114        settings = QSettings()
1115        value_key = "schemeinfo/show-at-new-scheme"
1116
1117        dialog = SchemeInfoDialog(self)
1118
1119        dialog.setWindowTitle(self.tr("Scheme Info"))
1120        dialog.setFixedSize(725, 450)
1121
1122        dialog.setDontShowAtNewScheme(
1123            not settings.value(value_key, True).toBool()
1124        )
1125
1126        return dialog
1127
1128    def show_scheme_properties(self):
1129        """Show current scheme properties.
1130        """
1131        settings = QSettings()
1132        value_key = "schemeinfo/show-at-new-scheme"
1133
1134        current_doc = self.current_document()
1135        scheme = current_doc.scheme()
1136        dlg = self.scheme_properties_dialog()
1137        dlg.setAutoCommit(False)
1138        dlg.setScheme(scheme)
1139        status = dlg.exec_()
1140
1141        if status == QDialog.Accepted:
1142            editor = dlg.editor
1143            stack = current_doc.undoStack()
1144            stack.beginMacro(self.tr("Change Info"))
1145            current_doc.setTitle(editor.title())
1146            current_doc.setDescription(editor.description())
1147            stack.endMacro()
1148
1149            # Store the check state.
1150            settings.setValue(value_key, not dlg.dontShowAtNewScheme())
1151        return status
1152
1153    def show_scheme_properties_for(self, scheme, window_title=None):
1154        """Show scheme properties for `scheme` with `window_title (if None
1155        a default 'Scheme Info' title will be used.
1156
1157        """
1158        settings = QSettings()
1159        value_key = "schemeinfo/show-at-new-scheme"
1160
1161        dialog = self.scheme_properties_dialog()
1162
1163        if window_title is not None:
1164            dialog.setWindowTitle(window_title)
1165
1166        dialog.setScheme(scheme)
1167
1168        status = dialog.exec_()
1169        if status == QDialog.Accepted:
1170            # Store the check state.
1171            settings.setValue(value_key, not dialog.dontShowAtNewScheme())
1172
1173        dialog.deleteLater()
1174
1175        return status
1176
1177    def set_signal_freeze(self, freeze):
1178        scheme = self.current_document().scheme()
1179        if freeze:
1180            scheme.signal_manager.freeze().push()
1181        else:
1182            scheme.signal_manager.freeze().pop()
1183
1184    def remove_selected(self):
1185        """Remove current scheme selection.
1186        """
1187        self.current_document().removeSelected()
1188
1189    def quit(self):
1190        """Quit the application.
1191        """
1192        self.close()
1193
1194    def select_all(self):
1195        self.current_document().selectAll()
1196
1197    def open_widget(self):
1198        """Open/raise selected widget's GUI.
1199        """
1200        self.current_document().openSelected()
1201
1202    def rename_widget(self):
1203        """Rename the current focused widget.
1204        """
1205        doc = self.current_document()
1206        nodes = doc.selectedNodes()
1207        if len(nodes) == 1:
1208            doc.editNodeTitle(nodes[0])
1209
1210    def widget_help(self):
1211        """Open widget help page.
1212        """
1213        doc = self.current_document()
1214        nodes = doc.selectedNodes()
1215        help_url = None
1216        if len(nodes) == 1:
1217            node = nodes[0]
1218            desc = node.description
1219            if desc.help:
1220                help_url = desc.help
1221
1222        if help_url is not None:
1223            QDesktopServices.openUrl(QUrl(help_url))
1224        else:
1225            message_information(
1226                self.tr("Sorry there is documentation available for "
1227                        "this widget."),
1228                parent=self)
1229
1230    def open_canvas_settings(self):
1231        """Open canvas settings/preferences dialog
1232        """
1233        dlg = UserSettingsDialog(self)
1234        dlg.show()
1235        status = dlg.exec_()
1236        if status == 0:
1237            self.__update_from_settings()
1238
1239    def show_output_view(self):
1240        """Show a window with application output.
1241        """
1242        self.output_dock.show()
1243
1244    def output_view(self):
1245        """Return the output text widget.
1246        """
1247        return self.output_dock.widget()
1248
1249    def open_about(self):
1250        """Open the about dialog.
1251        """
1252        dlg = AboutDialog(self)
1253        dlg.setAttribute(Qt.WA_DeleteOnClose)
1254        dlg.exec_()
1255
1256    def add_recent_scheme(self, title, path):
1257        """Add an entry (`title`, `path`) to the list of recent schemes.
1258        """
1259        if not path:
1260            # No associated persistent path so we can't do anything.
1261            return
1262
1263        if title is None:
1264            title = os.path.basename(path)
1265            title, _ = os.path.splitext(title)
1266
1267        filename = os.path.abspath(os.path.realpath(path))
1268        filename = os.path.normpath(filename)
1269
1270        actions_by_filename = {}
1271        for action in self.recent_scheme_action_group.actions():
1272            path = unicode(action.data().toString())
1273            actions_by_filename[path] = action
1274
1275        if filename in actions_by_filename:
1276            # Remove the title/filename (so it can be reinserted)
1277            recent_index = index(self.recent_schemes, filename,
1278                                 key=operator.itemgetter(1))
1279            self.recent_schemes.pop(recent_index)
1280
1281            action = actions_by_filename[filename]
1282            self.recent_menu.removeAction(action)
1283            action.setText(title or self.tr("untitled"))
1284        else:
1285            action = QAction(title or self.tr("untitled"), self,
1286                             toolTip=filename)
1287            action.setData(filename)
1288
1289        # Find the separator action in the menu (after 'Browse Recent')
1290        recent_actions = self.recent_menu.actions()
1291        begin_index = index(recent_actions, self.recent_menu_begin)
1292        action_before = recent_actions[begin_index + 1]
1293
1294        self.recent_menu.insertAction(action_before, action)
1295        self.recent_scheme_action_group.addAction(action)
1296        self.recent_schemes.insert(0, (title, filename))
1297
1298        config.save_recent_scheme_list(self.recent_schemes)
1299
1300    def clear_recent_schemes(self):
1301        """Clear list of recent schemes
1302        """
1303        actions = list(self.recent_menu.actions())
1304
1305        # Exclude permanent actions (Browse Recent, separators, Clear List)
1306        actions_to_remove = [action for action in actions \
1307                             if unicode(action.data().toString())]
1308
1309        for action in actions_to_remove:
1310            self.recent_menu.removeAction(action)
1311
1312        self.recent_schemes = []
1313        config.save_recent_scheme_list([])
1314
1315    def _on_recent_scheme_action(self, action):
1316        """A recent scheme action was triggered by the user
1317        """
1318        document = self.current_document()
1319        if document.isModifiedStrict():
1320            if self.ask_save_changes() == QDialog.Rejected:
1321                return
1322
1323        filename = unicode(action.data().toString())
1324        self.load_scheme(filename)
1325
1326    def _on_dock_location_changed(self, location):
1327        """Location of the dock_widget has changed, fix the margins
1328        if necessary.
1329
1330        """
1331        self.__update_scheme_margins()
1332
1333    def createPopupMenu(self):
1334        # Override the default context menu popup (we don't want the user to
1335        # be able to hide the tool dock widget).
1336        return None
1337
1338    def closeEvent(self, event):
1339        """Close the main window.
1340        """
1341        document = self.current_document()
1342        if document.isModifiedStrict():
1343            if self.ask_save_changes() == QDialog.Rejected:
1344                # Reject the event
1345                event.ignore()
1346                return
1347
1348        scheme = document.scheme()
1349        scheme.save_widget_settings()
1350
1351        # Set an empty scheme to clear the document
1352        document.setScheme(widgetsscheme.WidgetsScheme())
1353        document.deleteLater()
1354
1355        config.save_config()
1356
1357        geometry = self.saveGeometry()
1358        state = self.saveState(version=self.SETTINGS_VERSION)
1359        settings = QSettings()
1360        settings.beginGroup("mainwindow")
1361        settings.setValue("geometry", geometry)
1362        settings.setValue("state", state)
1363        settings.setValue("canvasdock/expanded",
1364                          self.dock_widget.expanded())
1365        settings.setValue("scheme-margins-enabled",
1366                          self.scheme_margins_enabled)
1367
1368        settings.setValue("last-scheme-dir", self.last_scheme_dir)
1369        settings.setValue("widgettoolbox/state",
1370                          self.widgets_tool_box.saveState())
1371
1372        settings.setValue("quick-help/visible",
1373                          self.canvas_tool_dock.quickHelpVisible())
1374
1375        settings.endGroup()
1376
1377        event.accept()
1378
1379        # Close any windows left.
1380        application = QApplication.instance()
1381        QTimer.singleShot(0, application.closeAllWindows)
1382
1383    def showEvent(self, event):
1384        settings = QSettings()
1385        settings.beginGroup("mainwindow")
1386
1387        # Restore geometry and dock/toolbar state
1388        state = settings.value("state")
1389        if state.isValid():
1390            self.restoreState(state.toByteArray(),
1391                              version=self.SETTINGS_VERSION)
1392        geom_data = settings.value("geometry")
1393        if geom_data.isValid():
1394            self.restoreGeometry(geom_data.toByteArray())
1395
1396        return QMainWindow.showEvent(self, event)
1397
1398    def event(self, event):
1399        if event.type() == QEvent.StatusTip and \
1400                isinstance(event, QuickHelpTipEvent):
1401            # Using singleShot to update the text browser.
1402            # If updating directly the application experiences strange random
1403            # segfaults (in ~StatusTipEvent in QTextLayout or event just normal
1404            # event loop), but only when the contents are larger then the
1405            # QTextBrowser's viewport.
1406            if event.priority() == QuickHelpTipEvent.Normal:
1407                QTimer.singleShot(0, partial(self.dock_help.showHelp,
1408                                             event.html()))
1409            elif event.priority() == QuickHelpTipEvent.Temporary:
1410                QTimer.singleShot(0, partial(self.dock_help.showHelp,
1411                                             event.html(), event.timout()))
1412            elif event.priority() == QuickHelpTipEvent.Permanent:
1413                QTimer.singleShot(0, partial(self.dock_help.showPermanentHelp,
1414                                             event.html()))
1415
1416            return True
1417
1418        return QMainWindow.event(self, event)
1419
1420    # Mac OS X
1421    if sys.platform == "darwin":
1422        def toggleMaximized(self):
1423            """Toggle normal/maximized window state.
1424            """
1425            if self.isMinimized():
1426                # Do nothing if window is minimized
1427                return
1428
1429            if self.isMaximized():
1430                self.showNormal()
1431            else:
1432                self.showMaximized()
1433
1434        def changeEvent(self, event):
1435            if event.type() == QEvent.WindowStateChange:
1436                # Enable/disable window menu based on minimized state
1437                self.window_menu.setEnabled(not self.isMinimized())
1438            QMainWindow.changeEvent(self, event)
1439
1440    def tr(self, sourceText, disambiguation=None, n=-1):
1441        """Translate the string.
1442        """
1443        return unicode(QMainWindow.tr(self, sourceText, disambiguation, n))
1444
1445    def __update_from_settings(self):
1446        settings = QSettings()
1447        settings.beginGroup("mainwindow")
1448        toolbox_floatable = settings.value("toolbox-dock-floatable",
1449                                           defaultValue=False)
1450
1451        features = self.dock_widget.features()
1452        features = updated_flags(features, QDockWidget.DockWidgetFloatable,
1453                                 toolbox_floatable.toBool())
1454        self.dock_widget.setFeatures(features)
1455
1456        toolbox_exclusive = settings.value("toolbox-dock-exclusive",
1457                                           defaultValue=False)
1458        self.widgets_tool_box.setExclusive(toolbox_exclusive.toBool())
1459
1460        settings.endGroup()
1461        settings.beginGroup("quickmenu")
1462
1463        triggers = 0
1464        dbl_click = settings.value("trigger-on-double-click",
1465                                   defaultValue=True)
1466        if dbl_click.toBool():
1467            triggers |= SchemeEditWidget.DoubleClicked
1468
1469        left_click = settings.value("trigger-on-left-click",
1470                                    defaultValue=False)
1471        if left_click.toBool():
1472            triggers |= SchemeEditWidget.Clicked
1473
1474        space_press = settings.value("trigger-on-space-key",
1475                                     defaultValue=True)
1476        if space_press.toBool():
1477            triggers |= SchemeEditWidget.SpaceKey
1478
1479        any_press = settings.value("trigger-on-any-key",
1480                                   defaultValue=False)
1481        if any_press.toBool():
1482            triggers |= SchemeEditWidget.AnyKey
1483
1484        self.scheme_widget.setQuickMenuTriggers(triggers)
1485
1486
1487def updated_flags(flags, mask, state):
1488    if state:
1489        flags |= mask
1490    else:
1491        flags &= ~mask
1492    return flags
1493
1494
1495def identity(item):
1496    return item
1497
1498
1499def index(sequence, *what, **kwargs):
1500    """index(sequence, what, [key=None, [predicate=None]])
1501    Return index of `what` in `sequence`.
1502    """
1503    what = what[0]
1504    key = kwargs.get("key", identity)
1505    predicate = kwargs.get("predicate", operator.eq)
1506    for i, item in enumerate(sequence):
1507        item_key = key(item)
1508        if predicate(what, item_key):
1509            return i
1510    raise ValueError("%r not in sequence" % what)
Note: See TracBrowser for help on using the repository browser.