source: orange/Orange/OrangeCanvas/orngDoc.py @ 10827:8527a7511c4f

Revision 10827:8527a7511c4f, 34.0 KB checked in by mitar, 2 years ago (diff)

Support for widgets loading from modules, registered through entry point.

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