diff -r bdd583f96e96 -r a8fad276cbd5 eric6/MicroPython/MicroPythonReplWidget.py --- a/eric6/MicroPython/MicroPythonReplWidget.py Tue Jul 09 19:49:41 2019 +0200 +++ b/eric6/MicroPython/MicroPythonReplWidget.py Wed Jul 10 20:21:57 2019 +0200 @@ -11,7 +11,9 @@ import re -from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QPoint, QEvent, QIODevice +from PyQt5.QtCore import ( + pyqtSlot, pyqtSignal, Qt, QPoint, QEvent, QIODevice, QTimer +) from PyQt5.QtGui import QColor, QKeySequence, QTextCursor from PyQt5.QtWidgets import ( QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy) @@ -20,9 +22,15 @@ HAS_QTSERIALPORT = True except ImportError: HAS_QTSERIALPORT = False +try: + from PyQt5.QtChart import QChart # __IGNORE_WARNING__ + HAS_QTCHART = True +except ImportError: + HAS_QTCHART = False from E5Gui.E5ZoomWidget import E5ZoomWidget -from E5Gui import E5MessageBox +from E5Gui import E5MessageBox, E5FileDialog +from E5Gui.E5Application import e5App from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget @@ -59,6 +67,10 @@ self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( "", False)) + + self.openButton.setIcon(UI.PixmapCache.getIcon("open")) + self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSaveAs")) + self.checkButton.setIcon(UI.PixmapCache.getIcon("question")) self.runButton.setIcon(UI.PixmapCache.getIcon("start")) self.replButton.setIcon(UI.PixmapCache.getIcon("terminal")) @@ -171,9 +183,13 @@ Public method to set the enabled state of the various action buttons. @param kwargs keyword arguments containg the enabled states (keys are - 'run', 'repl', 'files', 'chart' + 'run', 'repl', 'files', 'chart', 'open', 'save' @type dict """ + if "open" in kwargs: + self.openButton.setEnabled(kwargs["open"]) + if "save" in kwargs: + self.saveButton.setEnabled(kwargs["save"]) if "run" in kwargs: self.runButton.setEnabled(kwargs["run"]) if "repl" in kwargs: @@ -218,21 +234,27 @@ self.disconnectButton.setEnabled(connected) + def __showNoDeviceMessage(self): + """ + Private method to show a message dialog indicating a missing device. + """ + E5MessageBox.critical( + self, + self.tr("No device attached"), + self.tr("""Please ensure the device is plugged into your""" + """ computer and selected.\n\nIt must have a version""" + """ of MicroPython (or CircuitPython) flashed onto""" + """ it before anything will work.\n\nFinally press""" + """ the device's reset button and wait a few seconds""" + """ before trying again.""")) + @pyqtSlot() def on_replButton_clicked(self): """ Private slot to connect to the selected device and start a REPL. """ if not self.__device: - E5MessageBox.critical( - self, - self.tr("No device attached"), - self.tr("""Please ensure the device is plugged inti your""" - """ computer.\n\nIt must have a version of""" - """ MicroPython (or CircuitPython) flashed onto it""" - """ before the REPL will work.\n\nFinally press the""" - """ device's reset button and wait a few seconds""" - """ before trying again.""")) + self.__showNoDeviceMessage() return if self.__replRunning: @@ -260,6 +282,9 @@ self.__serial.write(b'\x02') # send Ctrl-C (keyboard interrupt) self.__serial.write(b'\x03') + + self.__replRunning = True + self.__device.setRepl(True) @pyqtSlot() def on_disconnectButton_clicked(self): @@ -283,7 +308,6 @@ Private method to activate a data plotter widget. """ # TODO: not implemented yet - raise NotImplementedError def __deactivatePlotter(self): """ @@ -368,7 +392,7 @@ while tc.movePosition(QTextCursor.Down): pass - index = 1 + index = 0 while index < len(data): if data[index] == 8: # \b tc.movePosition(QTextCursor.Left) @@ -380,7 +404,7 @@ data[index + 1] == 91): # VT100 cursor command detected: <Esc>[ index += 2 # move index to after the [ - match = self.__vt100Re.search(data[index:].decaode("utf-8")) + match = self.__vt100Re.search(data[index:].decode("utf-8")) if match: # move to last position in control sequence # ++ will be done at end of loop @@ -494,3 +518,89 @@ """ data = bytes(self.__serial.readAll()) self.dataReceived.emit(data) + + def execute(self, commandsList): + """ + Public method to execute a series of commands over a period of time. + + @param commandsList list of commands to be execute on the device + @type list of bytes + """ + if commandsList: + command = commandsList[0] + self.__serial.write(command) + remainder = commandsList[1:] + remainingTask = lambda commands=remainder: self.execute(commands) + QTimer.singleShot(2, remainingTask) + + @pyqtSlot() + def on_runButton_clicked(self): + """ + Private slot to execute the script of the active editor on the + selected device. + """ + if not self.__device: + self.__showNoDeviceMessage() + return + + aw = e5App().getObject("ViewManager").activeWindow() + if aw is None: + E5MessageBox.critical( + self, + self.tr("Run Script"), + self.tr("""There is no editor open. Abort...""")) + return + + script = aw.text() + if not script: + E5MessageBox.critical( + self, + self.tr("Run Script"), + self.tr("""The current editor does not contain a script.""" + """ Abort...""")) + return + + ok, reason = self.__device.canRunScript() + if not ok: + E5MessageBox.warning( + self, + self.tr("Run Script"), + self.tr("""<p>Cannot run script.</p><p>Reason:""" + """ {0}</p>""").format(reason)) + return + + if not self.__replRunning: + self.on_replButton_clicked() + if self.__replRunning: + self.__device.runScript(script) + + @pyqtSlot() + def on_openButton_clicked(self): + """ + Private slot to open a file of the connected device. + """ + if not self.__device: + self.__showNoDeviceMessage() + return + + workspace = self.__device.getWorkspace() + fileName = E5FileDialog.getOpenFileName( + self, + self.tr("Open Python File"), + workspace, + self.tr("Python3 Files (*.py)")) + if fileName: + e5App().getObject("ViewManager").openSourceFile(fileName) + + @pyqtSlot() + def on_saveButton_clicked(self): + """ + Private slot to save the current editor to the connected device. + """ + if not self.__device: + self.__showNoDeviceMessage() + return + + workspace = self.__device.getWorkspace() + aw = e5App().getObject("ViewManager").activeWindow() + aw.saveFileAs(workspace)