source: orange/Orange/OrangeCanvas/application/canvasmain.py @ 11264:a5c2117750d3

Revision 11264:a5c2117750d3, 52.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Providing help for widgets.

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