source: orange/Orange/OrangeWidgets/Data/OWPythonScript.py @ 11748:467f952c108d

Revision 11748:467f952c108d, 24.5 KB checked in by blaz <blaz.zupan@…>, 6 months ago (diff)

Changes in headers, widget descriptions text.

Line 
1import sys
2import os
3import code
4import keyword
5import itertools
6
7from PyQt4.QtGui import (
8    QSyntaxHighlighter, QPlainTextEdit, QTextCharFormat, QTextCursor,
9    QTextDocument, QPlainTextDocumentLayout, QBrush, QFont, QColor, QPalette,
10    QStyledItemDelegate, QStyleOptionViewItemV4, QLineEdit, QListView,
11    QSizePolicy, QAction, QMenu, QKeySequence, QSplitter, QToolButton,
12    QItemSelectionModel, QFileDialog
13)
14
15from PyQt4.QtCore import Qt, QRegExp, QByteArray
16from PyQt4.QtCore import SIGNAL
17
18from OWWidget import *
19
20from OWItemModels import PyListModel, ModelActionsWidget
21
22import OWGUI
23import Orange
24
25NAME = "Python Script"
26DESCRIPTION = "Executes a Python script."
27LONG_DESCRIPTION = ""
28ICON = "icons/PythonScript.svg"
29PRIORITY = 3150
30AUTHOR = "Miha Stajdohar"
31AUTHOR_EMAIL = "miha.stajdohar(@at@)gmail.com"
32
33INPUTS = [("in_data", Orange.data.Table, "setExampleTable", Default),
34          ("in_distance", Orange.misc.SymMatrix, "setDistanceMatrix", Default),
35          ("in_learner", Orange.core.Learner, "setLearner", Default),
36          ("in_classifier", Orange.core.Classifier, "setClassifier", Default),
37          ("in_object", object, "setObject")]
38
39OUTPUTS = [("out_data", Orange.data.Table, ),
40           ("out_distance", Orange.misc.SymMatrix, ),
41           ("out_learner", Orange.core.Learner, ),
42           ("out_classifier", Orange.core.Classifier, Dynamic),
43           ("out_object", object, Dynamic)]
44
45
46class PythonSyntaxHighlighter(QSyntaxHighlighter):
47    def __init__(self, parent=None):
48        self.keywordFormat = QTextCharFormat()
49        self.keywordFormat.setForeground(QBrush(Qt.blue))
50        self.keywordFormat.setFontWeight(QFont.Bold)
51        self.stringFormat = QTextCharFormat()
52        self.stringFormat.setForeground(QBrush(Qt.darkGreen))
53        self.defFormat = QTextCharFormat()
54        self.defFormat.setForeground(QBrush(Qt.black))
55        self.defFormat.setFontWeight(QFont.Bold)
56        self.commentFormat = QTextCharFormat()
57        self.commentFormat.setForeground(QBrush(Qt.lightGray))
58        self.decoratorFormat = QTextCharFormat()
59        self.decoratorFormat.setForeground(QBrush(Qt.darkGray))
60
61        self.keywords = list(keyword.kwlist)
62
63        self.rules = [(QRegExp(r"\b%s\b" % kwd), self.keywordFormat)
64                      for kwd in self.keywords] + \
65                     [(QRegExp(r"\bdef\s+([A-Za-z_]+[A-Za-z0-9_]+)\s*\("),
66                       self.defFormat),
67                      (QRegExp(r"\bclass\s+([A-Za-z_]+[A-Za-z0-9_]+)\s*\("),
68                       self.defFormat),
69                      (QRegExp(r"'.*'"), self.stringFormat),
70                      (QRegExp(r'".*"'), self.stringFormat),
71                      (QRegExp(r"#.*"), self.commentFormat),
72                      (QRegExp(r"@[A-Za-z_]+[A-Za-z0-9_]+"),
73                       self.decoratorFormat)]
74
75        self.multilineStart = QRegExp(r"(''')|" + r'(""")')
76        self.multilineEnd = QRegExp(r"(''')|" + r'(""")')
77
78        QSyntaxHighlighter.__init__(self, parent)
79
80    def highlightBlock(self, text):
81        for pattern, format in self.rules:
82            exp = QRegExp(pattern)
83            index = exp.indexIn(text)
84            while index >= 0:
85                length = exp.matchedLength()
86                if exp.numCaptures() > 0:
87                    self.setFormat(exp.pos(1), len(str(exp.cap(1))), format)
88                else:
89                    self.setFormat(exp.pos(0), len(str(exp.cap(0))), format)
90                index = exp.indexIn(text, index + length)
91
92        # Multi line strings
93        start = self.multilineStart
94        end = self.multilineEnd
95
96        self.setCurrentBlockState(0)
97        startIndex, skip = 0, 0
98        if self.previousBlockState() != 1:
99            startIndex, skip = start.indexIn(text), 3
100        while startIndex >= 0:
101            endIndex = end.indexIn(text, startIndex + skip)
102            if endIndex == -1:
103                self.setCurrentBlockState(1)
104                commentLen = len(text) - startIndex
105            else:
106                commentLen = endIndex - startIndex + 3
107            self.setFormat(startIndex, commentLen, self.stringFormat)
108            startIndex, skip = (start.indexIn(text,
109                                              startIndex + commentLen + 3),
110                                3)
111
112
113class PythonScriptEditor(QPlainTextEdit):
114    INDENT = 4
115
116    def lastLine(self):
117        text = str(self.toPlainText())
118        pos = self.textCursor().position()
119        index = text.rfind("\n", 0, pos)
120        text = text[index: pos].lstrip("\n")
121        return text
122
123    def keyPressEvent(self, event):
124        if event.key() == Qt.Key_Return:
125            text = self.lastLine()
126            indent = len(text) - len(text.lstrip())
127            if text.strip() == "pass" or text.strip().startswith("return "):
128                indent = max(0, indent - self.INDENT)
129            elif text.strip().endswith(":"):
130                indent += self.INDENT
131            QPlainTextEdit.keyPressEvent(self, event)
132            self.insertPlainText(" " * indent)
133        elif event.key() == Qt.Key_Tab:
134            self.insertPlainText(" " * self.INDENT)
135        elif event.key() == Qt.Key_Backspace:
136            text = self.lastLine()
137            if text and not text.strip():
138                cursor = self.textCursor()
139                for i in range(min(self.INDENT, len(text))):
140                    cursor.deletePreviousChar()
141            else:
142                QPlainTextEdit.keyPressEvent(self, event)
143
144        else:
145            QPlainTextEdit.keyPressEvent(self, event)
146
147
148class PythonConsole(QPlainTextEdit, code.InteractiveConsole):
149    def __init__(self, locals=None, parent=None):
150        QPlainTextEdit.__init__(self, parent)
151        code.InteractiveConsole.__init__(self, locals)
152        self.history, self.historyInd = [""], 0
153        self.loop = self.interact()
154        self.loop.next()
155
156    def setLocals(self, locals):
157        self.locals = locals
158
159    def interact(self, banner=None):
160        try:
161            sys.ps1
162        except AttributeError:
163            sys.ps1 = ">>> "
164        try:
165            sys.ps2
166        except AttributeError:
167            sys.ps2 = "... "
168        cprt = ('Type "help", "copyright", "credits" or "license" '
169                'for more information.')
170        if banner is None:
171            self.write("Python %s on %s\n%s\n(%s)\n" %
172                       (sys.version, sys.platform, cprt,
173                        self.__class__.__name__))
174        else:
175            self.write("%s\n" % str(banner))
176        more = 0
177        while 1:
178            try:
179                if more:
180                    prompt = sys.ps2
181                else:
182                    prompt = sys.ps1
183                self.new_prompt(prompt)
184                yield
185                try:
186                    line = self.raw_input(prompt)
187                except EOFError:
188                    self.write("\n")
189                    break
190                else:
191                    more = self.push(line)
192            except KeyboardInterrupt:
193                self.write("\nKeyboardInterrupt\n")
194                self.resetbuffer()
195                more = 0
196
197    def raw_input(self, prompt):
198        input = str(self.document().lastBlock().previous().text())
199        return input[len(prompt):]
200
201    def new_prompt(self, prompt):
202        self.write(prompt)
203        self.newPromptPos = self.textCursor().position()
204
205    def write(self, data):
206        cursor = QTextCursor(self.document())
207        cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
208        cursor.insertText(data)
209        self.setTextCursor(cursor)
210        self.ensureCursorVisible()
211
212    def push(self, line):
213        if self.history[0] != line:
214            self.history.insert(0, line)
215        self.historyInd = 0
216
217        saved = sys.stdout, sys.stderr
218        try:
219            sys.stdout, sys.stderr = self, self
220            return code.InteractiveConsole.push(self, line)
221        finally:
222            sys.stdout, sys.stderr = saved
223
224    def setLine(self, line):
225        cursor = QTextCursor(self.document())
226        cursor.movePosition(QTextCursor.End)
227        cursor.setPosition(self.newPromptPos, QTextCursor.KeepAnchor)
228        cursor.removeSelectedText()
229        cursor.insertText(line)
230        self.setTextCursor(cursor)
231
232    def keyPressEvent(self, event):
233        if event.key() == Qt.Key_Return:
234            self.write("\n")
235            self.loop.next()
236        elif event.key() == Qt.Key_Up:
237            self.historyUp()
238        elif event.key() == Qt.Key_Down:
239            self.historyDown()
240        elif event.key() == Qt.Key_Tab:
241            self.complete()
242        elif event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
243            if self.textCursor().position() > self.newPromptPos:
244                QPlainTextEdit.keyPressEvent(self, event)
245        else:
246            QPlainTextEdit.keyPressEvent(self, event)
247
248    def historyUp(self):
249        self.setLine(self.history[self.historyInd])
250        self.historyInd = min(self.historyInd + 1, len(self.history) - 1)
251
252    def historyDown(self):
253        self.setLine(self.history[self.historyInd])
254        self.historyInd = max(self.historyInd - 1, 0)
255
256    def complete(self):
257        pass
258
259    def _moveCursorToInputLine(self):
260        """
261        Move the cursor to the input line if not already there. If the cursor
262        if already in the input line (at position greater or equal to
263        `newPromptPos`) it is left unchanged, otherwise it is moved at the
264        end.
265
266        """
267        cursor = self.textCursor()
268        pos = cursor.position()
269        if pos < self.newPromptPos:
270            cursor.movePosition(QTextCursor.End)
271            self.setTextCursor(cursor)
272
273    def pasteCode(self, source):
274        """
275        Paste source code into the console.
276        """
277        self._moveCursorToInputLine()
278
279        for line in interleave(source.splitlines(), itertools.repeat("\n")):
280            if line != "\n":
281                self.insertPlainText(line)
282            else:
283                self.write("\n")
284                self.loop.next()
285
286    def insertFromMimeData(self, source):
287        """
288        Reimplemented from QPlainTextEdit.insertFromMimeData.
289        """
290        if source.hasText():
291            self.pasteCode(unicode(source.text()))
292            return
293
294
295def interleave(seq1, seq2):
296    """
297    Interleave elements of `seq2` between consecutive elements of `seq1`.
298
299        >>> list(interleave([1, 3, 5], [2, 4]))
300        [1, 2, 3, 4, 5]
301
302    """
303    iterator1, iterator2 = iter(seq1), iter(seq2)
304    leading = next(iterator1)
305    for element in iterator1:
306        yield leading
307        yield next(iterator2)
308        leading = element
309
310    yield leading
311
312
313class Script(object):
314    Modified = 1
315    MissingFromFilesystem = 2
316
317    def __init__(self, name, script, flags=0, sourceFileName=None):
318        self.name = name
319        self.script = script
320        self.flags = flags
321        self.sourceFileName = sourceFileName
322        self.modifiedScript = None
323
324
325class ScriptItemDelegate(QStyledItemDelegate):
326    def __init__(self, parent):
327        QStyledItemDelegate.__init__(self, parent)
328
329    def displayText(self, variant, locale):
330        script = variant.toPyObject()
331        if script.flags & Script.Modified:
332            return QString("*" + script.name)
333        else:
334            return QString(script.name)
335
336    def paint(self, painter, option, index):
337        script = index.data(Qt.DisplayRole).toPyObject()
338
339        if script.flags & Script.Modified:
340            option = QStyleOptionViewItemV4(option)
341            option.palette.setColor(QPalette.Text, QColor(Qt.red))
342            option.palette.setColor(QPalette.Highlight, QColor(Qt.darkRed))
343        QStyledItemDelegate.paint(self, painter, option, index)
344
345    def createEditor(self, parent, option, index):
346        return QLineEdit(parent)
347
348    def setEditorData(self, editor, index):
349        script = index.data(Qt.DisplayRole).toPyObject()
350        editor.setText(script.name)
351
352    def setModelData(self, editor, model, index):
353        model[index.row()].name = str(editor.text())
354
355
356class OWPythonScript(OWWidget):
357
358    settingsList = ["codeFile", "libraryListSource", "currentScriptIndex",
359                    "splitterState", "auto_execute"]
360
361    def __init__(self, parent=None, signalManager=None):
362        OWWidget.__init__(self, parent, signalManager, 'Python Script')
363
364        self.inputs = [("in_data", Orange.data.Table, self.setExampleTable,
365                        Default),
366                       ("in_distance", Orange.misc.SymMatrix,
367                        self.setDistanceMatrix, Default),
368                       ("in_learner", Orange.core.Learner, self.setLearner,
369                        Default),
370                       ("in_classifier", Orange.core.Classifier,
371                        self.setClassifier, Default),
372                       ("in_object", object, self.setObject)]
373
374        self.outputs = [("out_data", Orange.data.Table),
375                        ("out_distance", Orange.misc.SymMatrix),
376                        ("out_learner", Orange.core.Learner),
377                        ("out_classifier", Orange.core.Classifier, Dynamic),
378                        ("out_object", object, Dynamic)]
379
380        self.in_data = None
381        self.in_distance = None
382        self.in_learner = None
383        self.in_classifier = None
384        self.in_object = None
385        self.auto_execute = False
386
387        self.codeFile = ''
388        self.libraryListSource = [Script("Hello world",
389                                         "print 'Hello world'\n")]
390        self.currentScriptIndex = 0
391        self.splitterState = None
392        self.loadSettings()
393
394        for s in self.libraryListSource:
395            s.flags = 0
396
397        self._cachedDocuments = {}
398
399        self.infoBox = OWGUI.widgetBox(self.controlArea, 'Info')
400        OWGUI.label(
401            self.infoBox, self,
402            "<p>Execute python script.</p><p>Input variables:<ul><li> " + \
403            "<li>".join(t[0] for t in self.inputs) + \
404            "</ul></p><p>Output variables:<ul><li>" + \
405            "<li>".join(t[0] for t in self.outputs) + \
406            "</ul></p>"
407        )
408
409        self.libraryList = PyListModel([], self, flags=Qt.ItemIsSelectable | \
410                                       Qt.ItemIsEnabled | Qt.ItemIsEditable)
411
412        self.libraryList.wrap(self.libraryListSource)
413
414        self.controlBox = OWGUI.widgetBox(self.controlArea, 'Library')
415        self.controlBox.layout().setSpacing(1)
416
417        self.libraryView = QListView()
418        self.libraryView.setEditTriggers(QListView.DoubleClicked | \
419                                         QListView.EditKeyPressed)
420        self.libraryView.setSizePolicy(QSizePolicy.Ignored,
421                                       QSizePolicy.Preferred)
422        self.libraryView.setItemDelegate(ScriptItemDelegate(self))
423        self.libraryView.setModel(self.libraryList)
424
425        self.connect(self.libraryView.selectionModel(),
426                     SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),
427                     self.onSelectedScriptChanged)
428        self.controlBox.layout().addWidget(self.libraryView)
429
430        w = ModelActionsWidget()
431
432        self.addNewScriptAction = action = QAction("+", self)
433        action.setToolTip("Add a new script to the library")
434        self.connect(action, SIGNAL("triggered()"), self.onAddScript)
435        w.addAction(action)
436
437        self.removeAction = action = QAction("-", self)
438        action.setToolTip("Remove script from library")
439        self.connect(action, SIGNAL("triggered()"), self.onRemoveScript)
440        w.addAction(action)
441
442        action = QAction("Update", self)
443        action.setToolTip("Save changes in the editor to library")
444        action.setShortcut(QKeySequence(QKeySequence.Save))
445        self.connect(action, SIGNAL("triggered()"),
446                     self.commitChangesToLibrary)
447        w.addAction(action)
448
449        action = QAction("More", self)
450        action.pyqtConfigure(toolTip="More actions")
451
452        new_from_file = QAction("Import a script from a file", self)
453        save_to_file = QAction("Save selected script to a file", self)
454        save_to_file.setShortcut(QKeySequence(QKeySequence.SaveAs))
455
456        self.connect(new_from_file, SIGNAL("triggered()"),
457                     self.onAddScriptFromFile)
458        self.connect(save_to_file, SIGNAL("triggered()"), self.saveScript)
459
460        menu = QMenu(w)
461        menu.addAction(new_from_file)
462        menu.addAction(save_to_file)
463        action.setMenu(menu)
464        button = w.addAction(action)
465        button.setPopupMode(QToolButton.InstantPopup)
466
467        w.layout().setSpacing(1)
468
469        self.controlBox.layout().addWidget(w)
470
471        self.runBox = OWGUI.widgetBox(self.controlArea, 'Run')
472        OWGUI.button(self.runBox, self, "Execute", callback=self.execute)
473        OWGUI.checkBox(self.runBox, self, "auto_execute", "Auto execute",
474                       tooltip=("Run the script automatically whenever "
475                                "the inputs to the widget change."))
476
477        self.splitCanvas = QSplitter(Qt.Vertical, self.mainArea)
478        self.mainArea.layout().addWidget(self.splitCanvas)
479
480        self.defaultFont = defaultFont = \
481            "Monaco" if sys.platform == "darwin" else "Courier"
482
483        self.textBox = OWGUI.widgetBox(self, 'Python script')
484        self.splitCanvas.addWidget(self.textBox)
485        self.text = PythonScriptEditor(self)
486        self.textBox.layout().addWidget(self.text)
487
488        self.textBox.setAlignment(Qt.AlignVCenter)
489        self.text.setTabStopWidth(4)
490
491        self.connect(self.text, SIGNAL("modificationChanged(bool)"),
492                     self.onModificationChanged)
493
494        self.saveAction = action = QAction("&Save", self.text)
495        action.setToolTip("Save script to file")
496        action.setShortcut(QKeySequence(QKeySequence.Save))
497        action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
498        self.connect(action, SIGNAL("triggered()"), self.saveScript)
499
500        self.consoleBox = OWGUI.widgetBox(self, 'Console')
501        self.splitCanvas.addWidget(self.consoleBox)
502        self.console = PythonConsole(self.__dict__, self)
503        self.consoleBox.layout().addWidget(self.console)
504        self.console.document().setDefaultFont(QFont(defaultFont))
505        self.consoleBox.setAlignment(Qt.AlignBottom)
506        self.console.setTabStopWidth(4)
507
508        self.openScript(self.codeFile)
509        try:
510            self.libraryView.selectionModel().select(
511                self.libraryList.index(self.currentScriptIndex),
512                QItemSelectionModel.ClearAndSelect
513            )
514        except Exception:
515            pass
516
517        self.splitCanvas.setSizes([2, 1])
518        if self.splitterState is not None:
519            self.splitCanvas.restoreState(QByteArray(self.splitterState))
520
521        self.connect(self.splitCanvas, SIGNAL("splitterMoved(int, int)"),
522                     self.onSpliterMoved)
523        self.controlArea.layout().addStretch(1)
524        self.resize(800, 600)
525
526    def setExampleTable(self, et):
527        self.in_data = et
528
529    def setDistanceMatrix(self, dm):
530        self.in_distance = dm
531
532    def setLearner(self, learner):
533        self.in_learner = learner
534
535    def setClassifier(self, classifier):
536        self.in_classifier = classifier
537
538    def setObject(self, obj):
539        self.in_object = obj
540
541    def handleNewSignals(self):
542        if self.auto_execute:
543            self.execute()
544
545    def selectedScriptIndex(self):
546        rows = self.libraryView.selectionModel().selectedRows()
547        if rows:
548            return  [i.row() for i in rows][0]
549        else:
550            return None
551
552    def setSelectedScript(self, index):
553        selection = self.libraryView.selectionModel()
554        selection.select(self.libraryList.index(index),
555                         QItemSelectionModel.ClearAndSelect)
556
557    def onAddScript(self, *args):
558        self.libraryList.append(Script("New script", "", 0))
559        self.setSelectedScript(len(self.libraryList) - 1)
560
561    def onAddScriptFromFile(self, *args):
562        filename = QFileDialog.getOpenFileName(
563            self, 'Open Python Script',
564            self.codeFile,
565            'Python files (*.py)\nAll files(*.*)'
566        )
567
568        filename = unicode(filename)
569        if filename:
570            name = os.path.basename(filename)
571            self.libraryList.append(Script(name, open(filename, "rb").read(),
572                                           0, filename))
573            self.setSelectedScript(len(self.libraryList) - 1)
574
575    def onRemoveScript(self, *args):
576        index = self.selectedScriptIndex()
577        if index is not None:
578            del self.libraryList[index]
579
580            self.libraryView.selectionModel().select(
581                self.libraryList.index(max(index - 1, 0)),
582                QItemSelectionModel.ClearAndSelect
583            )
584
585    def onSaveScriptToFile(self, *args):
586        index = self.selectedScriptIndex()
587        if index is not None:
588            self.saveScript()
589
590    def onSelectedScriptChanged(self, selected, deselected):
591        index = [i.row() for i in selected.indexes()]
592        if index:
593            current = index[0]
594            if current >= len(self.libraryList):
595                self.addNewScriptAction.trigger()
596                return
597
598            self.text.setDocument(self.documentForScript(current))
599            self.currentScriptIndex = current
600
601    def documentForScript(self, script=0):
602        if type(script) != Script:
603            script = self.libraryList[script]
604
605        if script not in self._cachedDocuments:
606            doc = QTextDocument(self)
607            doc.setDocumentLayout(QPlainTextDocumentLayout(doc))
608            doc.setPlainText(script.script)
609            doc.setDefaultFont(QFont(self.defaultFont))
610            doc.highlighter = PythonSyntaxHighlighter(doc)
611            self.connect(doc, SIGNAL("modificationChanged(bool)"),
612                         self.onModificationChanged)
613            doc.setModified(False)
614            self._cachedDocuments[script] = doc
615        return self._cachedDocuments[script]
616
617    def commitChangesToLibrary(self, *args):
618        index = self.selectedScriptIndex()
619        if index is not None:
620            self.libraryList[index].script = self.text.toPlainText()
621            self.text.document().setModified(False)
622            self.libraryList.emitDataChanged(index)
623
624    def onModificationChanged(self, modified):
625        index = self.selectedScriptIndex()
626        if index is not None:
627            self.libraryList[index].flags = Script.Modified if modified else 0
628            self.libraryList.emitDataChanged(index)
629
630    def onSpliterMoved(self, pos, ind):
631        self.splitterState = str(self.splitCanvas.saveState())
632
633    def updateSelecetdScriptState(self):
634        index = self.selectedScriptIndex()
635        if index is not None:
636            script = self.libraryList[index]
637            self.libraryList[index] = Script(script.name,
638                                             self.text.toPlainText(),
639                                             0)
640
641    def openScript(self, filename=None):
642        if filename == None:
643            filename = QFileDialog.getOpenFileName(
644                self, 'Open Python Script',
645                self.codeFile,
646                'Python files (*.py)\nAll files(*.*)'
647            )
648
649            filename = unicode(filename)
650            self.codeFile = filename
651        else:
652            self.codeFile = filename
653
654        if self.codeFile == "":
655            return
656
657        self.error(0)
658        try:
659            f = open(self.codeFile, 'r')
660        except (IOError, OSError), ex:
661            self.text.setPlainText("")
662            return
663
664        self.text.setPlainText(f.read())
665        f.close()
666
667    def saveScript(self):
668        index = self.selectedScriptIndex()
669        if index is not None:
670            script = self.libraryList[index]
671            filename = script.sourceFileName or self.codeFile
672        else:
673            filename = self.codeFile
674        filename = QFileDialog.getSaveFileName(
675            self, 'Save Python Script',
676            filename,
677            'Python files (*.py)\nAll files(*.*)'
678        )
679
680        self.codeFile = unicode(filename)
681
682        if self.codeFile:
683            fn = ""
684            head, tail = os.path.splitext(self.codeFile)
685            if not tail:
686                fn = head + ".py"
687            else:
688                fn = self.codeFile
689
690            f = open(fn, 'w')
691            f.write(self.text.toPlainText())
692            f.close()
693
694    def execute(self):
695        self._script = str(self.text.toPlainText())
696        self.console.write("\nRunning script:\n")
697        self.console.push("exec(_script)")
698        self.console.new_prompt(sys.ps1)
699        for out in self.outputs:
700            signal = out[0]
701            self.send(signal, getattr(self, signal, None))
702
703
704if __name__ == "__main__":
705    appl = QApplication(sys.argv)
706    ow = OWPythonScript()
707    ow.show()
708    appl.exec_()
709    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.