source: orange/Orange/OrangeCanvas/application/canvasmain.py @ 11270:487bf7bfdcf6

Revision 11270:487bf7bfdcf6, 52.4 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Fixed default output dock size when first shown/undocked.

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