source: orange/Orange/OrangeWidgets/Data/OWPythonScript.py @ 11434:e3ed3a611526

Revision 11434:e3ed3a611526, 23.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 13 months ago (diff)

Added 'Default' flag to "in_data", "in_learner", ... channels.

They are preferred to "in_object".

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