source: orange/Orange/OrangeCanvas/orngDoc.py @ 10831:4d7aed8471ea

Revision 10831:4d7aed8471ea, 34.2 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Changed the order by which widgets are removed from the schema in
'clear' method.

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       
258        if saveTempDoc:
259            self.saveTempDoc()
260           
261        qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
262
263    # remove line, connecting two widgets
264    def removeLine(self, outWidget, inWidget, saveTempDoc=True):
265        """ Remove the line connecting two widgets
266        """
267        #print "<extra> orngDoc.py - removeLine() - ", outWidget, inWidget
268        line = self.getLine(outWidget, inWidget)
269        if line:
270            self.removeLine1(line, saveTempDoc)
271
272    # add new widget
273    def addWidget(self, widgetInfo, x= -1, y=-1, caption = "", widgetSettings = {}, saveTempDoc = True):
274        qApp.setOverrideCursor(Qt.WaitCursor)
275        try:
276            newwidget = orngCanvasItems.CanvasWidget(self.signalManager, self.canvas, self.canvasView, widgetInfo, self.canvasDlg.defaultPic, self.canvasDlg, widgetSettings)
277        except:
278            type, val, traceback = sys.exc_info()
279            sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that it doesn't crash canvas
280            qApp.restoreOverrideCursor()
281            return None
282
283        if x==-1 or y==-1:
284            if self.widgets != []:
285                x = self.widgets[-1].x() + 110
286                y = self.widgets[-1].y()
287            else:
288                x = 30
289                y = 150
290        newwidget.setCoords(x, y)
291        # move the widget to a valid position if necessary
292        invalidPosition = (self.canvasView.findItemTypeCount(self.canvas.collidingItems(newwidget), orngCanvasItems.CanvasWidget) > 0)
293        if invalidPosition:
294            for r in range(20, 200, 20):
295                for fi in [90, -90, 180, 0, 45, -45, 135, -135]:
296                    xOff = r * math.cos(math.radians(fi))
297                    yOff = r * math.sin(math.radians(fi))
298                    rect = QRectF(x+xOff, y+yOff, 48, 48)
299                    invalidPosition = self.canvasView.findItemTypeCount(self.canvas.items(rect), orngCanvasItems.CanvasWidget) > 0
300                    if not invalidPosition:
301                        newwidget.setCoords(x+xOff, y+yOff)
302                        break
303                if not invalidPosition:
304                    break
305           
306        #self.canvasView.ensureVisible(newwidget)
307
308        if caption == "":
309            caption = newwidget.caption
310
311        if self.getWidgetByCaption(caption):
312            i = 2
313            while self.getWidgetByCaption(caption + " (" + str(i) + ")"):
314                i+=1
315            caption = caption + " (" + str(i) + ")"
316        newwidget.updateText(caption)
317        newwidget.instance.setWindowTitle(caption)
318
319        self.widgets.append(newwidget)
320        if saveTempDoc:
321            self.saveTempDoc()
322        self.canvas.update()
323
324        # show the widget and activate the settings
325        try:
326            self.signalManager.addWidget(newwidget.instance)
327            newwidget.show()
328            newwidget.updateTooltip()
329            newwidget.setProcessing(1)
330            if self.canvasDlg.settings["saveWidgetsPosition"]:
331                newwidget.instance.restoreWidgetPosition()
332            newwidget.setProcessing(0)
333            orngHistory.logAddWidget(self.schemaID, id(newwidget), (newwidget.widgetInfo.category, newwidget.widgetInfo.name), newwidget.x(), newwidget.y())
334        except:
335            type, val, traceback = sys.exc_info()
336            sys.excepthook(type, val, traceback)  # we pretend that we handled the exception, so that it doesn't crash canvas
337
338        qApp.restoreOverrideCursor()
339        self.update_guide()
340        return newwidget
341
342    # remove widget
343    def removeWidget(self, widget, saveTempDoc = True):
344        if not widget:
345            return
346       
347        #with self.signalManager.freeze():
348        while widget.inLines != []: self.removeLine1(widget.inLines[0])
349        while widget.outLines != []:  self.removeLine1(widget.outLines[0])
350   
351        self.signalManager.removeWidget(widget.instance)
352           
353        self.widgets.remove(widget)
354        widget.remove()
355       
356        if saveTempDoc:
357            self.saveTempDoc()
358           
359        qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
360       
361        self.update_guide()
362        orngHistory.logRemoveWidget(self.schemaID, id(widget), (widget.widgetInfo.category, widget.widgetInfo.name))
363
364    def clear(self):
365        self.canvasDlg.setCaption()
366
367        # remove widgets in the reverse order as listed in signalManager
368        # (it keeps them ordered topologically)
369        widgets = sorted(self.widgets,
370                key=lambda w: self.signalManager.widgets.index(w.instance),
371                reverse=True)
372
373        for widget in widgets:
374            self.removeWidget(widget, saveTempDoc = False)
375        self.canvas.update()
376        self.schemaPath = self.canvasDlg.settings["saveSchemaDir"]
377        self.schemaName = ""
378        self.loadedSettingsDict = {}
379        self.saveTempDoc()
380
381    def enableAllLines(self):
382        with self.signalManager.freeze():
383            for line in self.lines:
384                self.signalManager.setLinkEnabled(line.outWidget.instance, line.inWidget.instance, 1)
385                line.update()
386        self.canvas.update()
387
388    def disableAllLines(self):
389        for line in self.lines:
390            self.signalManager.setLinkEnabled(line.outWidget.instance, line.inWidget.instance, 0)
391            line.update()
392        self.canvas.update()
393       
394    def setFreeze(self, bool):
395        self.signalManager.setFreeze(self.signalManager.freezing + (1 if bool else -1), None)
396
397    # return a new widget instance of a widget with filename "widgetName"
398    def addWidgetByFileName(self, widgetFileName, x, y, caption, widgetSettings = {}, saveTempDoc = True):
399        for category in self.canvasDlg.widgetRegistry.keys():
400            for name, widget in self.canvasDlg.widgetRegistry[category].items():
401                if widget.fileName == widgetFileName: 
402                    return self.addWidget(widget, x, y, caption, widgetSettings, saveTempDoc)
403        return None
404
405    # return the widget instance that has caption "widgetName"
406    def getWidgetByCaption(self, widgetName):
407        for widget in self.widgets:
408            if (widget.caption == widgetName):
409                return widget
410        return None
411
412    def getWidgetCaption(self, widgetInstance):
413        for widget in self.widgets:
414            if widget.instance == widgetInstance:
415                return widget.caption
416        print "Error. Invalid widget instance : ", widgetInstance
417        return ""
418
419
420    # get line from outWidget to inWidget
421    def getLine(self, outWidget, inWidget):
422        for line in self.lines:
423            if line.outWidget == outWidget and line.inWidget == inWidget:
424                return line
425        return None
426
427
428    # find orngCanvasItems.CanvasWidget from widget instance
429    def findWidgetFromInstance(self, widgetInstance):
430        for widget in self.widgets:
431            if widget.instance == widgetInstance:
432                return widget
433        return None
434
435
436    # ###########################################
437    # SAVING, LOADING, ....
438    # ###########################################
439    def reportAll(self):
440        for widget in self.widgets:
441            widget = widget.instance
442            if hasattr(widget, "sendReport"):
443                widget.reportAndFinish()
444           
445    def saveDocument(self):
446        if self.schemaName == "":
447            self.saveDocumentAs()
448        else:
449            self.save()
450            self.setSchemaModified(False)
451
452    def saveDocumentAs(self):
453        name = QFileDialog.getSaveFileName(self, "Save Orange Schema", os.path.join(self.schemaPath, self.schemaName or "Untitled.ows"), "Orange Widget Schema (*.ows)")
454        name = unicode(name)
455        if os.path.splitext(name)[0] == "":
456            return
457        if os.path.splitext(name)[1].lower() != ".ows":
458            name = os.path.splitext(name)[0] + ".ows"
459        self.save(name)
460        self.setSchemaModified(False)
461       
462    # save the file
463    def save(self, filename=None):
464        if filename is None:
465            filename = os.path.join(self.schemaPath, self.schemaName)
466           
467        # create xml document
468        doc = Document()
469        schema = doc.createElement("schema")
470        widgets = doc.createElement("widgets")
471        lines = doc.createElement("channels")
472        settings = doc.createElement("settings")
473        doc.appendChild(schema)
474        schema.appendChild(widgets)
475        schema.appendChild(lines)
476        schema.appendChild(settings)
477        settingsDict = {}
478
479        #save widgets
480        for widget in self.widgets:
481            temp = doc.createElement("widget")
482            temp.setAttribute("xPos", str(int(widget.x())) )
483            temp.setAttribute("yPos", str(int(widget.y())) )
484            temp.setAttribute("caption", widget.caption)
485            temp.setAttribute("widgetName", widget.widgetInfo.fileName)
486            settingsDict[widget.caption] = widget.instance.saveSettingsStr()
487            widgets.appendChild(temp)
488
489        #save connections
490        for line in self.lines:
491            temp = doc.createElement("channel")
492            temp.setAttribute("outWidgetCaption", line.outWidget.caption)
493            temp.setAttribute("inWidgetCaption", line.inWidget.caption)
494            temp.setAttribute("enabled", str(line.getEnabled()))
495            temp.setAttribute("signals", str(line.getSignals()))
496            lines.appendChild(temp)
497
498        settings.setAttribute("settingsDictionary", str(settingsDict))
499
500        xmlText = doc.toprettyxml()
501
502        file = open(filename, "wt")
503        file.write(xmlText)
504        file.close()
505        doc.unlink()
506
507        if os.path.splitext(filename)[1].lower() == ".ows":
508            (self.schemaPath, self.schemaName) = os.path.split(filename)
509            self.canvasDlg.settings["saveSchemaDir"] = self.schemaPath
510            self.canvasDlg.addToRecentMenu(filename)
511            self.canvasDlg.setCaption(self.schemaName)
512
513    def saveBeforeClose(self):
514        """ Call to ask the user to save the schema before it is closed.
515        Return True if save was successful or user did not want to save,
516        else return False (user canceled the operation).
517         
518        """
519        newSettings = self.isSchemaChanged()
520
521        self.synchronizeContexts()
522        if self.widgets != []:
523            self.save(os.path.join(self.canvasDlg.canvasSettingsDir, "lastSchema.tmp"))
524
525        if self.canvasDlg.settings["dontAskBeforeClose"]:
526            if newSettings and self.schemaName != "":
527                self.save()
528            return True
529               
530        elif newSettings:
531            res = QMessageBox.question(self, 'Orange Canvas', 'Do you wish to save the schema?', QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
532            if res == QMessageBox.Yes:
533                self.saveDocument()
534                return True
535            elif res == QMessageBox.No:
536                return True
537            else:     
538                # User pressed cancel - we don't want to close the document
539                return False
540        else:
541            return True
542
543    # load a scheme with name "filename"
544    def loadDocument(self, filename, caption = None, freeze = 0):
545        self.clear()
546       
547        if not os.path.exists(filename):
548            if os.path.splitext(filename)[1].lower() != ".tmp":
549                QMessageBox.critical(self, 'Orange Canvas', 'Unable to locate file "'+ filename + '"',  QMessageBox.Ok)
550            return
551
552        # set cursor
553        qApp.setOverrideCursor(Qt.WaitCursor)
554        failureText = ""
555       
556        if os.path.splitext(filename)[1].lower() == ".ows":
557            self.schemaPath, self.schemaName = os.path.split(filename)
558            self.canvasDlg.setCaption(caption or self.schemaName)
559        self.signalManager.freeze().push()
560        try:
561            #load the data ...
562            doc = parse(filename)
563            schema = doc.firstChild
564            widgets = schema.getElementsByTagName("widgets")[0]
565            lines = schema.getElementsByTagName("channels")[0]
566            settings = schema.getElementsByTagName("settings")
567            settingsDict = eval(str(settings[0].getAttribute("settingsDictionary")))
568            self.loadedSettingsDict = settingsDict
569             
570            # read widgets
571            loadedOk = 1
572            for widget in widgets.getElementsByTagName("widget"):
573                name = widget.getAttribute("widgetName")
574                settings = cPickle.loads(settingsDict[widget.getAttribute("caption")])
575                tempWidget = self.addWidgetByFileName(name, int(widget.getAttribute("xPos")), int(widget.getAttribute("yPos")), widget.getAttribute("caption"), settings, saveTempDoc = False)
576                if not tempWidget:
577                    #QMessageBox.information(self, 'Orange Canvas','Unable to create instance of widget \"'+ name + '\"',  QMessageBox.Ok + QMessageBox.Default)
578                    failureText += '<nobr>Unable to create instance of a widget <b>%s</b></nobr><br>' %(name)
579                    loadedOk = 0
580                qApp.processEvents()
581
582            #read lines
583            lineList = lines.getElementsByTagName("channel")
584            for line in lineList:
585                inCaption = line.getAttribute("inWidgetCaption")
586                outCaption = line.getAttribute("outWidgetCaption")
587                if freeze: enabled = 0
588                else:      enabled = int(line.getAttribute("enabled"))
589                signals = line.getAttribute("signals")
590                inWidget = self.getWidgetByCaption(inCaption)
591                outWidget = self.getWidgetByCaption(outCaption)
592                if inWidget == None or outWidget == None:
593                    failureText += "<nobr>Failed to create a signal line between widgets <b>%s</b> and <b>%s</b></nobr><br>" % (outCaption, inCaption)
594                    loadedOk = 0
595                    continue
596
597                signalList = eval(signals)
598                for (outName, inName) in signalList:
599                    self.addLink(outWidget, inWidget, outName, inName, enabled, saveTempDoc=False)
600                #qApp.processEvents()
601        finally:
602            qApp.restoreOverrideCursor()
603            self.signalManager.freeze().pop()
604
605        for widget in self.widgets:
606            widget.updateTooltip()
607        self.canvas.update()
608
609        self.saveTempDoc()
610
611        if not loadedOk:
612            QMessageBox.information(self, 'Schema Loading Failed', 'The following errors occured while loading the schema: <br><br>' + failureText,  QMessageBox.Ok + QMessageBox.Default)
613
614        if self.widgets:
615            self.signalManager.processNewSignals(self.widgets[0].instance)
616           
617        # Store the loaded settings dict again
618        self.loadedSettingsDict = dict((widget.caption, widget.instance.saveSettingsStr()) for widget in self.widgets)
619        self.canvasDlg.setWindowModified(False)
620
621        # do we want to restore last position and size of the widget
622        if self.canvasDlg.settings["saveWidgetsPosition"]:
623            for widget in self.widgets:
624                widget.instance.restoreWidgetStatus()
625           
626       
627
628    # save document as application
629    def saveDocumentAsApp(self, asTabs = 1):
630        # get filename
631        extension = sys.platform == "win32" and ".pyw" or ".py"
632        appName = (os.path.splitext(self.schemaName)[0] or "Schema") + extension
633        appPath = os.path.exists(self.canvasDlg.settings["saveApplicationDir"]) and self.canvasDlg.settings["saveApplicationDir"] or self.schemaPath
634        qname = QFileDialog.getSaveFileName(self, "Save Orange Schema as Application", os.path.join(appPath, appName) , "Orange Scripts (*%s)" % extension)
635        if qname.isEmpty(): return
636        qname = unicode(qname)
637        (appPath, appName) = os.path.split(qname)
638        appNameWithoutExt = os.path.splitext(appName)[0]
639        if os.path.splitext(appName)[1].lower() not in [".py", ".pyw"]: appName = appNameWithoutExt + extension
640        self.canvasDlg.settings["saveApplicationDir"] = appPath
641
642        saveDlg = saveApplicationDlg(None)
643
644        # add widget captions
645        for instance in self.signalManager.widgets:
646            widget = None
647            for i in range(len(self.widgets)):
648                if self.widgets[i].instance == instance: saveDlg.insertWidgetName(self.widgets[i].caption)
649
650        if saveDlg.exec_() == QDialog.Rejected:
651            return
652
653        #format string with file content
654        t = "    "  # instead of tab
655        n = "\n"
656
657        start = """#!/usr/bin/env python
658
659#This file is automatically created by Orange Canvas and containing an Orange schema
660
661import orngEnviron
662import orngDebugging
663import sys, os, cPickle, orange, orngSignalManager, OWGUI
664from OWBaseWidget import *
665
666class GUIApplication(OWBaseWidget):
667    def __init__(self,parent=None):
668        self.signalManager = orngSignalManager.SignalManager()
669        OWBaseWidget.__init__(self, title = '%s', signalManager = self.signalManager)
670        self.widgets = {}
671        self.loadSettings()
672        """ % (appNameWithoutExt)
673
674        if asTabs == 1:
675            start += """
676        self.tabs = QTabWidget(self)
677        self.setLayout(QVBoxLayout())
678        self.layout().addWidget(self.tabs)
679        self.resize(800,600)"""
680        else:
681            start += """
682        self.setLayout(QVBoxLayout())
683        self.box = OWGUI.widgetBox(self, 'Widgets')"""
684
685
686        links = "# add widget signals\n"+t+t + "self.signalManager.setFreeze(1)\n" +t+t
687        widgetParameters = ""
688
689        # gui for shown widgets
690        for widgetName in saveDlg.shownWidgetList:    # + saveDlg.hiddenWidgetList
691            if widgetName != "[Separator]":
692                widget = None
693                for i in range(len(self.widgets)):
694                    if self.widgets[i].caption == widgetName: widget = self.widgets[i]
695
696                shown = widgetName in saveDlg.shownWidgetList
697                widgetParameters += "self.createWidget('%s', '%s', '%s', %d, self.signalManager)\n" % ("%s.%s" % (widget.widgetInfo.module, widget.widgetInfo.fileName) if widget.widgetInfo.module else widget.widgetInfo.fileName, widget.widgetInfo.icon, widget.caption, shown) +t+t
698            else:
699                if not asTabs:
700                    widgetParameters += "self.box.layout().addSpacing(10)\n" +t+t
701
702        for line in self.lines:
703            if not line.getEnabled(): continue
704            for (outName, inName) in line.getSignals():
705                links += "self.signalManager.addLink( self.widgets['" + line.outWidget.caption+ "'], self.widgets['" + line.inWidget.caption+ "'], '" + outName + "', '" + inName + "', 1)\n" +t+t
706
707        links += "self.signalManager.setFreeze(0)\n" +t+t
708        if not asTabs:
709            widgetParameters += """
710        box2 = OWGUI.widgetBox(self, 1)
711        exitButton = OWGUI.button(box2, self, "Exit", callback = self.accept)
712        self.layout().addStretch(100)"""
713
714        if asTabs:
715            guiText = "OWGUI.createTabPage(self.tabs, caption, widget)"
716        else:
717            guiText = "OWGUI.button(self.box, self, caption, callback = widget.reshow)"
718
719        progress = """
720        statusBar = QStatusBar(self)
721        self.layout().addWidget(statusBar)
722        self.caption = QLabel('', statusBar)
723        self.caption.setMaximumWidth(230)
724        self.progress = QProgressBar(statusBar)
725        self.progress.setMaximumWidth(100)
726        self.status = QLabel("", statusBar)
727        self.status.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
728        statusBar.addWidget(self.progress)
729        statusBar.addWidget(self.caption)
730        statusBar.addWidget(self.status)"""
731
732        handlerFuncts = """
733    def createWidget(self, fname, iconName, caption, shown, signalManager):
734        widgetSettings = cPickle.loads(self.strSettings[caption])
735        m = __import__(fname)
736        widget = m.__dict__[fname].__new__(m.__dict__[fname], _settingsFromSchema = widgetSettings)
737        widget.__init__(signalManager=signalManager)
738        widget.setEventHandler(self.eventHandler)
739        widget.setProgressBarHandler(self.progressHandler)
740        widget.setWidgetIcon(iconName)
741        widget.setWindowTitle(caption)
742        self.signalManager.addWidget(widget)
743        self.widgets[caption] = widget
744        if shown: %s
745        for dlg in getattr(widget, "wdChildDialogs", []):
746            dlg.setEventHandler(self.eventHandler)
747            dlg.setProgressBarHandler(self.progressHandler)
748
749    def eventHandler(self, text, eventVerbosity = 1):
750        if orngDebugging.orngVerbosity >= eventVerbosity:
751            self.status.setText(text)
752
753    def progressHandler(self, widget, val):
754        if val < 0:
755            self.caption.setText("<nobr>Processing: <b>" + str(widget.captionTitle) + "</b></nobr>")
756            self.progress.setValue(0)
757        elif val >100:
758            self.caption.setText("")
759            self.progress.reset()
760        else:
761            self.progress.setValue(val)
762            self.update()
763
764    def loadSettings(self):
765        try:
766            file = open("%s", "r")
767            self.strSettings = cPickle.load(file)
768            file.close()
769
770        except:
771            print "unable to load settings"
772            pass
773
774    def closeEvent(self, ev):
775        OWBaseWidget.closeEvent(self, ev)
776        if orngDebugging.orngDebuggingEnabled: return
777        strSettings = {}
778        for (name, widget) in self.widgets.items():
779            widget.synchronizeContexts()
780            strSettings[name] = widget.saveSettingsStr()
781            widget.close()
782        file = open("%s", "w")
783        cPickle.dump(strSettings, file)
784        file.close()
785
786if __name__ == "__main__":
787    application = QApplication(sys.argv)
788    ow = GUIApplication()
789    ow.show()
790    # comment the next line if in debugging mode and are interested only in output text in 'signalManagerOutput.txt' file
791    application.exec_()
792        """ % (guiText, appNameWithoutExt + ".sav", appNameWithoutExt + ".sav")
793
794
795        #save app
796        f = open(os.path.join(appPath, appName), "wt")
797        f.write(start + n+n+t+t+ widgetParameters + n+t+t + progress + n+n+t+t + links + n + handlerFuncts)
798        f.close()
799
800        # save widget settings
801        list = {}
802        for widget in self.widgets:
803            list[widget.caption] = widget.instance.saveSettingsStr()
804
805        f = open(os.path.join(appPath, appNameWithoutExt) + ".sav", "wt")
806        cPickle.dump(list, f)
807        f.close
808       
809       
810    def dumpWidgetVariables(self):
811        for widget in self.widgets:
812            self.canvasDlg.output.write("<hr><b>%s</b><br>" % (widget.caption))
813            v = vars(widget.instance).keys()
814            v.sort()
815            for val in v:
816                self.canvasDlg.output.write("%s = %s" % (val, getattr(widget.instance, val)))
817
818    def keyReleaseEvent(self, e):
819        self.ctrlPressed = int(e.modifiers()) & Qt.ControlModifier != 0
820        e.ignore()
821
822    def keyPressEvent(self, e):
823        self.ctrlPressed = int(e.modifiers()) & Qt.ControlModifier != 0
824        if e.key() > 127 or e.key() < 0:
825            #e.ignore()
826            QWidget.keyPressEvent(self, e)
827            return
828
829        # the list could include (e.ShiftButton, "Shift") if the shift key didn't have the special meaning
830        pressed = "-".join(filter(None, [int(e.modifiers()) & x and y for x, y in [(Qt.ControlModifier, "Ctrl"), (Qt.AltModifier, "Alt")]]) + [chr(e.key())])
831        widgetToAdd = self.canvasDlg.widgetShortcuts.get(pressed)
832        if widgetToAdd:
833            self.addWidget(widgetToAdd)
834            if e.modifiers() & Qt.ShiftModifier and len(self.widgets) > 1:
835                self.addLine(self.widgets[-2], self.widgets[-1])
836        else:
837            #e.ignore()
838            QWidget.keyPressEvent(self, e)
839
840#    def resizeEvent(self, ev):
841#        QWidget.resizeEvent(self, ev)
842#        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.