source: orange/Orange/OrangeCanvas/orngDoc.py @ 10463:431347363442

Revision 10463:431347363442, 33.7 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Added unicode filename support for save/load dialogs.

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2# Description:
3#    document class - main operations (save, load, ...)
4#
5from __future__ import with_statement
6
7from PyQt4.QtCore import *
8from PyQt4.QtGui import *
9import sys, os, os.path, traceback
10from xml.dom.minidom import Document, parse
11import orngView, orngCanvasItems, orngTabs
12from orngDlgs import *
13from orngSignalManager import SignalManager
14import cPickle, math, orngHistory
15
16class SchemaDoc(QWidget):
17    def __init__(self, canvasDlg, *args):
18        QWidget.__init__(self, *args)
19        self.canvasDlg = canvasDlg
20        self.ctrlPressed = 0
21
22        self.lines = []                         # list of orngCanvasItems.CanvasLine items
23        self.widgets = []                       # list of orngCanvasItems.CanvasWidget items
24        self.signalManager = SignalManager()    # signal manager to correctly process signals
25
26        self.schemaPath = self.canvasDlg.settings["saveSchemaDir"]
27        self.schemaName = ""
28        self.loadedSettingsDict = {}
29        self.setLayout(QVBoxLayout())
30        #self.canvas = QGraphicsScene(0,0,2000,2000)
31        self.canvas = QGraphicsScene()
32
33        self.guide_text = self.canvas.addSimpleText("Right-click to add widgets")
34        self.guide_text.setBrush(QBrush(QColor(235,235,235)))
35        font = QFont()
36        font.setStyleHint(QFont.SansSerif)
37        font.setPixelSize(36)
38        self.guide_text.setFont(font)
39
40        oneItem = self.canvas.addRect(QRectF(0.0, 0.0, 300.0, 300.0)) # initial item so sceneRect always contains QPoint(0, 0)
41        self.canvas.sceneRect() # call sceneRect so it updates the rect
42        self.canvas.removeItem(oneItem)
43       
44        self.canvasView = orngView.SchemaView(self, self.canvas, self)
45        self.layout().addWidget(self.canvasView)
46        self.layout().setMargin(0)
47        self.schemaID = orngHistory.logNewSchema()
48
49        self.update_guide()
50
51    def update_guide(self):
52        """ Sets the visibility of the guide text """
53        visible = not len(self.widgets)
54        self.guide_text.setVisible(visible)
55        if visible:
56            self.canvasView.ensureVisible(self.guide_text)
57
58    def isSchemaChanged(self):
59        """ Is this schema document modified.
60        """
61        return self.loadedSettingsDict != dict([(widget.caption, widget.instance.saveSettingsStr()) for widget in self.widgets])
62   
63    def setSchemaModified(self, state):
64        """ Set the modified document state flag.
65        """
66        # Update the loaded settings dict so we know if the widget
67        # settings have changed from the last save when we quit
68        # the application (see closeEvent handler)
69        if not state:
70            self.loadedSettingsDict = dict([(widget.caption, widget.instance.saveSettingsStr()) for widget in self.widgets])
71        else:
72            self.loadedSettingsDict = {}
73       
74    def closeEvent(self, ce):
75        if self.saveBeforeClose():
76            self.clear()
77            self.removeTempDoc()
78            ce.accept()
79        else:
80            ce.ignore()
81            return
82           
83        QWidget.closeEvent(self, ce)
84        orngHistory.logCloseSchema(self.schemaID)
85       
86       
87    # save a temp document whenever anything changes. this doc is deleted on closeEvent
88    # in case that Orange crashes, Canvas on the next start offers an option to reload the crashed schema with links frozen
89    def saveTempDoc(self):
90        if self.widgets != []:
91            tempName = os.path.join(self.canvasDlg.canvasSettingsDir, "tempSchema.tmp")
92            self.save(tempName)
93       
94    def removeTempDoc(self):
95        tempName = os.path.join(self.canvasDlg.canvasSettingsDir, "tempSchema.tmp")
96        if os.path.exists(tempName):
97            os.remove(tempName)
98
99    # called to properly close all widget contexts
100    def synchronizeContexts(self):
101        for widget in self.widgets[::-1]:
102            widget.instance.synchronizeContexts()
103
104    # add line connecting widgets outWidget and inWidget
105    # if necessary ask which signals to connect
106    def addLine(self, outWidget, inWidget, enabled=True, saveTempDoc=True):
107        if outWidget == inWidget: 
108            return None
109        # check if line already exists
110        line = self.getLine(outWidget, inWidget)
111        if line:
112            self.resetActiveSignals(outWidget, inWidget, None, enabled, saveTempDoc)
113            return None
114
115        if self.signalManager.existsPath(inWidget.instance, outWidget.instance):
116            QMessageBox.critical( self, "Failed to Connect", "Circular connections are not allowed in Orange Canvas.", QMessageBox.Ok)
117            return None
118
119        dialog = SignalDialog(self.canvasDlg, self.canvasDlg)
120        dialog.setOutInWidgets(outWidget, inWidget)
121        connectStatus = dialog.addDefaultLinks()
122        if connectStatus == 0:
123            QMessageBox.information( self, "Failed to Connect", "Selected widgets don't share a common signal type.", QMessageBox.Ok)
124            return
125
126        # if there are multiple choices, how to connect this two widget, then show the dialog
127        if len(dialog.getLinks()) > 1 or dialog.multiplePossibleConnections or dialog.getLinks() == []:
128            if dialog.exec_() == QDialog.Rejected:
129                return None
130        else:
131            dialog.deleteLater() # Dialog must be deleted
132
133#        self.signalManager.setFreeze(1)
134        with self.signalManager.freeze(outWidget.instance):
135            linkCount = 0
136            for (outName, inName) in dialog.getLinks():
137                linkCount += self.addLink(outWidget, inWidget, outName, inName, enabled, saveTempDoc=False)
138
139#        self.signalManager.setFreeze(0, outWidget.instance)
140
141        # if signals were set correctly create the line, update widget tooltips and show the line
142        line = self.getLine(outWidget, inWidget)
143        if line:
144            outWidget.updateTooltip()
145            inWidget.updateTooltip()
146           
147        if saveTempDoc:
148            self.saveTempDoc()
149        return line
150
151
152    # reset signals of an already created line
153    def resetActiveSignals(self, outWidget, inWidget, newSignals = None, enabled = 1, saveTempDoc=True):
154        #print "<extra>orngDoc.py - resetActiveSignals() - ", outWidget, inWidget, newSignals
155        signals = []
156        for line in self.lines:
157            if line.outWidget == outWidget and line.inWidget == inWidget:
158                signals = line.getSignals()
159
160        if newSignals == None:
161            dialog = SignalDialog(self.canvasDlg, self.canvasDlg)
162            dialog.setOutInWidgets(outWidget, inWidget)
163            for (outName, inName) in signals:
164                #print "<extra>orngDoc.py - SignalDialog.addLink() - adding signal to dialog: ", outName, inName
165                dialog.addLink(outName, inName)
166
167            # if there are multiple choices, how to connect this two widget, then show the dialog
168            if dialog.exec_() == QDialog.Rejected:
169                return
170
171            newSignals = dialog.getLinks()
172
173        for (outName, inName) in signals:
174            if (outName, inName) not in newSignals:
175                self.removeLink(outWidget, inWidget, outName, inName, saveTempDoc=False)
176                signals.remove((outName, inName))
177
178        with self.signalManager.freeze(outWidget.instance):
179            for (outName, inName) in newSignals:
180                if (outName, inName) not in signals:
181                    self.addLink(outWidget, inWidget, outName, inName, enabled, saveTempDoc=False)
182
183        outWidget.updateTooltip()
184        inWidget.updateTooltip()
185       
186        if saveTempDoc:
187            self.saveTempDoc()
188
189
190
191    # add one link (signal) from outWidget to inWidget. if line doesn't exist yet, we create it
192    def addLink(self, outWidget, inWidget, outSignalName, inSignalName, enabled=1, saveTempDoc=True):
193        #print "<extra>orngDoc - addLink() - ", outWidget, inWidget, outSignalName, inSignalName
194        # in case there already exists link to inSignalName in inWidget that is single, we first delete it
195        widgetInstance = inWidget.instance.removeExistingSingleLink(inSignalName)
196        if widgetInstance:
197            widget = self.findWidgetFromInstance(widgetInstance)
198            existingSignals = self.signalManager.findSignals(widgetInstance, inWidget.instance)
199            for (outN, inN) in existingSignals:
200                if inN == inSignalName:
201                    self.removeLink(widget, inWidget, outN, inN)
202                    line = self.getLine(widget, inWidget)
203                    if line:
204                        line.updateTooltip()
205
206        # if line does not exist yet, we must create it
207        existingSignals = self.signalManager.findSignals(outWidget.instance, inWidget.instance)
208        if not existingSignals:
209            line = orngCanvasItems.CanvasLine(self.signalManager, self.canvasDlg, self.canvasView, outWidget, inWidget, self.canvas)
210            self.lines.append(line)
211            line.show()
212            outWidget.addOutLine(line)
213            outWidget.updateTooltip()
214            inWidget.addInLine(line)
215            inWidget.updateTooltip()
216        else:
217            line = self.getLine(outWidget, inWidget)
218
219        ok = self.signalManager.addLink(outWidget.instance, inWidget.instance, outSignalName, inSignalName, enabled)
220        if not ok:
221            self.removeLink(outWidget, inWidget, outSignalName, inSignalName)
222            QMessageBox.warning( None, "Orange Canvas", "Unable to add link. Try restarting Orange Canvas.", QMessageBox.Ok + QMessageBox.Default, 0)
223            return 0
224        else:
225            orngHistory.logAddLink(self.schemaID, outWidget, inWidget, outSignalName)
226
227        line.updateTooltip()
228        line.update()
229        return 1
230
231
232    # remove only one signal from connected two widgets. If no signals are left, delete the line
233    def removeLink(self, outWidget, inWidget, outSignalName, inSignalName, saveTempDoc=True):
234        #print "<extra> orngDoc.py - removeLink() - ", outWidget, inWidget, outSignalName, inSignalName
235        self.signalManager.removeLink(outWidget.instance, inWidget.instance, outSignalName, inSignalName)
236
237       
238        otherSignals = self.signalManager.getLinks(outWidget.instance, inWidget.instance, outSignalName, inSignalName)
239        if not otherSignals:
240            self.removeLine(outWidget, inWidget, saveTempDoc=False)
241
242        if saveTempDoc:
243            self.saveTempDoc()
244
245
246    # remove line line
247    def removeLine1(self, line, saveTempDoc=True):
248        for (outName, inName) in line.getSignals():
249            self.signalManager.removeLink(line.outWidget.instance, line.inWidget.instance, outName, inName)   # update SignalManager
250
251        self.lines.remove(line)
252        line.inWidget.removeLine(line)
253        line.outWidget.removeLine(line)
254        line.inWidget.updateTooltip()
255        line.outWidget.updateTooltip()
256        line.remove()
257        if saveTempDoc:
258            self.saveTempDoc()
259
260    # remove line, connecting two widgets
261    def removeLine(self, outWidget, inWidget, saveTempDoc=True):
262        """ Remove the line connecting two widgets
263        """
264        #print "<extra> orngDoc.py - removeLine() - ", outWidget, inWidget
265        line = self.getLine(outWidget, inWidget)
266        if line:
267            self.removeLine1(line, saveTempDoc)
268
269    # add new widget
270    def addWidget(self, widgetInfo, x= -1, y=-1, caption = "", widgetSettings = {}, saveTempDoc = True):
271        qApp.setOverrideCursor(Qt.WaitCursor)
272        try:
273            newwidget = orngCanvasItems.CanvasWidget(self.signalManager, self.canvas, self.canvasView, widgetInfo, self.canvasDlg.defaultPic, self.canvasDlg, widgetSettings)
274        except:
275            type, val, traceback = sys.exc_info()
276            sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that it doesn't crash canvas
277            qApp.restoreOverrideCursor()
278            return None
279
280        if x==-1 or y==-1:
281            if self.widgets != []:
282                x = self.widgets[-1].x() + 110
283                y = self.widgets[-1].y()
284            else:
285                x = 30
286                y = 150
287        newwidget.setCoords(x, y)
288        # move the widget to a valid position if necessary
289        invalidPosition = (self.canvasView.findItemTypeCount(self.canvas.collidingItems(newwidget), orngCanvasItems.CanvasWidget) > 0)
290        if invalidPosition:
291            for r in range(20, 200, 20):
292                for fi in [90, -90, 180, 0, 45, -45, 135, -135]:
293                    xOff = r * math.cos(math.radians(fi))
294                    yOff = r * math.sin(math.radians(fi))
295                    rect = QRectF(x+xOff, y+yOff, 48, 48)
296                    invalidPosition = self.canvasView.findItemTypeCount(self.canvas.items(rect), orngCanvasItems.CanvasWidget) > 0
297                    if not invalidPosition:
298                        newwidget.setCoords(x+xOff, y+yOff)
299                        break
300                if not invalidPosition:
301                    break
302           
303        #self.canvasView.ensureVisible(newwidget)
304
305        if caption == "":
306            caption = newwidget.caption
307
308        if self.getWidgetByCaption(caption):
309            i = 2
310            while self.getWidgetByCaption(caption + " (" + str(i) + ")"):
311                i+=1
312            caption = caption + " (" + str(i) + ")"
313        newwidget.updateText(caption)
314        newwidget.instance.setWindowTitle(caption)
315
316        self.widgets.append(newwidget)
317        if saveTempDoc:
318            self.saveTempDoc()
319        self.canvas.update()
320
321        # show the widget and activate the settings
322        try:
323            self.signalManager.addWidget(newwidget.instance)
324            newwidget.show()
325            newwidget.updateTooltip()
326            newwidget.setProcessing(1)
327            if self.canvasDlg.settings["saveWidgetsPosition"]:
328                newwidget.instance.restoreWidgetPosition()
329            newwidget.setProcessing(0)
330            orngHistory.logAddWidget(self.schemaID, id(newwidget), (newwidget.widgetInfo.category, newwidget.widgetInfo.name), newwidget.x(), newwidget.y())
331        except:
332            type, val, traceback = sys.exc_info()
333            sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that it doesn't crash canvas
334
335        qApp.restoreOverrideCursor()
336        self.update_guide()
337        return newwidget
338
339    # remove widget
340    def removeWidget(self, widget, saveTempDoc = True):
341        if not widget:
342            return
343       
344        #with self.signalManager.freeze():
345        while widget.inLines != []: self.removeLine1(widget.inLines[0])
346        while widget.outLines != []:  self.removeLine1(widget.outLines[0])
347   
348        self.signalManager.removeWidget(widget.instance)
349           
350        self.widgets.remove(widget)
351        widget.remove()
352        if saveTempDoc:
353            self.saveTempDoc()
354       
355        self.update_guide()
356        orngHistory.logRemoveWidget(self.schemaID, id(widget), (widget.widgetInfo.category, widget.widgetInfo.name))
357
358    def clear(self):
359        self.canvasDlg.setCaption()
360        for widget in self.widgets[::-1]:   
361            self.removeWidget(widget, saveTempDoc = False)   # remove widgets from last to first
362        self.canvas.update()
363        self.schemaPath = self.canvasDlg.settings["saveSchemaDir"]
364        self.schemaName = ""
365        self.loadedSettingsDict = {}
366        self.saveTempDoc()
367
368    def enableAllLines(self):
369        with self.signalManager.freeze():
370            for line in self.lines:
371                self.signalManager.setLinkEnabled(line.outWidget.instance, line.inWidget.instance, 1)
372                line.update()
373        self.canvas.update()
374
375    def disableAllLines(self):
376        for line in self.lines:
377            self.signalManager.setLinkEnabled(line.outWidget.instance, line.inWidget.instance, 0)
378            line.update()
379        self.canvas.update()
380       
381    def setFreeze(self, bool):
382        self.signalManager.setFreeze(self.signalManager.freezing + (1 if bool else -1), None)
383
384    # return a new widget instance of a widget with filename "widgetName"
385    def addWidgetByFileName(self, widgetFileName, x, y, caption, widgetSettings = {}, saveTempDoc = True):
386        for category in self.canvasDlg.widgetRegistry.keys():
387            for name, widget in self.canvasDlg.widgetRegistry[category].items():
388                if widget.fileName == widgetFileName: 
389                    return self.addWidget(widget, x, y, caption, widgetSettings, saveTempDoc)
390        return None
391
392    # return the widget instance that has caption "widgetName"
393    def getWidgetByCaption(self, widgetName):
394        for widget in self.widgets:
395            if (widget.caption == widgetName):
396                return widget
397        return None
398
399    def getWidgetCaption(self, widgetInstance):
400        for widget in self.widgets:
401            if widget.instance == widgetInstance:
402                return widget.caption
403        print "Error. Invalid widget instance : ", widgetInstance
404        return ""
405
406
407    # get line from outWidget to inWidget
408    def getLine(self, outWidget, inWidget):
409        for line in self.lines:
410            if line.outWidget == outWidget and line.inWidget == inWidget:
411                return line
412        return None
413
414
415    # find orngCanvasItems.CanvasWidget from widget instance
416    def findWidgetFromInstance(self, widgetInstance):
417        for widget in self.widgets:
418            if widget.instance == widgetInstance:
419                return widget
420        return None
421
422
423    # ###########################################
424    # SAVING, LOADING, ....
425    # ###########################################
426    def reportAll(self):
427        for widget in self.widgets:
428            widget = widget.instance
429            if hasattr(widget, "sendReport"):
430                widget.reportAndFinish()
431           
432    def saveDocument(self):
433        if self.schemaName == "":
434            self.saveDocumentAs()
435        else:
436            self.save()
437            self.setSchemaModified(False)
438
439    def saveDocumentAs(self):
440        name = QFileDialog.getSaveFileName(self, "Save Orange Schema", os.path.join(self.schemaPath, self.schemaName or "Untitled.ows"), "Orange Widget Schema (*.ows)")
441        name = unicode(name)
442        if os.path.splitext(name)[0] == "":
443            return
444        if os.path.splitext(name)[1].lower() != ".ows":
445            name = os.path.splitext(name)[0] + ".ows"
446        self.save(name)
447        self.setSchemaModified(False)
448       
449    # save the file
450    def save(self, filename=None):
451        if filename is None:
452            filename = os.path.join(self.schemaPath, self.schemaName)
453           
454        # create xml document
455        doc = Document()
456        schema = doc.createElement("schema")
457        widgets = doc.createElement("widgets")
458        lines = doc.createElement("channels")
459        settings = doc.createElement("settings")
460        doc.appendChild(schema)
461        schema.appendChild(widgets)
462        schema.appendChild(lines)
463        schema.appendChild(settings)
464        settingsDict = {}
465
466        #save widgets
467        for widget in self.widgets:
468            temp = doc.createElement("widget")
469            temp.setAttribute("xPos", str(int(widget.x())) )
470            temp.setAttribute("yPos", str(int(widget.y())) )
471            temp.setAttribute("caption", widget.caption)
472            temp.setAttribute("widgetName", widget.widgetInfo.fileName)
473            settingsDict[widget.caption] = widget.instance.saveSettingsStr()
474            widgets.appendChild(temp)
475
476        #save connections
477        for line in self.lines:
478            temp = doc.createElement("channel")
479            temp.setAttribute("outWidgetCaption", line.outWidget.caption)
480            temp.setAttribute("inWidgetCaption", line.inWidget.caption)
481            temp.setAttribute("enabled", str(line.getEnabled()))
482            temp.setAttribute("signals", str(line.getSignals()))
483            lines.appendChild(temp)
484
485        settings.setAttribute("settingsDictionary", str(settingsDict))
486
487        xmlText = doc.toprettyxml()
488
489        file = open(filename, "wt")
490        file.write(xmlText)
491        file.close()
492        doc.unlink()
493
494        if os.path.splitext(filename)[1].lower() == ".ows":
495            (self.schemaPath, self.schemaName) = os.path.split(filename)
496            self.canvasDlg.settings["saveSchemaDir"] = self.schemaPath
497            self.canvasDlg.addToRecentMenu(filename)
498            self.canvasDlg.setCaption(self.schemaName)
499
500    def saveBeforeClose(self):
501        """ Call to ask the user to save the schema before it is closed.
502        Return True if save was successful or user did not want to save,
503        else return False (user canceled the operation).
504         
505        """
506        newSettings = self.isSchemaChanged()
507
508        self.synchronizeContexts()
509        if self.widgets != []:
510            self.save(os.path.join(self.canvasDlg.canvasSettingsDir, "lastSchema.tmp"))
511
512        if self.canvasDlg.settings["dontAskBeforeClose"]:
513            if newSettings and self.schemaName != "":
514                self.save()
515            return True
516               
517        elif newSettings:
518            res = QMessageBox.question(self, 'Orange Canvas', 'Do you wish to save the schema?', QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
519            if res == QMessageBox.Yes:
520                self.saveDocument()
521                return True
522            elif res == QMessageBox.No:
523                return True
524            else:     
525                # User pressed cancel - we don't want to close the document
526                return False
527        else:
528            return True
529
530    # load a scheme with name "filename"
531    def loadDocument(self, filename, caption = None, freeze = 0):
532        self.clear()
533       
534        if not os.path.exists(filename):
535            if os.path.splitext(filename)[1].lower() != ".tmp":
536                QMessageBox.critical(self, 'Orange Canvas', 'Unable to locate file "'+ filename + '"',  QMessageBox.Ok)
537            return
538
539        # set cursor
540        qApp.setOverrideCursor(Qt.WaitCursor)
541        failureText = ""
542       
543        if os.path.splitext(filename)[1].lower() == ".ows":
544            self.schemaPath, self.schemaName = os.path.split(filename)
545            self.canvasDlg.setCaption(caption or self.schemaName)
546        self.signalManager.freeze().push()
547        try:
548            #load the data ...
549            doc = parse(filename)
550            schema = doc.firstChild
551            widgets = schema.getElementsByTagName("widgets")[0]
552            lines = schema.getElementsByTagName("channels")[0]
553            settings = schema.getElementsByTagName("settings")
554            settingsDict = eval(str(settings[0].getAttribute("settingsDictionary")))
555            self.loadedSettingsDict = settingsDict
556             
557            # read widgets
558            loadedOk = 1
559            for widget in widgets.getElementsByTagName("widget"):
560                name = widget.getAttribute("widgetName")
561                settings = cPickle.loads(settingsDict[widget.getAttribute("caption")])
562                tempWidget = self.addWidgetByFileName(name, int(widget.getAttribute("xPos")), int(widget.getAttribute("yPos")), widget.getAttribute("caption"), settings, saveTempDoc = False)
563                if not tempWidget:
564                    #QMessageBox.information(self, 'Orange Canvas','Unable to create instance of widget \"'+ name + '\"',  QMessageBox.Ok + QMessageBox.Default)
565                    failureText += '<nobr>Unable to create instance of a widget <b>%s</b></nobr><br>' %(name)
566                    loadedOk = 0
567                qApp.processEvents()
568
569            #read lines
570            lineList = lines.getElementsByTagName("channel")
571            for line in lineList:
572                inCaption = line.getAttribute("inWidgetCaption")
573                outCaption = line.getAttribute("outWidgetCaption")
574                if freeze: enabled = 0
575                else:      enabled = int(line.getAttribute("enabled"))
576                signals = line.getAttribute("signals")
577                inWidget = self.getWidgetByCaption(inCaption)
578                outWidget = self.getWidgetByCaption(outCaption)
579                if inWidget == None or outWidget == None:
580                    failureText += "<nobr>Failed to create a signal line between widgets <b>%s</b> and <b>%s</b></nobr><br>" % (outCaption, inCaption)
581                    loadedOk = 0
582                    continue
583
584                signalList = eval(signals)
585                for (outName, inName) in signalList:
586                    self.addLink(outWidget, inWidget, outName, inName, enabled, saveTempDoc=False)
587                #qApp.processEvents()
588        finally:
589            qApp.restoreOverrideCursor()
590            self.signalManager.freeze().pop()
591
592        for widget in self.widgets:
593            widget.updateTooltip()
594        self.canvas.update()
595
596        self.saveTempDoc()
597
598        if not loadedOk:
599            QMessageBox.information(self, 'Schema Loading Failed', 'The following errors occured while loading the schema: <br><br>' + failureText,  QMessageBox.Ok + QMessageBox.Default)
600
601        if self.widgets:
602            self.signalManager.processNewSignals(self.widgets[0].instance)
603           
604        # Store the loaded settings dict again
605        self.loadedSettingsDict = dict((widget.caption, widget.instance.saveSettingsStr()) for widget in self.widgets)
606        self.canvasDlg.setWindowModified(False)
607
608        # do we want to restore last position and size of the widget
609        if self.canvasDlg.settings["saveWidgetsPosition"]:
610            for widget in self.widgets:
611                widget.instance.restoreWidgetStatus()
612           
613       
614
615    # save document as application
616    def saveDocumentAsApp(self, asTabs = 1):
617        # get filename
618        extension = sys.platform == "win32" and ".pyw" or ".py"
619        appName = (os.path.splitext(self.schemaName)[0] or "Schema") + extension
620        appPath = os.path.exists(self.canvasDlg.settings["saveApplicationDir"]) and self.canvasDlg.settings["saveApplicationDir"] or self.schemaPath
621        qname = QFileDialog.getSaveFileName(self, "Save Orange Schema as Application", os.path.join(appPath, appName) , "Orange Scripts (*%s)" % extension)
622        if qname.isEmpty(): return
623        qname = unicode(qname)
624        (appPath, appName) = os.path.split(qname)
625        appNameWithoutExt = os.path.splitext(appName)[0]
626        if os.path.splitext(appName)[1].lower() not in [".py", ".pyw"]: appName = appNameWithoutExt + extension
627        self.canvasDlg.settings["saveApplicationDir"] = appPath
628
629        saveDlg = saveApplicationDlg(None)
630
631        # add widget captions
632        for instance in self.signalManager.widgets:
633            widget = None
634            for i in range(len(self.widgets)):
635                if self.widgets[i].instance == instance: saveDlg.insertWidgetName(self.widgets[i].caption)
636
637        if saveDlg.exec_() == QDialog.Rejected:
638            return
639
640        #format string with file content
641        t = "    "  # instead of tab
642        n = "\n"
643
644        start = """#!/usr/bin/env python
645
646#This file is automatically created by Orange Canvas and containing an Orange schema
647
648import orngEnviron
649import orngDebugging
650import sys, os, cPickle, orange, orngSignalManager, OWGUI
651from OWBaseWidget import *
652
653class GUIApplication(OWBaseWidget):
654    def __init__(self,parent=None):
655        self.signalManager = orngSignalManager.SignalManager()
656        OWBaseWidget.__init__(self, title = '%s', signalManager = self.signalManager)
657        self.widgets = {}
658        self.loadSettings()
659        """ % (appNameWithoutExt)
660
661        if asTabs == 1:
662            start += """
663        self.tabs = QTabWidget(self)
664        self.setLayout(QVBoxLayout())
665        self.layout().addWidget(self.tabs)
666        self.resize(800,600)"""
667        else:
668            start += """
669        self.setLayout(QVBoxLayout())
670        self.box = OWGUI.widgetBox(self, 'Widgets')"""
671
672
673        links = "# add widget signals\n"+t+t + "self.signalManager.setFreeze(1)\n" +t+t
674        widgetParameters = ""
675
676        # gui for shown widgets
677        for widgetName in saveDlg.shownWidgetList:    # + saveDlg.hiddenWidgetList
678            if widgetName != "[Separator]":
679                widget = None
680                for i in range(len(self.widgets)):
681                    if self.widgets[i].caption == widgetName: widget = self.widgets[i]
682
683                shown = widgetName in saveDlg.shownWidgetList
684                widgetParameters += "self.createWidget('%s', '%s', '%s', %d, self.signalManager)\n" % (widget.widgetInfo.fileName, widget.widgetInfo.icon, widget.caption, shown) +t+t
685            else:
686                if not asTabs:
687                    widgetParameters += "self.box.layout().addSpacing(10)\n" +t+t
688
689        for line in self.lines:
690            if not line.getEnabled(): continue
691            for (outName, inName) in line.getSignals():
692                links += "self.signalManager.addLink( self.widgets['" + line.outWidget.caption+ "'], self.widgets['" + line.inWidget.caption+ "'], '" + outName + "', '" + inName + "', 1)\n" +t+t
693
694        links += "self.signalManager.setFreeze(0)\n" +t+t
695        if not asTabs:
696            widgetParameters += """
697        box2 = OWGUI.widgetBox(self, 1)
698        exitButton = OWGUI.button(box2, self, "Exit", callback = self.accept)
699        self.layout().addStretch(100)"""
700
701        if asTabs:
702            guiText = "OWGUI.createTabPage(self.tabs, caption, widget)"
703        else:
704            guiText = "OWGUI.button(self.box, self, caption, callback = widget.reshow)"
705
706        progress = """
707        statusBar = QStatusBar(self)
708        self.layout().addWidget(statusBar)
709        self.caption = QLabel('', statusBar)
710        self.caption.setMaximumWidth(230)
711        self.progress = QProgressBar(statusBar)
712        self.progress.setMaximumWidth(100)
713        self.status = QLabel("", statusBar)
714        self.status.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
715        statusBar.addWidget(self.progress)
716        statusBar.addWidget(self.caption)
717        statusBar.addWidget(self.status)"""
718
719        handlerFuncts = """
720    def createWidget(self, fname, iconName, caption, shown, signalManager):
721        widgetSettings = cPickle.loads(self.strSettings[caption])
722        m = __import__(fname)
723        widget = m.__dict__[fname].__new__(m.__dict__[fname], _settingsFromSchema = widgetSettings)
724        widget.__init__(signalManager=signalManager)
725        widget.setEventHandler(self.eventHandler)
726        widget.setProgressBarHandler(self.progressHandler)
727        widget.setWidgetIcon(iconName)
728        widget.setWindowTitle(caption)
729        self.signalManager.addWidget(widget)
730        self.widgets[caption] = widget
731        if shown: %s
732        for dlg in getattr(widget, "wdChildDialogs", []):
733            dlg.setEventHandler(self.eventHandler)
734            dlg.setProgressBarHandler(self.progressHandler)
735
736    def eventHandler(self, text, eventVerbosity = 1):
737        if orngDebugging.orngVerbosity >= eventVerbosity:
738            self.status.setText(text)
739
740    def progressHandler(self, widget, val):
741        if val < 0:
742            self.caption.setText("<nobr>Processing: <b>" + str(widget.captionTitle) + "</b></nobr>")
743            self.progress.setValue(0)
744        elif val >100:
745            self.caption.setText("")
746            self.progress.reset()
747        else:
748            self.progress.setValue(val)
749            self.update()
750
751    def loadSettings(self):
752        try:
753            file = open("%s", "r")
754            self.strSettings = cPickle.load(file)
755            file.close()
756
757        except:
758            print "unable to load settings"
759            pass
760
761    def closeEvent(self, ev):
762        OWBaseWidget.closeEvent(self, ev)
763        if orngDebugging.orngDebuggingEnabled: return
764        strSettings = {}
765        for (name, widget) in self.widgets.items():
766            widget.synchronizeContexts()
767            strSettings[name] = widget.saveSettingsStr()
768            widget.close()
769        file = open("%s", "w")
770        cPickle.dump(strSettings, file)
771        file.close()
772
773if __name__ == "__main__":
774    application = QApplication(sys.argv)
775    ow = GUIApplication()
776    ow.show()
777    # comment the next line if in debugging mode and are interested only in output text in 'signalManagerOutput.txt' file
778    application.exec_()
779        """ % (guiText, appNameWithoutExt + ".sav", appNameWithoutExt + ".sav")
780
781
782        #save app
783        f = open(os.path.join(appPath, appName), "wt")
784        f.write(start + n+n+t+t+ widgetParameters + n+t+t + progress + n+n+t+t + links + n + handlerFuncts)
785        f.close()
786
787        # save widget settings
788        list = {}
789        for widget in self.widgets:
790            list[widget.caption] = widget.instance.saveSettingsStr()
791
792        f = open(os.path.join(appPath, appNameWithoutExt) + ".sav", "wt")
793        cPickle.dump(list, f)
794        f.close
795       
796       
797    def dumpWidgetVariables(self):
798        for widget in self.widgets:
799            self.canvasDlg.output.write("<hr><b>%s</b><br>" % (widget.caption))
800            v = vars(widget.instance).keys()
801            v.sort()
802            for val in v:
803                self.canvasDlg.output.write("%s = %s" % (val, getattr(widget.instance, val)))
804
805    def keyReleaseEvent(self, e):
806        self.ctrlPressed = int(e.modifiers()) & Qt.ControlModifier != 0
807        e.ignore()
808
809    def keyPressEvent(self, e):
810        self.ctrlPressed = int(e.modifiers()) & Qt.ControlModifier != 0
811        if e.key() > 127 or e.key() < 0:
812            #e.ignore()
813            QWidget.keyPressEvent(self, e)
814            return
815
816        # the list could include (e.ShiftButton, "Shift") if the shift key didn't have the special meaning
817        pressed = "-".join(filter(None, [int(e.modifiers()) & x and y for x, y in [(Qt.ControlModifier, "Ctrl"), (Qt.AltModifier, "Alt")]]) + [chr(e.key())])
818        widgetToAdd = self.canvasDlg.widgetShortcuts.get(pressed)
819        if widgetToAdd:
820            self.addWidget(widgetToAdd)
821            if e.modifiers() & Qt.ShiftModifier and len(self.widgets) > 1:
822                self.addLine(self.widgets[-2], self.widgets[-1])
823        else:
824            #e.ignore()
825            QWidget.keyPressEvent(self, e)
826
827#    def resizeEvent(self, ev):
828#        QWidget.resizeEvent(self, ev)
829#        self.canvas.addRect(self.canvasView.size().width()-1, self.canvasView.size().height()-1, 1, 1)
Note: See TracBrowser for help on using the repository browser.