source: orange/Orange/OrangeCanvas/orngCanvas.pyw @ 9671:a7b056375472

Revision 9671:a7b056375472, 59.0 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved orange to Orange (part 2)

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