source: orange/orange/OrangeCanvas/orngDoc.py @ 7721:bc444fa6611f

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