source: orange/orange/OrangeCanvas/orngDoc.py @ 7173:33f8a991a4fb

Revision 7173:33f8a991a4fb, 32.1 KB checked in by markotoplak, 3 years ago (diff)

Fixed "guide text" so that it does not crash on Windows.

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