source: orange/Orange/OrangeCanvas/application/canvasmain.py @ 11259:93eddd7eed11

Revision 11259:93eddd7eed11, 51.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 16 months ago (diff)

Set the channel names visiblity state from the settings.

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        self.__update_from_settings()
593
594    def set_document_title(self, title):
595        """Set the document title (and the main window title). If `title`
596        is an empty string a default 'untitled' placeholder will be used.
597
598        """
599        if self.__document_title != title:
600            self.__document_title = title
601
602            if not title:
603                # TODO: should the default name be platform specific
604                title = self.tr("untitled")
605
606            self.setWindowTitle(title + "[*]")
607
608    def document_title(self):
609        """Return the document title.
610        """
611        return self.__document_title
612
613    def set_widget_registry(self, widget_registry):
614        """Set widget registry.
615        """
616        if self.widget_registry is not None:
617            # Clear the dock widget and popup.
618            pass
619
620        self.widget_registry = widget_registry
621        self.widgets_tool_box.setModel(widget_registry.model())
622        self.quick_category.setModel(widget_registry.model())
623
624        self.scheme_widget.setRegistry(widget_registry)
625
626        # Restore possibly saved widget toolbox tab states
627        settings = QSettings()
628        state = settings.value("mainwindow/widgettoolbox/state",
629                                defaultValue=None)
630        state = state.toPyObject()
631        if state:
632            self.widgets_tool_box.restoreState(state)
633
634    def set_quick_help_text(self, text):
635        self.canvas_tool_dock.help.setText(text)
636
637    def current_document(self):
638        return self.scheme_widget
639
640    def on_tool_box_widget_activated(self, action):
641        """A widget action in the widget toolbox has been activated.
642        """
643        widget_desc = action.data().toPyObject()
644        if widget_desc:
645            scheme_widget = self.current_document()
646            if scheme_widget:
647                scheme_widget.createNewNode(widget_desc)
648
649    def on_quick_category_action(self, action):
650        """The quick category menu action triggered.
651        """
652        category = action.text()
653        for i in range(self.widgets_tool_box.count()):
654            cat_act = self.widgets_tool_box.tabAction(i)
655            if cat_act.text() == category:
656                if not cat_act.isChecked():
657                    # Trigger the action to expand the tool grid contained
658                    # within.
659                    cat_act.trigger()
660
661            else:
662                if cat_act.isChecked():
663                    # Trigger the action to hide the tool grid contained
664                    # within.
665                    cat_act.trigger()
666
667        self.dock_widget.expand()
668
669    def set_scheme_margins_enabled(self, enabled):
670        """Enable/disable the margins around the scheme document.
671        """
672        if self.__scheme_margins_enabled != enabled:
673            self.__scheme_margins_enabled = enabled
674            self.__update_scheme_margins()
675
676    def scheme_margins_enabled(self):
677        return self.__scheme_margins_enabled
678
679    scheme_margins_enabled = Property(bool,
680                                      fget=scheme_margins_enabled,
681                                      fset=set_scheme_margins_enabled)
682
683    def __update_scheme_margins(self):
684        """Update the margins around the scheme document.
685        """
686        enabled = self.__scheme_margins_enabled
687        self.__dummy_top_toolbar.setVisible(enabled)
688        self.__dummy_bottom_toolbar.setVisible(enabled)
689        central = self.centralWidget()
690
691        margin = 20 if enabled else 0
692
693        if self.dockWidgetArea(self.dock_widget) == Qt.LeftDockWidgetArea:
694            margins = (margin / 2, 0, margin, 0)
695        else:
696            margins = (margin, 0, margin / 2, 0)
697
698        central.layout().setContentsMargins(*margins)
699
700    #################
701    # Action handlers
702    #################
703    def new_scheme(self):
704        """New scheme. Return QDialog.Rejected if the user canceled
705        the operation and QDialog.Accepted otherwise.
706
707        """
708        document = self.current_document()
709        if document.isModifiedStrict():
710            # Ask for save changes
711            if self.ask_save_changes() == QDialog.Rejected:
712                return QDialog.Rejected
713
714        new_scheme = widgetsscheme.WidgetsScheme()
715
716        settings = QSettings()
717        show = settings.value("schemeinfo/show-at-new-scheme", True).toBool()
718
719        if show:
720            status = self.show_scheme_properties_for(
721                new_scheme, self.tr("New Scheme")
722            )
723
724            if status == QDialog.Rejected:
725                return QDialog.Rejected
726
727        self.set_new_scheme(new_scheme)
728
729        return QDialog.Accepted
730
731    def open_scheme(self):
732        """Open a new scheme. Return QDialog.Rejected if the user canceled
733        the operation and QDialog.Accepted otherwise.
734
735        """
736        document = self.current_document()
737        if document.isModifiedStrict():
738            if self.ask_save_changes() == QDialog.Rejected:
739                return QDialog.Rejected
740
741        if self.last_scheme_dir is None:
742            # Get user 'Documents' folder
743            start_dir = QDesktopServices.storageLocation(
744                            QDesktopServices.DocumentsLocation)
745        else:
746            start_dir = self.last_scheme_dir
747
748        # TODO: Use a dialog instance and use 'addSidebarUrls' to
749        # set one or more extra sidebar locations where Schemes are stored.
750        # Also use setHistory
751        filename = QFileDialog.getOpenFileName(
752            self, self.tr("Open Orange Scheme File"),
753            start_dir, self.tr("Orange Scheme (*.ows)"),
754        )
755
756        if filename:
757            self.load_scheme(filename)
758            return QDialog.Accepted
759        else:
760            return QDialog.Rejected
761
762    def load_scheme(self, filename):
763        """Load a scheme from a file (`filename`) into the current
764        document updates the recent scheme list and the loaded scheme path
765        property.
766
767        """
768        filename = unicode(filename)
769        dirname = os.path.dirname(filename)
770
771        self.last_scheme_dir = dirname
772
773        new_scheme = self.new_scheme_from(filename)
774
775        self.set_new_scheme(new_scheme)
776
777        scheme_doc_widget = self.current_document()
778        scheme_doc_widget.setPath(filename)
779
780        self.add_recent_scheme(new_scheme.title, filename)
781
782    def new_scheme_from(self, filename):
783        """Create and return a new :class:`widgetsscheme.WidgetsScheme`
784        from a saved `filename`.
785
786        """
787        new_scheme = widgetsscheme.WidgetsScheme()
788        try:
789            new_scheme.load_from(open(filename, "rb"))
790        except Exception:
791            message_critical(
792                 self.tr("Could not load Orange Scheme file"),
793                 title=self.tr("Error"),
794                 informative_text=self.tr("An unexpected error occurred"),
795                 exc_info=True,
796                 parent=self)
797            return None
798
799        return new_scheme
800
801    def reload_last(self):
802        """Reload last opened scheme. Return QDialog.Rejected if the
803        user canceled the operation and QDialog.Accepted otherwise.
804
805        """
806        document = self.current_document()
807        if document.isModifiedStrict():
808            if self.ask_save_changes() == QDialog.Rejected:
809                return QDialog.Rejected
810
811        # TODO: Search for a temp backup scheme with per process
812        # locking.
813        if self.recent_schemes:
814            self.load_scheme(self.recent_schemes[0][1])
815
816        return QDialog.Accepted
817
818    def set_new_scheme(self, new_scheme):
819        """Set new_scheme as the current shown scheme.
820        """
821        scheme_doc = self.current_document()
822        old_scheme = scheme_doc.scheme()
823
824        manager = new_scheme.signal_manager
825        if self.freeze_action.isChecked():
826            manager.freeze().push()
827
828        scheme_doc.setScheme(new_scheme)
829
830        old_scheme.save_widget_settings()
831        old_scheme.deleteLater()
832
833    def ask_save_changes(self):
834        """Ask the user to save the changes to the current scheme.
835        Return QDialog.Accepted if the scheme was successfully saved
836        or the user selected to discard the changes. Otherwise return
837        QDialog.Rejected.
838
839        """
840        document = self.current_document()
841
842        selected = message_question(
843            self.tr("Do you want to save the changes you made to scheme %r?") \
844                    % document.scheme().title,
845            self.tr("Save Changes?"),
846            self.tr("If you do not save your changes will be lost"),
847            buttons=QMessageBox.Save | QMessageBox.Cancel | \
848                    QMessageBox.Discard,
849            default_button=QMessageBox.Save,
850            parent=self)
851
852        if selected == QMessageBox.Save:
853            return self.save_scheme()
854        elif selected == QMessageBox.Discard:
855            return QDialog.Accepted
856        elif selected == QMessageBox.Cancel:
857            return QDialog.Rejected
858
859    def save_scheme(self):
860        """Save the current scheme. If the scheme does not have an associated
861        path then prompt the user to select a scheme file. Return
862        QDialog.Accepted if the scheme was successfully saved and
863        QDialog.Rejected if the user canceled the file selection.
864
865        """
866        document = self.current_document()
867        curr_scheme = document.scheme()
868
869        if document.path():
870            curr_scheme.save_to(open(document.path(), "wb"))
871            document.setModified(False)
872            self.add_recent_scheme(curr_scheme.title, document.path())
873            return QDialog.Accepted
874        else:
875            return self.save_scheme_as()
876
877    def save_scheme_as(self):
878        """Save the current scheme by asking the user for a filename.
879        Return QFileDialog.Accepted if the scheme was saved successfully
880        and QFileDialog.Rejected if not.
881
882        """
883        document = self.current_document()
884        curr_scheme = document.scheme()
885
886        if document.path():
887            start_dir = document.path()
888        else:
889            if self.last_scheme_dir is not None:
890                start_dir = self.last_scheme_dir
891            else:
892                start_dir = QDesktopServices.storageLocation(
893                    QDesktopServices.DocumentsLocation
894                )
895
896            title = curr_scheme.title or "untitled"
897            start_dir = os.path.join(unicode(start_dir), title + ".ows")
898
899        filename = QFileDialog.getSaveFileName(
900            self, self.tr("Save Orange Scheme File"),
901            start_dir, self.tr("Orange Scheme (*.ows)")
902        )
903
904        if filename:
905            filename = unicode(filename)
906            dirname, basename = os.path.split(filename)
907            self.last_scheme_dir = dirname
908
909            try:
910                curr_scheme.save_to(open(filename, "wb"))
911            except Exception:
912                log.error("Error saving %r to %r", curr_scheme, filename,
913                          exc_info=True)
914                # Also show a message box
915                # TODO: should handle permission errors with a
916                # specialized messages.
917                message_critical(
918                     self.tr("An error occurred while trying to save the %r "
919                             "scheme to %r" % \
920                             (curr_scheme.title, basename)),
921                     title=self.tr("Error saving %r") % basename,
922                     exc_info=True,
923                     parent=self)
924                return QFileDialog.Rejected
925
926            document.setPath(filename)
927
928            document.setModified(False)
929            self.add_recent_scheme(curr_scheme.title, document.path())
930            return QFileDialog.Accepted
931        else:
932            return QFileDialog.Rejected
933
934    def get_started(self, *args):
935        """Show getting started video
936        """
937        url = QUrl(LINKS["start-using"])
938        QDesktopServices.openUrl(url)
939
940    def tutorial(self, *args):
941        """Show tutorial.
942        """
943        url = QUrl(LINKS["tutorial"])
944        QDesktopServices.openUrl(url)
945
946    def documentation(self, *args):
947        """Show reference documentation.
948        """
949        url = QUrl(LINKS["tutorial"])
950        QDesktopServices.openUrl(url)
951
952    def recent_scheme(self, *args):
953        """Browse recent schemes. Return QDialog.Rejected if the user
954        canceled the operation and QDialog.Accepted otherwise.
955
956        """
957        items = [previewmodel.PreviewItem(name=title, path=path)
958                 for title, path in self.recent_schemes]
959        model = previewmodel.PreviewModel(items=items)
960
961        dialog = previewdialog.PreviewDialog(self)
962        title = self.tr("Recent Schemes")
963        dialog.setWindowTitle(title)
964        template = ('<h3 style="font-size: 26px">\n'
965                    #'<img height="26" src="canvas_icons:Recent.svg">\n'
966                    '{0}\n'
967                    '</h3>')
968        dialog.setHeading(template.format(title))
969        dialog.setModel(model)
970
971        model.delayedScanUpdate()
972
973        status = dialog.exec_()
974
975        index = dialog.currentIndex()
976
977        dialog.deleteLater()
978
979        if status == QDialog.Accepted:
980            doc = self.current_document()
981            if doc.isModifiedStrict():
982                if self.ask_save_changes() == QDialog.Rejected:
983                    return QDialog.Rejected
984
985            selected = model.item(index)
986
987            self.load_scheme(unicode(selected.path()))
988
989        return status
990
991    def tutorial_scheme(self, *args):
992        """Browse a collection of tutorial schemes. Returns QDialog.Rejected
993        if the user canceled the dialog else loads the selected scheme into
994        the canvas and returns QDialog.Accepted.
995
996        """
997        tutors = tutorials.tutorials()
998        items = [previewmodel.PreviewItem(path=t.abspath()) for t in tutors]
999        model = previewmodel.PreviewModel(items=items)
1000        dialog = previewdialog.PreviewDialog(self)
1001        title = self.tr("Tutorials")
1002        dialog.setWindowTitle(title)
1003        template = ('<h3 style="font-size: 26px">\n'
1004                    #'<img height="26" src="canvas_icons:Tutorials.svg">\n'
1005                    '{0}\n'
1006                    '</h3>')
1007
1008        dialog.setHeading(template.format(title))
1009        dialog.setModel(model)
1010
1011        model.delayedScanUpdate()
1012        status = dialog.exec_()
1013        index = dialog.currentIndex()
1014
1015        dialog.deleteLater()
1016
1017        if status == QDialog.Accepted:
1018            doc = self.current_document()
1019            if doc.isModifiedStrict():
1020                if self.ask_save_changes() == QDialog.Rejected:
1021                    return QDialog.Rejected
1022
1023            selected = model.item(index)
1024
1025            new_scheme = self.new_scheme_from(unicode(selected.path()))
1026
1027            self.set_new_scheme(new_scheme)
1028
1029        return status
1030
1031    def welcome_dialog(self):
1032        """Show a modal welcome dialog for Orange Canvas.
1033        """
1034
1035        dialog = welcomedialog.WelcomeDialog(self)
1036        dialog.setWindowTitle(self.tr("Welcome to Orange Data Mining"))
1037
1038        def new_scheme():
1039            if self.new_scheme() == QDialog.Accepted:
1040                dialog.accept()
1041
1042        def open_scheme():
1043            if self.open_scheme() == QDialog.Accepted:
1044                dialog.accept()
1045
1046        def open_recent():
1047            if self.recent_scheme() == QDialog.Accepted:
1048                dialog.accept()
1049
1050        def tutorial():
1051            if self.tutorial_scheme() == QDialog.Accepted:
1052                dialog.accept()
1053
1054        new_action = \
1055            QAction(self.tr("New"), dialog,
1056                    toolTip=self.tr("Open a new scheme."),
1057                    triggered=new_scheme,
1058                    shortcut=QKeySequence.New,
1059                    icon=canvas_icons("New.svg")
1060                    )
1061
1062        open_action = \
1063            QAction(self.tr("Open"), dialog,
1064                    objectName="welcome-action-open",
1065                    toolTip=self.tr("Open a scheme."),
1066                    triggered=open_scheme,
1067                    shortcut=QKeySequence.Open,
1068                    icon=canvas_icons("Open.svg")
1069                    )
1070
1071        recent_action = \
1072            QAction(self.tr("Recent"), dialog,
1073                    objectName="welcome-recent-action",
1074                    toolTip=self.tr("Browse and open a recent scheme."),
1075                    triggered=open_recent,
1076                    shortcut=QKeySequence(Qt.ControlModifier | \
1077                                          (Qt.ShiftModifier | Qt.Key_R)),
1078                    icon=canvas_icons("Recent.svg")
1079                    )
1080
1081        tutorials_action = \
1082            QAction(self.tr("Tutorial"), dialog,
1083                    objectName="welcome-tutorial-action",
1084                    toolTip=self.tr("Browse tutorial schemes."),
1085                    triggered=tutorial,
1086                    icon=canvas_icons("Tutorials.svg")
1087                    )
1088
1089        top_row = [self.get_started_action, tutorials_action,
1090                   self.documentation_action]
1091
1092        self.new_action.triggered.connect(dialog.accept)
1093        bottom_row = [new_action, open_action, recent_action]
1094
1095        dialog.addRow(top_row, background="light-grass")
1096        dialog.addRow(bottom_row, background="light-orange")
1097
1098        settings = QSettings()
1099
1100        dialog.setShowAtStartup(
1101            settings.value("startup/show-welcome-screen", True).toBool()
1102        )
1103
1104        status = dialog.exec_()
1105
1106        settings.setValue("startup/show-welcome-screen",
1107                          dialog.showAtStartup())
1108
1109        dialog.deleteLater()
1110
1111        return status
1112
1113    def scheme_properties_dialog(self):
1114        """Return an empty `SchemeInfo` dialog instance.
1115        """
1116        settings = QSettings()
1117        value_key = "schemeinfo/show-at-new-scheme"
1118
1119        dialog = SchemeInfoDialog(self)
1120
1121        dialog.setWindowTitle(self.tr("Scheme Info"))
1122        dialog.setFixedSize(725, 450)
1123
1124        dialog.setDontShowAtNewScheme(
1125            not settings.value(value_key, True).toBool()
1126        )
1127
1128        return dialog
1129
1130    def show_scheme_properties(self):
1131        """Show current scheme properties.
1132        """
1133        settings = QSettings()
1134        value_key = "schemeinfo/show-at-new-scheme"
1135
1136        current_doc = self.current_document()
1137        scheme = current_doc.scheme()
1138        dlg = self.scheme_properties_dialog()
1139        dlg.setAutoCommit(False)
1140        dlg.setScheme(scheme)
1141        status = dlg.exec_()
1142
1143        if status == QDialog.Accepted:
1144            editor = dlg.editor
1145            stack = current_doc.undoStack()
1146            stack.beginMacro(self.tr("Change Info"))
1147            current_doc.setTitle(editor.title())
1148            current_doc.setDescription(editor.description())
1149            stack.endMacro()
1150
1151            # Store the check state.
1152            settings.setValue(value_key, not dlg.dontShowAtNewScheme())
1153        return status
1154
1155    def show_scheme_properties_for(self, scheme, window_title=None):
1156        """Show scheme properties for `scheme` with `window_title (if None
1157        a default 'Scheme Info' title will be used.
1158
1159        """
1160        settings = QSettings()
1161        value_key = "schemeinfo/show-at-new-scheme"
1162
1163        dialog = self.scheme_properties_dialog()
1164
1165        if window_title is not None:
1166            dialog.setWindowTitle(window_title)
1167
1168        dialog.setScheme(scheme)
1169
1170        status = dialog.exec_()
1171        if status == QDialog.Accepted:
1172            # Store the check state.
1173            settings.setValue(value_key, not dialog.dontShowAtNewScheme())
1174
1175        dialog.deleteLater()
1176
1177        return status
1178
1179    def set_signal_freeze(self, freeze):
1180        scheme = self.current_document().scheme()
1181        if freeze:
1182            scheme.signal_manager.freeze().push()
1183        else:
1184            scheme.signal_manager.freeze().pop()
1185
1186    def remove_selected(self):
1187        """Remove current scheme selection.
1188        """
1189        self.current_document().removeSelected()
1190
1191    def quit(self):
1192        """Quit the application.
1193        """
1194        self.close()
1195
1196    def select_all(self):
1197        self.current_document().selectAll()
1198
1199    def open_widget(self):
1200        """Open/raise selected widget's GUI.
1201        """
1202        self.current_document().openSelected()
1203
1204    def rename_widget(self):
1205        """Rename the current focused widget.
1206        """
1207        doc = self.current_document()
1208        nodes = doc.selectedNodes()
1209        if len(nodes) == 1:
1210            doc.editNodeTitle(nodes[0])
1211
1212    def widget_help(self):
1213        """Open widget help page.
1214        """
1215        doc = self.current_document()
1216        nodes = doc.selectedNodes()
1217        help_url = None
1218        if len(nodes) == 1:
1219            node = nodes[0]
1220            desc = node.description
1221            if desc.help:
1222                help_url = desc.help
1223
1224        if help_url is not None:
1225            QDesktopServices.openUrl(QUrl(help_url))
1226        else:
1227            message_information(
1228                self.tr("Sorry there is documentation available for "
1229                        "this widget."),
1230                parent=self)
1231
1232    def open_canvas_settings(self):
1233        """Open canvas settings/preferences dialog
1234        """
1235        dlg = UserSettingsDialog(self)
1236        dlg.show()
1237        status = dlg.exec_()
1238        if status == 0:
1239            self.__update_from_settings()
1240
1241    def show_output_view(self):
1242        """Show a window with application output.
1243        """
1244        self.output_dock.show()
1245
1246    def output_view(self):
1247        """Return the output text widget.
1248        """
1249        return self.output_dock.widget()
1250
1251    def open_about(self):
1252        """Open the about dialog.
1253        """
1254        dlg = AboutDialog(self)
1255        dlg.setAttribute(Qt.WA_DeleteOnClose)
1256        dlg.exec_()
1257
1258    def add_recent_scheme(self, title, path):
1259        """Add an entry (`title`, `path`) to the list of recent schemes.
1260        """
1261        if not path:
1262            # No associated persistent path so we can't do anything.
1263            return
1264
1265        if title is None:
1266            title = os.path.basename(path)
1267            title, _ = os.path.splitext(title)
1268
1269        filename = os.path.abspath(os.path.realpath(path))
1270        filename = os.path.normpath(filename)
1271
1272        actions_by_filename = {}
1273        for action in self.recent_scheme_action_group.actions():
1274            path = unicode(action.data().toString())
1275            actions_by_filename[path] = action
1276
1277        if filename in actions_by_filename:
1278            # Remove the title/filename (so it can be reinserted)
1279            recent_index = index(self.recent_schemes, filename,
1280                                 key=operator.itemgetter(1))
1281            self.recent_schemes.pop(recent_index)
1282
1283            action = actions_by_filename[filename]
1284            self.recent_menu.removeAction(action)
1285            action.setText(title or self.tr("untitled"))
1286        else:
1287            action = QAction(title or self.tr("untitled"), self,
1288                             toolTip=filename)
1289            action.setData(filename)
1290
1291        # Find the separator action in the menu (after 'Browse Recent')
1292        recent_actions = self.recent_menu.actions()
1293        begin_index = index(recent_actions, self.recent_menu_begin)
1294        action_before = recent_actions[begin_index + 1]
1295
1296        self.recent_menu.insertAction(action_before, action)
1297        self.recent_scheme_action_group.addAction(action)
1298        self.recent_schemes.insert(0, (title, filename))
1299
1300        config.save_recent_scheme_list(self.recent_schemes)
1301
1302    def clear_recent_schemes(self):
1303        """Clear list of recent schemes
1304        """
1305        actions = list(self.recent_menu.actions())
1306
1307        # Exclude permanent actions (Browse Recent, separators, Clear List)
1308        actions_to_remove = [action for action in actions \
1309                             if unicode(action.data().toString())]
1310
1311        for action in actions_to_remove:
1312            self.recent_menu.removeAction(action)
1313
1314        self.recent_schemes = []
1315        config.save_recent_scheme_list([])
1316
1317    def _on_recent_scheme_action(self, action):
1318        """A recent scheme action was triggered by the user
1319        """
1320        document = self.current_document()
1321        if document.isModifiedStrict():
1322            if self.ask_save_changes() == QDialog.Rejected:
1323                return
1324
1325        filename = unicode(action.data().toString())
1326        self.load_scheme(filename)
1327
1328    def _on_dock_location_changed(self, location):
1329        """Location of the dock_widget has changed, fix the margins
1330        if necessary.
1331
1332        """
1333        self.__update_scheme_margins()
1334
1335    def createPopupMenu(self):
1336        # Override the default context menu popup (we don't want the user to
1337        # be able to hide the tool dock widget).
1338        return None
1339
1340    def closeEvent(self, event):
1341        """Close the main window.
1342        """
1343        document = self.current_document()
1344        if document.isModifiedStrict():
1345            if self.ask_save_changes() == QDialog.Rejected:
1346                # Reject the event
1347                event.ignore()
1348                return
1349
1350        scheme = document.scheme()
1351        scheme.save_widget_settings()
1352
1353        # Set an empty scheme to clear the document
1354        document.setScheme(widgetsscheme.WidgetsScheme())
1355        document.deleteLater()
1356
1357        config.save_config()
1358
1359        geometry = self.saveGeometry()
1360        state = self.saveState(version=self.SETTINGS_VERSION)
1361        settings = QSettings()
1362        settings.beginGroup("mainwindow")
1363        settings.setValue("geometry", geometry)
1364        settings.setValue("state", state)
1365        settings.setValue("canvasdock/expanded",
1366                          self.dock_widget.expanded())
1367        settings.setValue("scheme-margins-enabled",
1368                          self.scheme_margins_enabled)
1369
1370        settings.setValue("last-scheme-dir", self.last_scheme_dir)
1371        settings.setValue("widgettoolbox/state",
1372                          self.widgets_tool_box.saveState())
1373
1374        settings.setValue("quick-help/visible",
1375                          self.canvas_tool_dock.quickHelpVisible())
1376
1377        settings.endGroup()
1378
1379        event.accept()
1380
1381        # Close any windows left.
1382        application = QApplication.instance()
1383        QTimer.singleShot(0, application.closeAllWindows)
1384
1385    def showEvent(self, event):
1386        settings = QSettings()
1387        settings.beginGroup("mainwindow")
1388
1389        # Restore geometry and dock/toolbar state
1390        state = settings.value("state")
1391        if state.isValid():
1392            self.restoreState(state.toByteArray(),
1393                              version=self.SETTINGS_VERSION)
1394        geom_data = settings.value("geometry")
1395        if geom_data.isValid():
1396            self.restoreGeometry(geom_data.toByteArray())
1397
1398        return QMainWindow.showEvent(self, event)
1399
1400    def event(self, event):
1401        if event.type() == QEvent.StatusTip and \
1402                isinstance(event, QuickHelpTipEvent):
1403            # Using singleShot to update the text browser.
1404            # If updating directly the application experiences strange random
1405            # segfaults (in ~StatusTipEvent in QTextLayout or event just normal
1406            # event loop), but only when the contents are larger then the
1407            # QTextBrowser's viewport.
1408            if event.priority() == QuickHelpTipEvent.Normal:
1409                QTimer.singleShot(0, partial(self.dock_help.showHelp,
1410                                             event.html()))
1411            elif event.priority() == QuickHelpTipEvent.Temporary:
1412                QTimer.singleShot(0, partial(self.dock_help.showHelp,
1413                                             event.html(), event.timout()))
1414            elif event.priority() == QuickHelpTipEvent.Permanent:
1415                QTimer.singleShot(0, partial(self.dock_help.showPermanentHelp,
1416                                             event.html()))
1417
1418            return True
1419
1420        return QMainWindow.event(self, event)
1421
1422    # Mac OS X
1423    if sys.platform == "darwin":
1424        def toggleMaximized(self):
1425            """Toggle normal/maximized window state.
1426            """
1427            if self.isMinimized():
1428                # Do nothing if window is minimized
1429                return
1430
1431            if self.isMaximized():
1432                self.showNormal()
1433            else:
1434                self.showMaximized()
1435
1436        def changeEvent(self, event):
1437            if event.type() == QEvent.WindowStateChange:
1438                # Enable/disable window menu based on minimized state
1439                self.window_menu.setEnabled(not self.isMinimized())
1440            QMainWindow.changeEvent(self, event)
1441
1442    def tr(self, sourceText, disambiguation=None, n=-1):
1443        """Translate the string.
1444        """
1445        return unicode(QMainWindow.tr(self, sourceText, disambiguation, n))
1446
1447    def __update_from_settings(self):
1448        settings = QSettings()
1449        settings.beginGroup("mainwindow")
1450        toolbox_floatable = settings.value("toolbox-dock-floatable",
1451                                           defaultValue=False)
1452
1453        features = self.dock_widget.features()
1454        features = updated_flags(features, QDockWidget.DockWidgetFloatable,
1455                                 toolbox_floatable.toBool())
1456        self.dock_widget.setFeatures(features)
1457
1458        toolbox_exclusive = settings.value("toolbox-dock-exclusive",
1459                                           defaultValue=False)
1460        self.widgets_tool_box.setExclusive(toolbox_exclusive.toBool())
1461
1462        settings.endGroup()
1463        settings.beginGroup("quickmenu")
1464
1465        triggers = 0
1466        dbl_click = settings.value("trigger-on-double-click",
1467                                   defaultValue=True)
1468        if dbl_click.toBool():
1469            triggers |= SchemeEditWidget.DoubleClicked
1470
1471        left_click = settings.value("trigger-on-left-click",
1472                                    defaultValue=False)
1473        if left_click.toBool():
1474            triggers |= SchemeEditWidget.Clicked
1475
1476        space_press = settings.value("trigger-on-space-key",
1477                                     defaultValue=True)
1478        if space_press.toBool():
1479            triggers |= SchemeEditWidget.SpaceKey
1480
1481        any_press = settings.value("trigger-on-any-key",
1482                                   defaultValue=False)
1483        if any_press.toBool():
1484            triggers |= SchemeEditWidget.AnyKey
1485
1486        self.scheme_widget.setQuickMenuTriggers(triggers)
1487
1488        settings.endGroup()
1489        settings.beginGroup("schemeedit")
1490        show_channel_names = settings.value("show-channel-names",
1491                                            defaultValue=True)
1492        self.scheme_widget.setChannelNamesVisible(show_channel_names.toBool())
1493
1494
1495def updated_flags(flags, mask, state):
1496    if state:
1497        flags |= mask
1498    else:
1499        flags &= ~mask
1500    return flags
1501
1502
1503def identity(item):
1504    return item
1505
1506
1507def index(sequence, *what, **kwargs):
1508    """index(sequence, what, [key=None, [predicate=None]])
1509    Return index of `what` in `sequence`.
1510    """
1511    what = what[0]
1512    key = kwargs.get("key", identity)
1513    predicate = kwargs.get("predicate", operator.eq)
1514    for i, item in enumerate(sequence):
1515        item_key = key(item)
1516        if predicate(what, item_key):
1517            return i
1518    raise ValueError("%r not in sequence" % what)
Note: See TracBrowser for help on using the repository browser.