source: orange/Orange/OrangeCanvas/orngCanvas.pyw @ 11021:258cd74e0722

Revision 11021:258cd74e0722, 58.8 KB checked in by Matija Polajnar <matija.polajnar@…>, 17 months ago (diff)

Show progress during installation of add-ons.

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2# Description:
3#    main file, that creates the MDI environment
4
5# This module has to be imported first because it takes care of the system PATH variable
6# Namely, it throws out the MikTeX directories which contain an incompatible Qt .dll's
7import Orange
8import orngEnviron, Orange.utils.addons
9
10from PyQt4.QtCore import *
11from PyQt4.QtGui import *
12   
13import sys, os, cPickle, orngRegistry, OWGUI
14import pkg_resources
15import orngTabs, orngDoc, orngDlgs, orngOutput, orngHelp, OWReport
16import user, orngMisc
17
18import gc
19
20RedR = False
21product = "Red R" if RedR else "Orange"
22
23class OrangeCanvasDlg(QMainWindow):
24    def __init__(self, app, parent=None, flags=0):
25        QMainWindow.__init__(self, parent)
26        self.debugMode = 1        # print extra output for debuging
27        self.setWindowTitle("%s Canvas" % product)
28        self.windows = []    # list of id for windows in Window menu
29        self.recentDocs = []
30        self.iconNameToIcon = {}
31        self.toolbarIconSizeList = [16, 32, 40, 48, 60]
32        self.schemeIconSizeList = [32, 40, 48]
33        self.widgetsToolBar = None
34        self.originalPalette = QApplication.palette()
35
36        self.__dict__.update(orngEnviron.directoryNames)
37           
38        self.defaultPic = os.path.join(self.picsDir, "Unknown.png")
39        self.defaultBackground = os.path.join(self.picsDir, "frame.png")
40        canvasPicsDir = os.path.join(self.canvasDir, "icons")
41        self.file_new = os.path.join(canvasPicsDir, "doc.png")
42        self.outputPix = os.path.join(canvasPicsDir, "output.png")
43        self.file_open = os.path.join(canvasPicsDir, "open.png")
44        self.file_save = os.path.join(canvasPicsDir, "save.png")
45        if RedR:
46            self.reload_pic = os.path.join(canvasPicsDir, "update1.png")
47        self.text_icon = os.path.join(canvasPicsDir, "text.png")
48        self.file_print = os.path.join(canvasPicsDir, "print.png")
49        self.file_exit = os.path.join(canvasPicsDir, "exit.png")
50        canvasIconName = os.path.join(canvasPicsDir, "CanvasIcon.png")
51        if os.path.exists(canvasIconName):
52            self.setWindowIcon(QIcon(canvasIconName))
53           
54        self.settings = {}
55        if RedR:
56            self.settings['svnSettings'] = {}
57            self.settings['versionNumber'] = 'Version1.0'
58        self.menuSaveSettingsID = -1
59        self.menuSaveSettings = 1
60
61        self.loadSettings()
62        if RedR:
63            import updateRedR
64            self.settings['svnSettings'], self.settings['versionNumber'] = updateRedR.start(self.settings['svnSettings'], self.settings['versionNumber'])
65           
66#        self.widgetSelectedColor = QColor(*self.settings["widgetSelectedColor"])
67#        self.widgetActiveColor = QColor(*self.settings["widgetActiveColor"])
68#        self.lineColor = QColor(*self.settings["lineColor"])
69
70        # output window
71        self.output = orngOutput.OutputWindow(self)
72#        self.output.catchException(1)
73        self.output.catchOutput(1)
74       
75        self.updateWidgetRegistry()
76
77        # create error and warning icons
78        informationIconName = os.path.join(canvasPicsDir, "information.png")
79        warningIconName = os.path.join(canvasPicsDir, "warning.png")
80        errorIconName = os.path.join(canvasPicsDir, "error.png")
81        if os.path.exists(errorIconName) and os.path.exists(warningIconName) and os.path.exists(informationIconName):
82            self.errorIcon = QPixmap(errorIconName)
83            self.warningIcon = QPixmap(warningIconName)
84            self.informationIcon = QPixmap(informationIconName)
85            self.widgetIcons = {"Info": self.informationIcon, "Warning": self.warningIcon, "Error": self.errorIcon}
86        else:
87            self.errorIcon = None
88            self.warningIcon = None
89            self.informationIcon = None
90            self.widgetIcons = None
91            print "Unable to load all necessary icons. Please reinstall Orange."
92       
93        #########
94        # Actions
95        #########
96        self.showMainToolbarAction = QAction("Main Toolbar", self)
97        self.showMainToolbarAction.setCheckable(True)
98        self.showMainToolbarAction.setChecked(self.settings.get("showToolbar", True))
99        self.connect(self.showMainToolbarAction,
100                     SIGNAL("triggered(bool)"),
101                     self.menuItemShowToolbar)
102       
103        self.showWidgetToolbarAction = QAction("Widget Toolbar", self)
104        self.showWidgetToolbarAction.setCheckable(True)
105        self.showWidgetToolbarAction.setChecked(self.settings.get("showWidgetToolbar", True))
106        self.connect(self.showWidgetToolbarAction,
107                     SIGNAL("triggered(bool)"),
108                     self.menuItemShowWidgetToolbar)
109       
110        # TODO: Move other actions currently defined elsewhere
111       
112        self.setStatusBar(MyStatusBar(self))
113        self.statusBar().setVisible(self.settings.get("showStatusBar", True))
114        self.sizeGrip = SizeGrip(self)
115        self.sizeGrip.setVisible(not self.settings.get("showStatusBar", True))
116       
117        self.updateStyle()
118       
119        self.eventNotifier = EventNotifier(parent=self)
120        # create toolbar
121        self.toolbar = self.addToolBar("Toolbar")
122        self.toolbar.setOrientation(Qt.Horizontal)
123#        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
124        if not self.settings.get("showToolbar", True):
125            self.toolbar.hide()
126       
127        # create a schema
128        self.schema = orngDoc.SchemaDoc(self)
129        # A corner widget to reserve space for the size grip when
130        # the status bar is hidden
131        self.schema.canvasView.setCornerWidget(QWidget())
132       
133        self.setCentralWidget(self.schema)
134        self.schema.setFocus()
135
136        self.toolbar.addAction(QIcon(self.file_open), "Open schema", self.menuItemOpen)
137#        self.toolbar.addAction(QIcon(self.style().standardIcon(QStyle.SP_FileIcon)), "Open schema", self.menuItemOpen)
138        self.toolSave = self.toolbar.addAction(QIcon(self.file_save), "Save schema", self.menuItemSave)
139#        self.toolSave = self.toolbar.addAction(QIcon(self.style().standardIcon(QStyle.SP_DialogSaveButton)), "Save schema", self.menuItemSave)
140        if RedR:
141            self.toolReloadWidgets = self.toolbar.addAction(QIcon(self.reload_pic), "Reload Widgets", self.reloadWidgets)
142        self.toolbar.addSeparator()
143        self.toolbar.addAction(QIcon(self.file_print), "Print", self.menuItemPrinter)
144        self.toolbar.addSeparator()
145       
146        # Create the controls for widget list type in the main toolbar
147        # Commented out because this duplicates the entries in the View menu.
148#        def updateWidgetListType():
149#            self.createWidgetsToolbar()
150#            ind = min(len(self.widgetListTypeActions) - 1, self.settings["widgetListType"])
151#            self.widgetListTypeActions[ind].setChecked(True)
152#           
153#        def updateToolbarIconSize():
154#            self.createWidgetsToolbar()
155#            ind = min(len(self.iconSizeActions) - 1, self.settings["toolbarIconSize"])
156#            self.iconSizeActions[ind].setChecked(True)
157#           
158#        w = QWidget()
159#        w.setLayout(QHBoxLayout())
160#       
161#        items = ["Tool box", "Tree view", "Tree view (no icons)", "Tabs without labels", "Tabs with labels"]
162#        ind = max(len(items) - 1, self.settings["widgetListType"])
163#        self.widgetListTypeCB = OWGUI.comboBox(w, self.settings, "widgetListType",
164#                                    label="Style:", orientation="horizontal",
165#                                    items=items,
166#                                    callback=updateWidgetListType,
167#                                    debuggingEnabled=0)
168#       
169#        self.widgetListTypeCB.setFocusPolicy(Qt.TabFocus)
170#        self.toolbar.addWidget(w)
171#       
172#        self.toolbar.addSeparator()
173#
174#        w = QWidget()
175#        w.setLayout(QHBoxLayout())
176#        items = ["%d x %d" % (v, v) for v in self.toolbarIconSizeList]
177#        self.settings["toolbarIconSize"] = min(len(items) - 1, self.settings["toolbarIconSize"])
178#        cb = OWGUI.comboBoxWithCaption(w, self.settings, "toolbarIconSize", "Icon size:",
179#                                       items=items,
180#                                       tooltip="Set the size of the widget icons in the toolbar, tool box, and tree view area",
181#                                       callback=updateToolbarIconSize,
182#                                       debuggingEnabled=0)
183#        cb.setFocusPolicy(Qt.TabFocus)
184#       
185#        self.toolbar.addWidget(w)
186       
187        self.freezeAction = self.toolbar.addAction("Freeze signals")
188        self.freezeAction.setCheckable(True)
189        self.freezeAction.setIcon(QIcon(self.style().standardIcon(QStyle.SP_MediaPause))) #self.schema_pause))
190       
191        def toogleSchemaFreeze(freeze):
192            self.freezeAction.setIcon(QIcon(self.style().standardIcon(QStyle.SP_MediaPlay if freeze else QStyle.SP_MediaPause))) #self.schema_pause))
193            self.schema.setFreeze(freeze)
194           
195        self.connect(self.freezeAction, SIGNAL("toggled(bool)"), toogleSchemaFreeze)
196       
197        # Restore geometry before calling createWidgetsToolbar.
198        # On Mac OSX with unified title bar the canvas can move up on restarts
199        state = self.settings.get("CanvasMainWindowGeometry", None)
200        if state is not None:
201            state = self.restoreGeometry(QByteArray(state))
202            width, height = self.width(), self.height()
203       
204        if not state:
205            width, height = self.settings.get("canvasWidth", 700), self.settings.get("canvasHeight", 600)
206
207        # center window in the desktop
208        # on multiheaded desktops if it does not fit
209       
210        desktop = qApp.desktop()
211        space = desktop.availableGeometry(self)
212        geometry, frame = self.geometry(), self.frameGeometry()
213       
214        #Fit the frame size to fit in space
215        width = min(space.width() - (frame.width() - geometry.width()), geometry.width())
216        height = min(space.height() - (frame.height() - geometry.height()), geometry.height())
217       
218        self.resize(width, height)
219       
220        self.addToolBarBreak()
221        orngTabs.constructCategoriesPopup(self)
222        self.createWidgetsToolbar()
223        self.readShortcuts()
224       
225        def addOnRefreshCallback():
226            self.updateWidgetRegistry()
227            orngTabs.constructCategoriesPopup(self)
228            self.createWidgetsToolbar()
229        Orange.utils.addons.addon_refresh_callback.append(addOnRefreshCallback)
230
231        # create menu
232        self.initMenu()
233        self.readRecentFiles()
234       
235        #move to center if frame not fully contained in space
236        if not space.contains(self.frameGeometry()):
237            x = max(0, space.width() / 2 - width / 2)
238            y = max(0, space.height() / 2 - height / 2)
239           
240            self.move(x, y)
241
242        self.helpWindow = orngHelp.HelpWindow(self)
243        self.reportWindow = OWReport.ReportWindow()
244        self.reportWindow.widgets = self.schema.widgets
245        self.reportWindow.saveDir = self.settings["reportsDir"]
246       
247
248        # did Orange crash the last time we used it? If yes, you will find a tempSchema.tmp file
249        if not RedR:
250            if os.path.exists(os.path.join(self.canvasSettingsDir, "tempSchema.tmp")):
251                mb = QMessageBox('%s Canvas' % product, "Your previous %s Canvas session was not closed successfully.\nYou can choose to reload your unsaved work or start a new session.\n\nIf you choose 'Reload', the links will be disabled to prevent reoccurence of the crash.\nYou can enable them by clicking Options/Enable all links." % product, QMessageBox.Information, QMessageBox.Ok | QMessageBox.Default, QMessageBox.Cancel | QMessageBox.Escape, QMessageBox.NoButton)
252                mb.setButtonText(QMessageBox.Ok, "Reload")
253                mb.setButtonText(QMessageBox.Cancel, "New schema")
254                if mb.exec_() == QMessageBox.Ok:
255                    self.schema.loadDocument(os.path.join(self.canvasSettingsDir, "tempSchema.tmp"), freeze=1)
256       
257        if self.schema.widgets == [] and len(sys.argv) > 1 and os.path.exists(sys.argv[-1]) and os.path.splitext(sys.argv[-1])[1].lower() == ".ows":
258            self.schema.loadDocument(sys.argv[-1])
259       
260        # Raise the size grip so it is above all other widgets
261        self.sizeGrip.raise_()
262       
263        # show message box if no numpy
264        qApp.processEvents()
265        try:
266            import numpy
267        except ImportError:
268            if QMessageBox.warning(self, '%s Canvas' % product, 'Several widgets now use numpy module, \nthat is not yet installed on this computer. \nDo you wish to download it?', QMessageBox.Ok | QMessageBox.Default, QMessageBox.Cancel | QMessageBox.Escape) == QMessageBox.Ok:
269                import webbrowser
270                webbrowser.open("http://sourceforge.net/projects/numpy/")
271               
272        # On Mac if the user clicks the Toolbar button in the title bar
273        if sys.platform == "darwin":
274            self.eventNotifier.attach(self, QEvent.ToolBarChange, self.toogleToolbarState)
275           
276        self.output.catchException(1)
277
278    def updateWidgetRegistry(self):
279        """ Update the widget registry and add new category tabs to the
280        settings dict. 
281        """
282        # The default Widget tabs order
283        if not self.settings.has_key("WidgetTabs") or self.settings["WidgetTabs"] == []:
284            f = open(os.path.join(self.canvasDir, "WidgetTabs.txt"), "r")
285            defaultTabs = [c for c in [line.split("#")[0].strip() for line in f.readlines()] if c!=""]
286            for i in xrange(len(defaultTabs)-1,0,-1):
287                if defaultTabs[i] in defaultTabs[0:i]:
288                    del defaultTabs[i]
289            self.settings["WidgetTabs"] = [(name, Qt.Checked) for name in defaultTabs] + [("Visualize Qt", Qt.Unchecked), ("Prototypes", Qt.Unchecked)]
290           
291        widgetTabList = self.settings["WidgetTabs"]
292        self.widgetRegistry = orngRegistry.readCategories()
293       
294        extraTabs = [(name, 1) for name in self.widgetRegistry.keys() if name not in [tab for (tab, s) in widgetTabList]]
295        extraTabs = sorted(extraTabs)
296       
297        # Keep Prototypes as last in list
298        if widgetTabList[-1][0] == "Prototypes" and widgetTabList[-2][0] == "Visualize Qt":
299            widgetTabList = widgetTabList[: -2] + extraTabs + widgetTabList[-2 :]
300        elif widgetTabList[-1][0] == "Prototypes":
301            widgetTabList = widgetTabList[: -1] + extraTabs + widgetTabList[-1 :]
302        else:
303            widgetTabList = widgetTabList + extraTabs
304        self.settings["WidgetTabs"] = widgetTabList
305           
306    def createWidgetsToolbar(self):
307        barstate, treestate = None, None
308        if self.widgetsToolBar:
309            self.settings["showWidgetToolbar"] = self.widgetsToolBar.isVisible()
310            if isinstance(self.widgetsToolBar, QToolBar):
311                self.removeToolBar(self.widgetsToolBar)
312                barstate = (self.tabs.currentIndex(), )
313            elif isinstance(self.widgetsToolBar, orngTabs.WidgetToolBox):
314                self.settings["toolboxWidth"] = self.widgetsToolBar.toolbox.width()
315                self.removeDockWidget(self.widgetsToolBar)
316                barstate = (self.tabs.toolbox.currentIndex(), )
317            elif isinstance(self.widgetsToolBar, orngTabs.WidgetTree):
318                self.settings["toolboxWidth"] = self.widgetsToolBar.treeWidget.width()
319                self.removeDockWidget(self.widgetsToolBar)
320                treestate = ( [self.tabs.treeWidget.topLevelItem(i).isExpanded()
321                               for i in range(self.tabs.treeWidget.topLevelItemCount())], )
322           
323        if self.settings["widgetListType"] == 0:
324            self.tabs = self.widgetsToolBar = orngTabs.WidgetToolBox(self, self.widgetRegistry)
325            self.addDockWidget(Qt.LeftDockWidgetArea, self.widgetsToolBar)
326        elif self.settings["widgetListType"] in [1, 2]:
327            self.tabs = self.widgetsToolBar = orngTabs.WidgetTree(self, self.widgetRegistry)
328            self.addDockWidget(Qt.LeftDockWidgetArea, self.widgetsToolBar)
329        else:
330            if sys.platform == "darwin":
331                self.setUnifiedTitleAndToolBarOnMac(False)
332            self.widgetsToolBar = self.addToolBar("Widgets")
333            self.insertToolBarBreak(self.widgetsToolBar)
334            self.tabs = orngTabs.WidgetTabs(self, self.widgetRegistry, self.widgetsToolBar)
335            self.widgetsToolBar.addWidget(self.tabs)
336           
337        if sys.platform == "darwin":
338            self.setUnifiedTitleAndToolBarOnMac(self.settings["widgetListType"] in [0, 1, 2] and self.settings["style"].lower() == "macintosh (aqua)")
339
340        # find widgets and create tab with buttons
341        self.tabs.createWidgetTabs(self.settings["WidgetTabs"], self.widgetRegistry, self.widgetDir, self.picsDir, self.defaultPic)
342        if not self.settings.get("showWidgetToolbar", True):
343            self.widgetsToolBar.hide()
344        if barstate:
345            if self.settings["widgetListType"] == 0:
346                self.tabs.toolbox.setCurrentIndex(barstate[0])
347            elif self.settings["widgetListType"] in [1, 2]:
348                widget = self.tabs.treeWidget
349                widget.scrollToItem(widget.topLevelItem(barstate[0]),
350                                    QAbstractItemView.PositionAtTop)
351            else:
352                self.tabs.setCurrentIndex(barstate[0])
353        if treestate and self.settings["widgetListType"] in [1, 2]:
354            for i, e in enumerate(treestate[0]):
355                self.tabs.treeWidget.topLevelItem(i).setExpanded(e)
356
357
358    def readShortcuts(self):
359        self.widgetShortcuts = {}
360        shfn = os.path.join(self.canvasSettingsDir, "shortcuts.txt")
361        if os.path.exists(shfn):
362            for t in file(shfn).readlines():
363                key, info = [x.strip() for x in t.split(":")]
364                if len(info) == 0: continue
365                if info[0] == "(" and info[-1] == ")":
366                    cat, widgetName = eval(info)            # new style of shortcuts are of form F: ("Data", "File")
367                else:
368                    cat, widgetName = info.split(" - ")   # old style of shortcuts are of form F: Data - File
369                if self.widgetRegistry.has_key(cat) and self.widgetRegistry[cat].has_key(widgetName):
370                    self.widgetShortcuts[key] = self.widgetRegistry[cat][widgetName]
371
372    def initMenu(self):
373        self.menuRecent = QMenu("Recent Schemas", self)
374
375        self.menuFile = QMenu("&File", self)
376        self.menuFile.addAction("New Scheme", self.menuItemNewScheme, QKeySequence.New)
377        self.menuFile.addAction(QIcon(self.file_open), "&Open...", self.menuItemOpen, QKeySequence.Open)
378        self.menuFile.addAction(QIcon(self.file_open), "&Open and Freeze...", self.menuItemOpenFreeze)
379        if RedR:
380            self.menuFile.addAction("Import Schema", self.importSchema)
381        if os.path.exists(os.path.join(self.canvasSettingsDir, "lastSchema.tmp")):
382            self.menuFile.addAction("Reload Last Schema", self.menuItemOpenLastSchema, Qt.CTRL + Qt.Key_R)
383        #self.menuFile.addAction( "&Clear", self.menuItemClear)
384        self.menuFile.addSeparator()
385        self.menuReportID = self.menuFile.addAction("&Report", self.menuItemReport, Qt.CTRL + Qt.ALT + Qt.Key_R)
386        self.menuFile.addSeparator()
387        self.menuSaveID = self.menuFile.addAction(QIcon(self.file_save), "&Save", self.menuItemSave, QKeySequence.Save)
388        self.menuSaveAsID = self.menuFile.addAction("Save &as...", self.menuItemSaveAs)
389        if not RedR:
390            self.menuFile.addAction("&Save as Application (Tabs)...", self.menuItemSaveAsAppTabs)
391            self.menuFile.addAction("&Save as Application (Buttons)...", self.menuItemSaveAsAppButtons)
392        self.menuFile.addSeparator()
393        self.menuFile.addAction(QIcon(self.file_print), "&Print Schema / Save image", self.menuItemPrinter, QKeySequence.Print)
394        self.menuFile.addSeparator()
395        self.menuFile.addMenu(self.menuRecent)
396        self.menuFile.addSeparator()
397        self.menuFile.addAction("E&xit", self.close, Qt.CTRL + Qt.Key_Q)
398       
399        self.menuView = QMenu("&View", self)
400        # Show Toolbars menu
401        toolbars = self.createPopupMenu()
402        self.menuView.addMenu(toolbars)
403        self.menuView.addSeparator()
404       
405       
406        # Widget list type menu
407        actions = ["Toolbox", "Tree View", "Tree View (no icons)",
408                   "Tabs Without Labels", "Tabs With Labels"]
409        style = QMenu("Widget Toolbar Style", self)
410        styleGroup = QActionGroup(style)
411        styleGroup.setExclusive(True)
412       
413        def setListType(id):
414            self.settings["widgetListType"] = id
415            self.createWidgetsToolbar()
416           
417        for id, action in enumerate(actions):
418            action = style.addAction(action, lambda id=id: setListType(id))
419            action.setCheckable(True)
420            styleGroup.addAction(action)
421            style.addAction(action)
422           
423        styleActions = list(styleGroup.actions())
424        ind = min(len(styleActions) -1 , self.settings.get("widgetListType", -1))
425        styleActions[ind].setChecked(True)
426        self.widgetListTypeActions = styleActions
427       
428        self.menuView.addMenu(style)
429       
430        # Widget toobox icon size menu
431        iconSize = QMenu("Widget Icon Size", self)
432        sizes = ["%i x %i" % (s, s) for s in self.toolbarIconSizeList]
433        sizeGroup = QActionGroup(iconSize)
434       
435        def setIconSize(id):
436            self.settings["toolbarIconSize"] = id
437            self.createWidgetsToolbar()
438           
439        for id, size in enumerate(sizes):
440            action = iconSize.addAction(size, lambda id=id: setIconSize(id))
441            action.setCheckable(True)
442            sizeGroup.addAction(action)
443            iconSize.addAction(action)
444           
445        sizeActions = list(sizeGroup.actions())
446        ind = min(len(sizeActions) - 1, self.settings.get("toolbarIconSize", -1))
447        sizeActions[ind].setChecked(True)
448        self.iconSizeActions = sizeActions
449       
450        self.menuView.addMenu(iconSize)
451       
452        self.menuView.addSeparator()
453        self.showStatusBarAction = self.menuView.addAction("Show Status Bar",
454                                                self.menuItemShowStatusBar)
455        self.showStatusBarAction.setCheckable(True)
456        self.showStatusBarAction.setChecked(self.settings.get("showStatusBar", True))
457
458        self.menuOptions = QMenu("&Options", self)
459        self.menuOptions.addAction("Enable All Links", self.menuItemEnableAll, Qt.CTRL + Qt.Key_E)
460        self.menuOptions.addAction("Disable All Links", self.menuItemDisableAll, Qt.CTRL + Qt.Key_D)
461        self.menuOptions.addSeparator()
462        self.menuOptions.addAction("Show Output Window", self.menuItemShowOutputWindow)
463        self.menuOptions.addAction("Clear Output Window", self.menuItemClearOutputWindow)
464        self.menuOptions.addAction("Save Output Text...", self.menuItemSaveOutputWindow)
465        if RedR:
466            self.menuOptions.addAction("Set to debug mode", self.setDebugMode)
467       
468        # uncomment this only for debugging
469#        self.menuOptions.addSeparator()
470#        self.menuOptions.addAction("Dump widget variables", self.dumpVariables)
471
472        self.menuOptions.addSeparator()
473#        self.menuOptions.addAction( "Channel preferences",  self.menuItemPreferences)
474        #self.menuOptions.addSeparator()
475        self.menuOptions.addAction("&Customize Shortcuts", self.menuItemEditWidgetShortcuts)
476        self.menuOptions.addAction("&Delete Widget Settings", self.menuItemDeleteWidgetSettings)
477        self.menuOptions.addSeparator()
478        self.menuOptions.addAction(sys.platform == "darwin" and "&Preferences..." or "Canvas &Options...", self.menuItemCanvasOptions)
479        self.menuOptions.addAction("&Add-ons...", self.menuItemAddOns)
480
481        localHelp = 0
482        self.menuHelp = QMenu("&Help", self)
483        self.menuHelp.addAction("Orange Online Reference", self.menuOpenOnlineOrangeReference)
484#        if os.path.exists(os.path.join(self.orangeDir, r"doc/catalog/index.html")):
485#            self.menuHelp.addAction("Orange Widget Catalog", self.menuOpenLocalWidgetCatalog)
486#        if os.path.exists(os.path.join(self.orangeDir, r"doc/canvas/default.htm")):
487#            self.menuHelp.addAction("Orange Canvas Help", self.menuOpenLocalCanvasHelp)
488        self.menuHelp.addAction("Orange Online Widget Catalog", self.menuOpenOnlineOrangeHelp)
489        #self.menuHelp.addAction("Orange Canvas Online Help", self.menuOpenOnlineCanvasHelp)           
490        self.menuHelp.addSeparator()
491        self.menuHelp.addAction("About Orange", self.menuItemAboutOrange)
492
493        # widget popup menu
494        self.widgetPopup = QMenu("Widget", self)
495        self.openActiveWidgetAction = self.widgetPopup.addAction("Open", self.schema.canvasView.openActiveWidget)
496        self.widgetPopup.addSeparator()
497        self.renameActiveWidgetAction = rename = self.widgetPopup.addAction("&Rename", self.schema.canvasView.renameActiveWidget, Qt.Key_F2)
498        self.removeActiveWidgetAction = delete = self.widgetPopup.addAction("Remove", self.schema.canvasView.removeActiveWidget, Qt.Key_Delete)
499        if sys.platform != "darwin":
500            delete.setShortcuts([Qt.Key_Delete, Qt.CTRL + Qt.Key_Backspace, Qt.CTRL + Qt.Key_Delete])
501        else:
502            delete.setShortcuts([Qt.CTRL + Qt.Key_Backspace, Qt.Key_Delete, Qt.CTRL + Qt.Key_Delete])
503        self.widgetPopup.addSeparator()
504        self.helpActiveWidgetAction = self.widgetPopup.addAction("Help", self.schema.canvasView.helpOnActiveWidget, Qt.Key_F1)
505        self.widgetPopup.setEnabled(0)
506       
507        if sys.platform == "darwin":
508            self.windowPopup = QMenu("Window", self)
509            self.windowPopup.addAction("Minimize", self.showMinimized, Qt.CTRL + Qt.Key_M)
510            self.windowPopup.addAction("Zoom", self.showMaximized, 0)
511
512        self.menuBar = QMenuBar(self)
513        self.menuBar.addMenu(self.menuFile)
514        self.menuBar.addMenu(self.menuView)
515        self.menuBar.addMenu(self.menuOptions)
516        self.menuBar.addMenu(self.widgetPopup)
517       
518        if hasattr(self, "windowPopup"):
519            self.menuBar.addMenu(self.windowPopup)
520           
521        self.menuBar.addMenu(self.menuHelp)
522       
523        self.setMenuBar(self.menuBar)
524       
525    def setDebugMode(self):   # RedR specific
526        if self.output.debugMode:
527            self.output.debugMode = 0
528        else:
529            self.output.debugMode = 1
530    def importSchema(self):   # RedR specific
531        name = QFileDialog.getOpenFileName(self, "Import File", self.settings["saveSchemaDir"], "Orange Widget Scripts (*.ows)")
532        if name.isEmpty():
533            return
534        name = unicode(name)
535        self.schema.clear()
536        self.schema.loadDocument(name, freeze = 0, importBlank = 1)
537        self.addToRecentMenu(name)
538   
539    def openSchema(self, filename):
540        if self.schema.isSchemaChanged() and self.schema.widgets:
541            ret = QMessageBox.warning(self, "Orange Canvas", "Changes to your present schema are not saved.\nSave them?",
542                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save)
543            if ret == QMessageBox.Save:
544                self.schema.saveDocument()
545            elif ret == QMessageBox.Cancel:
546                return
547        self.schema.clear()
548        dirname = os.path.dirname(filename)
549        os.chdir(dirname)
550        self.schema.loadDocument(filename)
551       
552    def menuItemOpen(self):
553        name = QFileDialog.getOpenFileName(self, "Open Orange Schema", self.settings["saveSchemaDir"], "Orange Widget Scripts (*.ows)")
554        if name.isEmpty():
555            return
556        name = unicode(name)
557        self.schema.clear()
558        dirname = os.path.dirname(name)
559        os.chdir(dirname)
560        self.schema.loadDocument(name, freeze=0)
561        self.addToRecentMenu(name)
562
563    def menuItemOpenFreeze(self):
564        name = QFileDialog.getOpenFileName(self, "Open Orange Schema", self.settings["saveSchemaDir"], "Orange Widget Scripts (*.ows)")
565        if name.isEmpty():
566            return
567        name = unicode(name)
568        self.schema.clear()
569        dirname = os.path.dirname(name)
570        os.chdir(dirname)
571        self.schema.loadDocument(name, freeze=1)
572        self.addToRecentMenu(name)
573
574    def menuItemOpenLastSchema(self):
575        fullName = os.path.join(self.canvasSettingsDir, "lastSchema.tmp")
576        if os.path.exists(fullName):
577            self.schema.loadDocument(fullName)
578
579    def menuItemReport(self):
580        self.schema.reportAll()
581       
582    def menuItemSave(self):
583        self.schema.saveDocument()
584       
585    def menuItemSaveAs(self):
586        self.schema.saveDocumentAs()
587
588    def menuItemSaveAsAppButtons(self):
589        self.schema.saveDocumentAsApp(asTabs=0)
590
591    def menuItemSaveAsAppTabs(self):
592        self.schema.saveDocumentAsApp(asTabs=1)
593
594    def menuItemPrinter(self):
595        try:
596            import OWDlgs
597            sizeDlg = OWDlgs.OWChooseImageSizeDlg(self.schema.canvas, defaultName=self.schema.schemaName or "schema", parent=self)
598            sizeDlg.exec_()
599        except:
600            print "Missing file 'OWDlgs.py'. This file should be in OrangeWidgets folder. Unable to print/save image."
601       
602
603    def readRecentFiles(self):
604        self.menuRecent.clear()
605        recentDocs = self.settings.get("RecentFiles", [])
606
607        # remove missing recent files
608        for i in range(len(recentDocs) - 1, -1, -1):
609            if not os.path.exists(recentDocs[i]):
610                recentDocs.remove(recentDocs[i])
611
612        recentDocs = recentDocs[:9]
613        self.settings["RecentFiles"] = recentDocs
614       
615        if len(recentDocs) == 0 :
616            self.menuRecent.addAction("None").setEnabled(False)
617        else :           
618            for i in range(len(recentDocs)):
619                shortName = "&" + str(i + 1) + " " + os.path.basename(recentDocs[i])
620                self.menuRecent.addAction(shortName, lambda ind=i: self.openRecentFile(ind),)
621
622    def openRecentFile(self, index):
623        if index < len(self.settings["RecentFiles"]):
624            name = self.settings["RecentFiles"][index]
625            if self.schema.saveBeforeClose():
626                self.schema.clear()
627                dirname = os.path.dirname(name)
628                os.chdir(dirname)
629                self.schema.loadDocument(name)
630                self.addToRecentMenu(name)
631
632    def addToRecentMenu(self, name):
633        recentDocs = []
634        if self.settings.has_key("RecentFiles"):
635            recentDocs = self.settings["RecentFiles"]
636
637        # convert to a valid file name
638        name = os.path.realpath(name)
639
640        if name in recentDocs:
641            recentDocs.remove(name)
642        recentDocs.insert(0, name)
643
644        if len(recentDocs) > 5:
645            recentDocs.remove(recentDocs[5])
646        self.settings["RecentFiles"] = recentDocs
647        self.readRecentFiles()
648
649    def menuItemSelectAll(self):
650        return
651
652    def updateSnapToGrid(self):
653        if self.settings["snapToGrid"]:
654            for widget in self.schema.widgets:
655                widget.setCoords(widget.x(), widget.y())
656            self.schema.canvas.update()
657
658    def menuItemEnableAll(self):
659        self.schema.enableAllLines()
660
661    def menuItemDisableAll(self):
662        self.schema.disableAllLines()
663
664    def menuItemSaveSettings(self):
665        self.menuSaveSettings = not self.menuSaveSettings
666        self.menuOptions.setItemChecked(self.menuSaveSettingsID, self.menuSaveSettings)
667
668    def menuItemNewScheme(self):
669        if self.schema.saveBeforeClose():
670            self.schema.clear()
671            self.schema.removeTempDoc()
672
673    def dumpVariables(self):
674        self.schema.dumpWidgetVariables()
675
676    def menuItemShowOutputWindow(self):
677        self.output.show()
678        self.output.raise_()
679#        self.output.activateWindow()
680
681    def menuItemClearOutputWindow(self):
682        self.output.textOutput.clear()
683        self.statusBar().showMessage("")
684
685    def menuItemSaveOutputWindow(self):
686        qname = QFileDialog.getSaveFileName(self, "Save Output To File", self.canvasSettingsDir + "/Output.html", "HTML Document (*.html)")
687        if qname.isEmpty(): return
688       
689        text = str(self.output.textOutput.toHtml())
690        #text = text.replace("</nobr>", "</nobr><br>")
691
692        file = open(unicode(name), "wt")
693        file.write(text)
694        file.close()
695
696    def menuItemShowToolbar(self, show=True):
697        self.toolbar.setVisible(show)
698        self.settings["showToolbar"] = show
699        self.showMainToolbarAction.setChecked(show)
700
701    def menuItemShowWidgetToolbar(self, show=True):
702        self.widgetsToolBar.setVisible(show)
703        self.settings["showWidgetToolbar"] = show
704        self.showWidgetToolbarAction.setChecked(show)
705       
706    def createPopupMenu(self):
707        """ Create a menu with show toolbar entries.
708        """
709        toolbars = QMenu("Show Toolbars", self)
710        toolbars.addAction(self.showMainToolbarAction)
711        toolbars.addAction(self.showWidgetToolbarAction)
712        return toolbars
713       
714    def toogleToolbarState(self):
715        """ Toogle the toolbar state (Mac OSX specific). This gets called when
716        the toolbar button in the unified title bar was clicked.
717        """
718        state = not self.settings["showToolbar"]
719        self.settings["showToolbar"] = state
720        self.showMainToolbarAction.setChecked(state)
721       
722
723    def menuItemEditWidgetShortcuts(self):
724        dlg = orngDlgs.WidgetShortcutDlg(self, self)
725        if dlg.exec_() == QDialog.Accepted:
726            self.widgetShortcuts = dict([(y, x) for x, y in dlg.invDict.items()])
727            shf = file(os.path.join(self.canvasSettingsDir, "shortcuts.txt"), "wt")
728            for k, widgetInfo in self.widgetShortcuts.items():
729                shf.write("%s: %s\n" % (k, (widgetInfo.category, widgetInfo.name)))
730
731    def menuItemDeleteWidgetSettings(self):
732        if QMessageBox.warning(self, 'Orange Canvas', 'Delete all settings?\nNote that for a complete reset there should be no open schema with any widgets.', QMessageBox.Ok | QMessageBox.Default, QMessageBox.Cancel | QMessageBox.Escape) == QMessageBox.Ok:
733            if os.path.exists(self.widgetSettingsDir):
734                for f in os.listdir(self.widgetSettingsDir):
735                    if os.path.splitext(f)[1].lower() == ".ini":
736                        os.remove(os.path.join(self.widgetSettingsDir, f))
737
738    def menuOpenLocalOrangeHelp(self):
739        import webbrowser
740        webbrowser.open("file:///" + os.path.join(self.orangeDir, "doc/reference/default.htm"))
741
742    def menuOpenLocalWidgetCatalog(self):
743        import webbrowser
744        webbrowser.open("file:///" + os.path.join(self.orangeDir, "doc/catalog/index.html"))
745
746    def menuOpenLocalCanvasHelp(self):
747        import webbrowser
748        webbrowser.open(os.path.join(self.orangeDir, "doc/canvas/default.htm"))
749
750    def menuOpenOnlineOrangeReference(self):
751        import webbrowser
752        webbrowser.open("http://orange.biolab.si/doc/reference/")
753       
754    def menuOpenOnlineOrangeHelp(self):
755        import webbrowser
756        webbrowser.open("http://orange.biolab.si/doc/widgets/")
757
758    def menuOpenOnlineCanvasHelp(self):
759        import webbrowser
760        #webbrowser.open("http://orange.biolab.si/orangeCanvas") # to be added on the web
761        webbrowser.open("http://orange.biolab.si")
762
763    def menuCheckForUpdates(self):
764        import updateOrange
765        self.updateDlg = updateOrange.updateOrangeDlg(None)#, Qt.WA_DeleteOnClose)
766
767    def menuItemAboutOrange(self):
768        dlg = orngDlgs.AboutDlg(self)
769        dlg.exec_()
770
771
772## to see the signals you have to call: self.output.catchException(0); self.output.catchOutput(0)
773## and run orngCanvas.pyw from command line using "python.exe orngCanvas.pyw"
774#    def event(self, e):
775#        eventDict = dict([(0, "None"), (130, "AccessibilityDescription"), (119, "AccessibilityHelp"), (86, "AccessibilityPrepare"), (114, "ActionAdded"), (113, "ActionChanged"), (115, "ActionRemoved"), (99, "ActivationChange"), (121, "ApplicationActivated"), (122, "ApplicationDeactivated"), (36, "ApplicationFontChange"), (37, "ApplicationLayoutDirectionChange"), (38, "ApplicationPaletteChange"), (35, "ApplicationWindowIconChange"), (68, "ChildAdded"), (69, "ChildPolished"), (71, "ChildRemoved"), (40, "Clipboard"), (19, "Close"), (82, "ContextMenu"), (52, "DeferredDelete"), (60, "DragEnter"), (62, "DragLeave"), (61, "DragMove"), (63, "Drop"), (98, "EnabledChange"), (10, "Enter"), (150, "EnterEditFocus"), (124, "EnterWhatsThisMode"), (116, "FileOpen"), (8, "FocusIn"), (9, "FocusOut"), (97, "FontChange"), (159, "GraphicsSceneContextMenu"), (164, "GraphicsSceneDragEnter"), (166, "GraphicsSceneDragLeave"), (165, "GraphicsSceneDragMove"), (167, "GraphicsSceneDrop"), (163, "GraphicsSceneHelp"), (160, "GraphicsSceneHoverEnter"), (162, "GraphicsSceneHoverLeave"), (161, "GraphicsSceneHoverMove"), (158, "GraphicsSceneMouseDoubleClick"), (155, "GraphicsSceneMouseMove"), (156, "GraphicsSceneMousePress"), (157, "GraphicsSceneMouseRelease"), (168, "GraphicsSceneWheel"), (18, "Hide"), (27, "HideToParent"), (127, "HoverEnter"), (128, "HoverLeave"), (129, "HoverMove"), (96, "IconDrag"), (101, "IconTextChange"), (83, "InputMethod"), (6, "KeyPress"), (7, "KeyRelease"), (89, "LanguageChange"), (90, "LayoutDirectionChange"), (76, "LayoutRequest"), (11, "Leave"), (151, "LeaveEditFocus"), (125, "LeaveWhatsThisMode"), (88, "LocaleChange"), (153, "MenubarUpdated"), (43, "MetaCall"), (102, "ModifiedChange"), (4, "MouseButtonDblClick"), (2, "MouseButtonPress"), (3, "MouseButtonRelease"), (5, "MouseMove"), (109, "MouseTrackingChange"), (13, "Move"), (12, "Paint"), (39, "PaletteChange"), (131, "ParentAboutToChange"), (21, "ParentChange"), (75, "Polish"), (74, "PolishRequest"), (123, "QueryWhatsThis"), (14, "Resize"), (117, "Shortcut"), (51, "ShortcutOverride"), (17, "Show"), (26, "ShowToParent"), (50, "SockAct"), (112, "StatusTip"), (100, "StyleChange"), (87, "TabletMove"), (92, "TabletPress"), (93, "TabletRelease"), (171, "TabletEnterProximity"), (172, "TabletLeaveProximity"), (1, "Timer"), (120, "ToolBarChange"), (110, "ToolTip"), (78, "UpdateLater"), (77, "UpdateRequest"), (111, "WhatsThis"), (118, "WhatsThisClicked"), (31, "Wheel"), (132, "WinEventAct"), (24, "WindowActivate"), (103, "WindowBlocked"), (25, "WindowDeactivate"), (34, "WindowIconChange"), (105, "WindowStateChange"), (33, "WindowTitleChange"), (104, "WindowUnblocked"), (126, "ZOrderChange"), (169, "KeyboardLayoutChange"), (170, "DynamicPropertyChange")])
776#        if eventDict.has_key(e.type()):
777#            print str(self.windowTitle()), eventDict[e.type()]
778#        return QMainWindow.event(self, e)
779
780
781    def menuItemCanvasOptions(self):
782        dlg = orngDlgs.CanvasOptionsDlg(self, self)
783
784        if dlg.exec_() == QDialog.Accepted:
785            if self.settings["snapToGrid"] != dlg.settings["snapToGrid"]:
786                self.updateSnapToGrid()
787
788            if self.settings["widgetListType"] != dlg.settings["widgetListType"]:
789                self.settings["widgetListType"] = dlg.settings["widgetListType"]
790                self.createWidgetsToolbar()
791                self.widgetListTypeCB.setCurrentIndex(self.settings["widgetListType"])
792            self.settings.update(dlg.settings)
793            self.updateStyle()
794           
795#            self.widgetSelectedColor = dlg.selectedWidgetIcon.color
796#            self.widgetActiveColor   = dlg.activeWidgetIcon.color
797#            self.lineColor           = dlg.lineIcon.color
798           
799            # update settings in widgets in current documents
800            for widget in self.schema.widgets:
801                widget.instance._useContexts = self.settings["useContexts"]
802                widget.instance._owInfo = self.settings["owInfo"]
803                widget.instance._owWarning = self.settings["owWarning"]
804                widget.instance._owError = self.settings["owError"]
805                widget.instance._owShowStatus = self.settings["owShow"]
806                widget.instance.updateStatusBarState()
807                widget.resetWidgetSize()
808                widget.updateWidgetState()
809               
810            # update tooltips for lines in all documents
811            for line in self.schema.lines:
812                line.showSignalNames = self.settings["showSignalNames"]
813                line.updateTooltip()
814           
815            self.schema.canvasView.repaint()
816       
817#            import orngEnviron, orngRegistry
818#            if dlg.toAdd != []:
819#                for (name, dir) in dlg.toAdd:
820#                    orngEnviron.registerAddOn(name, dir)
821           
822#            if dlg.toRemove != []:
823#                for (catName, cat) in dlg.toRemove:
824#                    addonsToRemove = set()
825#                    for widget in cat.values():
826#                        addonDir = widget.directory
827#                        while os.path.split(addonDir)[1] in ["prototypes", "widgets"]:
828#                            addonDir = os.path.split(addonDir)[0]
829#                        addonName = os.path.split(addonDir)[1]
830#                        addonsToRemove.add( (addonName, addonDir) )
831#                    for addonToRemove in addonsToRemove:
832#                        orngEnviron.registerAddOn(add=False, *addonToRemove)
833#           
834#            if dlg.toAdd != [] or dlg.toRemove != []:
835#                self.widgetRegistry = orngRegistry.readCategories()
836
837            # save tab order settings
838            newTabList = [(str(dlg.tabOrderList.item(i).text()), int(dlg.tabOrderList.item(i).checkState())) for i in range(dlg.tabOrderList.count())]
839            if newTabList != self.settings["WidgetTabs"]:
840                self.settings["WidgetTabs"] = newTabList
841                self.createWidgetsToolbar()
842                orngTabs.constructCategoriesPopup(self)
843
844    def menuItemAddOns(self):
845        import time
846        t = time.time()
847        lastRefresh = self.settings["lastAddonsRefresh"]
848        dlg = orngDlgs.AddOnManagerDialog(self, self)
849        if t - lastRefresh > 7*24*3600:
850            dlg.show()
851            if QMessageBox.question(self, "Refresh",
852                                    "List of add-ons in repository has %s. Do you want to %s the list now?" %
853                                    (("not yet been loaded" if lastRefresh==0 else "not been refreshed for more than a week"),
854                                     ("download" if lastRefresh==0 else "reload")),
855                                     QMessageBox.Yes | QMessageBox.Default,
856                                     QMessageBox.No | QMessageBox.Escape) == QMessageBox.Yes:
857               
858                try:
859                    dlg.reloadRepo()
860                    self.settings["lastAddonsRefresh"] = time.time()
861                except Exception, e:
862                    import traceback
863                    traceback.print_exc()
864                    QMessageBox.warning(self,'Download Failed', "Download of add-on list has failed.")
865
866        dlg.exec_()
867
868    def menuItemShowStatusBar(self):
869        state = self.showStatusBarAction.isChecked()
870        self.statusBar().setVisible(state)
871        self.sizeGrip.setVisible(not state)
872        self.sizeGrip.raise_()
873        self.settings["showStatusBar"] = state
874
875    def updateStyle(self):
876        QApplication.setStyle(QStyleFactory.create(self.settings["style"]))
877#        qApp.setStyleSheet(" QDialogButtonBox { button-layout: 0; }")       # we want buttons to go in the "windows" direction (Yes, No, Cancel)
878        if self.settings["useDefaultPalette"]:
879            QApplication.setPalette(qApp.style().standardPalette())
880        else:
881            QApplication.setPalette(self.originalPalette)
882
883
884    def setStatusBarEvent(self, text):
885        if text == "" or text == None:
886            self.statusBar().showMessage("")
887            return
888        elif text == "\n": return
889        text = str(text)
890        text = text.replace("<nobr>", ""); text = text.replace("</nobr>", "")
891        text = text.replace("<b>", ""); text = text.replace("</b>", "")
892        text = text.replace("<i>", ""); text = text.replace("</i>", "")
893        text = text.replace("<br>", ""); text = text.replace("&nbsp", "")
894        self.statusBar().showMessage("Last event: " + str(text), 5000)
895
896    # Loads settings from the widget's .ini file
897    def loadSettings(self):
898        self.settings = {"widgetListType": 4, "iconSize": "40 x 40", "toolbarIconSize": 2, "toolboxWidth": 200, 'schemeIconSize': 1,
899                       "snapToGrid": 1, "writeLogFile": 1, "dontAskBeforeClose": 0, "saveWidgetsPosition": 1,
900#                       "widgetSelectedColor": (0, 255, 0), "widgetActiveColor": (0, 0, 255), "lineColor": (0, 255, 0),
901                       "reportsDir": self.defaultReportsDir, "saveSchemaDir": self.canvasSettingsDir, "saveApplicationDir": self.canvasSettingsDir,
902                       "showSignalNames": 1, "useContexts": 1, "enableCanvasDropShadows": 0,
903                       "canvasWidth": 700, "canvasHeight": 600, "useDefaultPalette": 0,
904                       "focusOnCatchException": 1, "focusOnCatchOutput": 0, "printOutputInStatusBar": 1, "printExceptionInStatusBar": 1,
905                       "outputVerbosity": 0, "synchronizeHelp": 1,
906                       "ocShow": 1, "owShow": 0, "ocInfo": 1, "owInfo": 1, "ocWarning": 1, "owWarning": 1, "ocError": 1, "owError": 1,
907                       "lastAddonsRefresh": 0}
908        if RedR:
909            self.setting.update({"svnSettings": None, "versionNumber": "Version0"})
910        try:
911            filename = os.path.join(self.canvasSettingsDir, "orngCanvas.ini")
912            self.settings.update(cPickle.load(open(filename, "rb")))
913        except:
914            pass
915
916        if not self.settings.has_key("style"):
917            items = [str(n) for n in QStyleFactory.keys()]
918            lowerItems = [str(n).lower() for n in QStyleFactory.keys()]
919            if sys.platform == "darwin" and qVersion() < "4.6": #On Mac OSX full aqua style isn't supported until Qt 4.6
920                currStyle = "cleanlooks"
921            else:
922                currStyle = str(qApp.style().objectName()).lower()
923            self.settings.setdefault("style", items[lowerItems.index(currStyle)])
924
925
926    # Saves settings to this widget's .ini file
927    def saveSettings(self):
928        filename = os.path.join(self.canvasSettingsDir, "orngCanvas.ini")
929        file = open(filename, "wb")
930        if self.settings["widgetListType"] == 1:        # tree view
931            self.settings["treeItemsOpenness"] = dict([(key, self.tabs.tabDict[key].isExpanded()) for key in self.tabs.tabDict.keys()])
932        cPickle.dump(self.settings, file)
933        file.close()
934
935    def closeEvent(self, ce):
936        # save the current width of the toolbox, if we are using it
937        if isinstance(self.widgetsToolBar, orngTabs.WidgetToolBox):
938            self.settings["toolboxWidth"] = self.widgetsToolBar.toolbox.width()
939        self.settings["showWidgetToolbar"] = self.widgetsToolBar.isVisible()
940        self.settings["showToolbar"] = self.toolbar.isVisible()
941        self.settings["reportsDir"] = self.reportWindow.saveDir
942
943        closed = self.schema.close()
944        if closed:
945            self.canvasIsClosing = 1        # output window (and possibly report window also) will check this variable before it will close the window
946           
947            self.helpWindow.close()
948            self.reportWindow.close()
949           
950            self.output.catchOutput(False)
951            self.output.catchException(False)
952            self.output.hide()
953            self.output.logFile.close()
954           
955            ce.accept()
956           
957        else:
958            ce.ignore()
959       
960        self.reportWindow.removeTemp()
961       
962        size = self.geometry().size()
963        self.settings["canvasWidth"] = size.width()
964        self.settings["canvasHeight"] = size.height()
965        self.settings["CanvasMainWindowGeometry"] = str(self.saveGeometry())
966       
967        self.saveSettings()
968       
969    def wheelEvent(self, event):
970        """ Silently accept the wheel event. This is to ensure combo boxes
971        and other controls that have focus don't receive this event unless
972        the cursor is over them.
973       
974        """
975        event.accept()
976       
977
978    def setCaption(self, caption=""):
979        if caption:
980            caption = caption.split(".")[0]
981            self.setWindowTitle(caption + " - %s Canvas" % product)
982        else:
983            self.setWindowTitle("%s Canvas" % product)
984   
985    def getWidgetIcon(self, widgetInfo):
986        if self.iconNameToIcon.has_key(widgetInfo.icon):
987            return self.iconNameToIcon[widgetInfo.icon]
988       
989        iconNames = self.getFullWidgetIconName(widgetInfo)
990        iconBackgrounds = self.getFullIconBackgroundName(widgetInfo)
991        icon = QIcon()
992        if len(iconNames) == 1:
993            iconSize = QPixmap(iconNames[0]).width()
994            iconBackgrounds = [name for name in iconBackgrounds if QPixmap(name).width() == iconSize]
995        for name, back in zip(iconNames, iconBackgrounds):
996            image = QPixmap(back).toImage()
997            painter = QPainter(image)
998            painter.drawPixmap(0, 0, QPixmap(name))
999            painter.end()
1000            icon.addPixmap(QPixmap.fromImage(image))
1001        if iconNames != [self.defaultPic]:
1002            self.iconNameToIcon[widgetInfo.icon] = icon
1003        return icon
1004           
1005   
1006    def getFullWidgetIconName(self, widgetInfo):
1007        iconName = widgetInfo.icon
1008        names = []
1009        name, ext = os.path.splitext(iconName)
1010        for num in [16, 32, 40, 48, 60]:
1011            names.append("%s_%d%s" % (name, num, ext))
1012       
1013        if widgetInfo.module:
1014            widgetDir = ''
1015        else:
1016            widgetDir = str(widgetInfo.directory)  #os.path.split(self.getFileName())[0]
1017        fullPaths = []
1018        for paths in [(self.widgetDir, widgetInfo.category), (self.widgetDir,), (self.picsDir,), tuple(), (widgetDir,), (widgetDir, "icons")]:
1019            for name in names + [iconName]:
1020                fname = os.path.join(*paths + (name,))
1021                if widgetInfo.module:
1022                    if pkg_resources.resource_exists(widgetInfo.module, fname):
1023                        # TODO: Optimize, we should not be required to extract the icon
1024                        fullPaths.append(pkg_resources.resource_filename(widgetInfo.module, fname))
1025                elif os.path.exists(fname):
1026                    fullPaths.append(fname)
1027            if len(fullPaths) > 1 and fullPaths[-1].endswith(iconName):
1028                fullPaths.pop()     # if we have the new icons we can remove the default icon
1029            if fullPaths != []:
1030                return fullPaths
1031        return [self.defaultPic]
1032   
1033    def getFullIconBackgroundName(self, widgetInfo):
1034        if widgetInfo.module:
1035            widgetDir = ''
1036        else:
1037            widgetDir = str(widgetInfo.directory)
1038        fullPaths = []
1039        for paths in [(widgetDir, "icons"), (self.widgetDir, widgetInfo.category, "icons"), (self.widgetDir, "icons"), (self.picsDir,), tuple(), (widgetDir,), (widgetDir, "icons")]:
1040            for name in ["background_%d.png" % num for num in [16, 32, 40, 48, 60]]:
1041                fname = os.path.join(*paths + (name,))
1042                if widgetInfo.module:
1043                    if pkg_resources.resource_exists(widgetInfo.module, fname):
1044                        # TODO: Optimize, we should not be required to extract the icon
1045                        fullPaths.append(pkg_resources.resource_filename(widgetInfo.module, fname))
1046                elif os.path.exists(fname):
1047                    fullPaths.append(fname)
1048            if fullPaths != []:
1049                return fullPaths   
1050        return [self.defaultBackground]
1051   
1052class MyStatusBar(QStatusBar):
1053    def __init__(self, parent):
1054        QStatusBar.__init__(self, parent)
1055        self.parentWidget = parent
1056
1057    def mouseDoubleClickEvent(self, ev):
1058        self.parentWidget.menuItemShowOutputWindow()
1059       
1060class SizeGrip(QSizeGrip):
1061    def __init__(self, mainwindow):
1062        QSizeGrip.__init__(self, mainwindow)
1063        mainwindow.installEventFilter(self)
1064        self.updateMyPos(mainwindow)
1065       
1066    def eventFilter(self, obj, event):
1067        if obj is self.parent() and isinstance(event, QResizeEvent):
1068            self.updateMyPos(obj)
1069           
1070        return QSizeGrip.eventFilter(self, obj, event)
1071   
1072    def updateMyPos(self, mainwindow):
1073        window_size = mainwindow.size()
1074        mysize = self.size()
1075        self.move(window_size.width() - mysize.width(),
1076                  window_size.height() - mysize.height())
1077           
1078from collections import defaultdict
1079class EventNotifier(QObject):
1080    """ An Qt event notifier.
1081    """
1082    def __init__(self, parent=None):
1083        QObject.__init__(self, parent)
1084        self._filtering = set()
1085        self._attached = defaultdict(list)
1086       
1087    def attach(self, obj, event, slot):
1088        if hasattr(event, "__iter__"):
1089            events = list(event)
1090        else:
1091            events = [event]
1092        for e in events:
1093            self._attached[obj, e].append(slot)
1094       
1095        if obj not in self._filtering:
1096            self._filtering.add(obj)
1097            obj.installEventFilter(self)
1098           
1099    def detach(self, obj, event, slot=None):
1100        if hasattr(event, "__iter__"):
1101            events = list(event)
1102        else:
1103            events = [event]
1104        for e in events:
1105            slots = self._attached.get((obj, e), [])
1106            if slot is None:
1107                slots_to_remove = slots
1108            else:
1109                slots_to_remove = [slot]
1110            for s in slots_to_remove:
1111                if s in slots:
1112                    slots.remove(s)
1113            if not slots:
1114                del self._attached[obj, e]
1115       
1116        if not any([s for (obj_, _), s in self._attached.items() \
1117                    if obj_ == obj]):
1118            # If obj has no more slots
1119            self._filtering.remove(obj)
1120            obj.removeEventFilter(self)
1121           
1122    def eventFilter(self, obj, event):
1123        if obj in self._filtering:
1124            if (obj, type(event)) in self._attached or (obj, event.type()) in self._attached:
1125                slots = self._attached.get((obj, type(event)), []) + \
1126                        self._attached.get((obj, event.type()), [])
1127                for slot in slots:
1128                    slot()
1129        return False
1130   
1131       
1132class OrangeQApplication(QApplication):
1133    def __init__(self, *args):
1134        QApplication.__init__(self, *args)
1135       
1136    #QFileOpenEvent are Mac OSX only
1137    if sys.platform == "darwin":
1138        def event(self, event):
1139            if event.type() == QEvent.FileOpen:
1140                file = str(event.file())
1141                def send():
1142                    if hasattr(qApp, "canvasDlg"):
1143                        qApp.canvasDlg.openSchema(file)
1144                    else:
1145                        QTimer.singleShot(100, send)
1146                send()
1147            return QApplication.event(self, event)
1148       
1149#    def notify(self, receiver, event):
1150#        eventDict = {0: 'None', 1: 'Timer', 2: 'MouseButtonPress', 3: 'MouseButtonRelease', 4: 'MouseButtonDblClick', 5: 'MouseMove', 6: 'KeyPress', 7: 'KeyRelease', 8: 'FocusIn', 9: 'FocusOut', 10: 'Enter', 11: 'Leave', 12: 'Paint', 13: 'Move', 14: 'Resize', 17: 'Show', 18: 'Hide', 19: 'Close', 21: 'ParentChange', 24: 'WindowActivate', 25: 'WindowDeactivate', 26: 'ShowToParent', 27: 'HideToParent', 31: 'Wheel', 33: 'WindowTitleChange', 34: 'WindowIconChange', 35: 'ApplicationWindowIconChange', 36: 'ApplicationFontChange', 37: 'ApplicationLayoutDirectionChange', 38: 'ApplicationPaletteChange', 39: 'PaletteChange', 40: 'Clipboard', 43: 'MetaCall', 50: 'SockAct', 51: 'ShortcutOverride', 52: 'DeferredDelete', 60: 'DragEnter', 61: 'DragMove', 62: 'DragLeave', 63: 'Drop', 68: 'ChildAdded', 69: 'ChildPolished', 70: 'ChildInserted', 71: 'ChildRemoved', 74: 'PolishRequest', 75: 'Polish', 76: 'LayoutRequest', 77: 'UpdateRequest', 78: 'UpdateLater', 82: 'ContextMenu', 83: 'InputMethod', 86: 'AccessibilityPrepare', 87: 'TabletMove', 88: 'LocaleChange', 89: 'LanguageChange', 90: 'LayoutDirectionChange', 92: 'TabletPress', 93: 'TabletRelease', 94: 'OkRequest', 96: 'IconDrag', 97: 'FontChange', 98: 'EnabledChange', 99: 'ActivationChange', 100: 'StyleChange', 101: 'IconTextChange', 102: 'ModifiedChange', 103: 'WindowBlocked', 104: 'WindowUnblocked', 105: 'WindowStateChange', 109: 'MouseTrackingChange', 110: 'ToolTip', 111: 'WhatsThis', 112: 'StatusTip', 113: 'ActionChanged', 114: 'ActionAdded', 115: 'ActionRemoved', 116: 'FileOpen', 117: 'Shortcut', 118: 'WhatsThisClicked', 119: 'AccessibilityHelp', 120: 'ToolBarChange', 121: 'ApplicationActivate', 122: 'ApplicationDeactivate', 123: 'QueryWhatsThis', 124: 'EnterWhatsThisMode', 125: 'LeaveWhatsThisMode', 126: 'ZOrderChange', 127: 'HoverEnter', 128: 'HoverLeave', 129: 'HoverMove', 130: 'AccessibilityDescription', 131: 'ParentAboutToChange', 132: 'WinEventAct', 150: 'EnterEditFocus', 151: 'LeaveEditFocus', 153: 'MenubarUpdated', 155: 'GraphicsSceneMouseMove', 156: 'GraphicsSceneMousePress', 157: 'GraphicsSceneMouseRelease', 158: 'GraphicsSceneMouseDoubleClick', 159: 'GraphicsSceneContextMenu', 160: 'GraphicsSceneHoverEnter', 161: 'GraphicsSceneHoverMove', 162: 'GraphicsSceneHoverLeave', 163: 'GraphicsSceneHelp', 164: 'GraphicsSceneDragEnter', 165: 'GraphicsSceneDragMove', 166: 'GraphicsSceneDragLeave', 167: 'GraphicsSceneDrop', 168: 'GraphicsSceneWheel', 169: 'KeyboardLayoutChange', 170: 'DynamicPropertyChange', 171: 'TabletEnterProximity', 172: 'TabletLeaveProximity', 173: 'NonClientAreaMouseMove', 174: 'NonClientAreaMouseButtonPress', 175: 'NonClientAreaMouseButtonRelease', 176: 'NonClientAreaMouseButtonDblClick', 177: 'MacSizeChange', 178: 'ContentsRectChange', 181: 'GraphicsSceneResize', 182: 'GraphicsSceneMove', 183: 'CursorChange', 184: 'ToolTipChange', 186: 'GrabMouse', 187: 'UngrabMouse', 188: 'GrabKeyboard', 189: 'UngrabKeyboard'}
1151#        import time
1152#        if isinstance(receiver, QWidget):
1153#            if str(receiver.windowTitle()) != "":
1154#                print time.strftime("%H:%M:%S"), "%15s %15s" % (str(receiver.windowTitle()) ,type(receiver).__name__)  + ": ",      # print only events for QWidget classes and up
1155#                if eventDict.has_key(event.type()):
1156#                    print eventDict[event.type()]
1157#                else:
1158#                    print "unknown event name (" + str(event.type()) + ")"
1159#        else:
1160#            print time.strftime("%H:%M:%S"), type(receiver).__name__, str(receiver.objectName()),
1161#            if eventDict.has_key(event.type()):
1162#                print eventDict[event.type()]
1163#            else:
1164#                print "unknown event name (" + str(event.type()) + ")"
1165#               
1166#        return QApplication.notify(self, receiver, event)
1167
1168
1169def main(argv=None):
1170    if argv == None:
1171        argv = sys.argv
1172
1173    app = OrangeQApplication(sys.argv)
1174    dlg = OrangeCanvasDlg(app)
1175    qApp.canvasDlg = dlg
1176    dlg.show()
1177    for arg in sys.argv[1:]:
1178        if arg == "-reload":
1179            dlg.menuItemOpenLastSchema()
1180    r = app.exec_()
1181    app.closeAllWindows()
1182   
1183    del qApp.canvasDlg
1184    del dlg
1185   
1186    app.processEvents()
1187   
1188    # Call gc.collect before the app is destroyed to colect any remainig
1189    # cycles. Some sip objects are stored in it's global state (for instance
1190    # QGraphicsScene) and can cause a crash in Py_Finalize when sip trys to
1191    # deallocate it (a bug in sip?).
1192   
1193    gc.collect()
1194    del app
1195   
1196    return r
1197
1198if __name__ == "__main__":
1199    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.