source: orange/Orange/OrangeWidgets/Data/OWPythonScript.py @ 11780:df916a1a0cb1

Revision 11780:df916a1a0cb1, 24.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 4 months ago (diff)

Replaced hypens with unicode minus character in GUI button labels.

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