Continued implementing the MicroPython support. micropython

Thu, 18 Jul 2019 20:30:03 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 18 Jul 2019 20:30:03 +0200
branch
micropython
changeset 7067
3fc4082fc6ba
parent 7066
e3d034e65afc
child 7068
e3200e4dfa63

Continued implementing the MicroPython support.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonFileSystem.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonGraphWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonReplWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonSerialPort.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Wed Jul 17 20:31:47 2019 +0200
+++ b/eric6.e4p	Thu Jul 18 20:30:03 2019 +0200
@@ -457,8 +457,10 @@
     <Source>eric6/MicroPython/CircuitPythonDevices.py</Source>
     <Source>eric6/MicroPython/EspDevices.py</Source>
     <Source>eric6/MicroPython/MicroPythonDevices.py</Source>
+    <Source>eric6/MicroPython/MicroPythonFileSystem.py</Source>
     <Source>eric6/MicroPython/MicroPythonGraphWidget.py</Source>
     <Source>eric6/MicroPython/MicroPythonReplWidget.py</Source>
+    <Source>eric6/MicroPython/MicroPythonSerialPort.py</Source>
     <Source>eric6/MicroPython/MicrobitDevices.py</Source>
     <Source>eric6/MicroPython/__init__.py</Source>
     <Source>eric6/MultiProject/AddProjectDialog.py</Source>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/MicroPython/MicroPythonFileSystem.py	Thu Jul 18 20:30:03 2019 +0200
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some file system commands for MicroPython.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+
+class MicroPythonFileSystem(QObject):
+    """
+    Class implementing some file system commands for MicroPython.
+    
+    Some FTP like commands are provided to perform operations on the file
+    system of a connected MicroPython device. Supported commands are:
+    <ul>
+    <li>ls: directory listing</li>
+    <li>lls: directory listing with meta data</li>
+    <li>cd: change directory</li>
+    <li>pwd: get the current directory</li>
+    <li>put: copy a file to the connected device</li>
+    <li>get: get a file from the connected device</li>
+    </ul>
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(MicroPythonFileSystem, self).__init__(parent)
+    
+    def ls(self):
+        """
+        Public method to get a directory listing of the connected device.
+        
+        @return tuple containg the directory listing
+        @rtype tuple of str
+        """
+        # TODO: not implemented yet
+    
+    def lls(self):
+        """
+        Public method to get a long directory listing of the connected device
+        including meta data.
+        
+        @return tuple containg the the directory listing with tuple entries
+            containing the name, size, time and mode
+        @rtype tuple of str
+        """
+        # TODO: not implemented yet
+    
+    def cd(self, path):
+        """
+        Public method to change the current directory on the connected device.
+        
+        @param path directory to change to
+        @type str
+        """
+        # TODO: not implemented yet
+    
+    def pwd(self):
+        """
+        Public method to get the current directory of the connected device.
+        
+        @return current directory
+        @rtype str
+        """
+        # TODO: not implemented yet
+    
+    def put(self, hostFileName, deviceFileName):
+        """
+        Public method to copy a local file to the connected device.
+        
+        @param hostFileName name of the file to be copied
+        @type str
+        @param deviceFileName name of the file to copy to
+        @type str
+        @return flag indicating success
+        @rtype bool
+        """
+        # TODO: not implemented yet
+    
+    def get(self, deviceFileName, hostFileName):
+        """
+        Public method to copy a file from the connected device.
+        
+        @param deviceFileName name of the file to copy
+        @type str
+        @param hostFileName name of the file to copy to
+        @type str
+        @return flag indicating success
+        @rtype bool
+        """
+        # TODO: not implemented yet
--- a/eric6/MicroPython/MicroPythonGraphWidget.py	Wed Jul 17 20:31:47 2019 +0200
+++ b/eric6/MicroPython/MicroPythonGraphWidget.py	Thu Jul 18 20:30:03 2019 +0200
@@ -154,7 +154,7 @@
         for line in lines:
             if not line.endswith(b"\n"):
                 # incomplete line (last line); skip it
-                continue
+                break
             
             line = line.strip()
             if line.startswith(b"(") and line.endswith(b")"):
--- a/eric6/MicroPython/MicroPythonReplWidget.py	Wed Jul 17 20:31:47 2019 +0200
+++ b/eric6/MicroPython/MicroPythonReplWidget.py	Thu Jul 18 20:30:03 2019 +0200
@@ -14,9 +14,12 @@
 from PyQt5.QtCore import (
     pyqtSlot, pyqtSignal, Qt, QPoint, QEvent, QIODevice, QTimer
 )
-from PyQt5.QtGui import QColor, QKeySequence, QTextCursor, QBrush
+from PyQt5.QtGui import (
+    QColor, QKeySequence, QTextCursor, QBrush, QTextCharFormat
+)
 from PyQt5.QtWidgets import (
-    QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy)
+    QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy
+)
 try:
     from PyQt5.QtSerialPort import QSerialPort
     HAS_QTSERIALPORT = True
@@ -38,6 +41,7 @@
 
 import Globals
 import UI.PixmapCache
+import Preferences
 
 
 class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget):
@@ -57,22 +61,22 @@
     
     # ANSI Colors
     AnsiColors = {
-        (1, 30): QBrush(Qt.darkGray),
-        (1, 31): QBrush(Qt.red),
-        (1, 32): QBrush(Qt.green),
-        (1, 33): QBrush(Qt.yellow),
-        (1, 34): QBrush(Qt.blue),
-        (1, 35): QBrush(Qt.magenta),
-        (1, 36): QBrush(Qt.cyan),
-        (1, 37): QBrush(Qt.white),
-        (2, 30): QBrush(Qt.black),
-        (2, 31): QBrush(Qt.darkRed),
-        (2, 32): QBrush(Qt.darkGreen),
-        (2, 33): QBrush(Qt.darkYellow),
-        (2, 34): QBrush(Qt.darkBlue),
-        (2, 35): QBrush(Qt.darkMagenta),
-        (2, 36): QBrush(Qt.darkCyan),
-        (2, 37): QBrush(Qt.lightGray),
+        0: QBrush(Qt.black),
+        1: QBrush(Qt.darkRed),
+        2: QBrush(Qt.darkGreen),
+        3: QBrush(Qt.darkYellow),
+        4: QBrush(Qt.darkBlue),
+        5: QBrush(Qt.darkMagenta),
+        6: QBrush(Qt.darkCyan),
+        7: QBrush(Qt.lightGray),
+        10: QBrush(Qt.darkGray),
+        11: QBrush(Qt.red),
+        12: QBrush(Qt.green),
+        13: QBrush(Qt.yellow),
+        14: QBrush(Qt.blue),
+        15: QBrush(Qt.magenta),
+        16: QBrush(Qt.cyan),
+        17: QBrush(Qt.white),
     }
     
     def __init__(self, parent=None):
@@ -135,7 +139,7 @@
             return
         
         self.__vt100Re = re.compile(
-            r'(?P<count>\d*);?(?P<color>\d*)(?P<action>[ABCDKm])')
+            r'(?P<count>\d*)(?P<color>(?:;?\d*)*)(?P<action>[ABCDKm])')
         
         self.__populateDeviceTypeComboBox()
         
@@ -148,9 +152,12 @@
         self.replEdit.customContextMenuRequested.connect(
             self.__showContextMenu)
         
-        defaultCharFormat = self.replEdit.textCursor().charFormat()
-        self.DefaultForeground = defaultCharFormat.foreground()
-        self.DefaultBackground = defaultCharFormat.background()
+        font = Preferences.getEditorOtherFonts("MonospacedFont")
+        self.replEdit.setFontFamily(font.family())
+        self.replEdit.setFontPointSize(font.pointSize())
+        self.DefaultCharFormat = self.replEdit.currentCharFormat()
+        self.DefaultForeground = self.DefaultCharFormat.foreground()
+        self.DefaultBackground = self.DefaultCharFormat.background()
     
     def __populateDeviceTypeComboBox(self):
         """
@@ -455,29 +462,13 @@
                             tc.removeSelectedText()
                             self.replEdit.setTextCursor(tc)
                     elif action == "m":
-                        print(match.group("count"), match.group("color"))
-                        charFormat = tc.charFormat()
-                        if count == 0 and match.group("color") == "":
-                            # reset color
-                            charFormat.setForeground(self.DefaultForeground)
-                            charFormat.setBackground(self.DefaultBackground)
-                        elif count in (0, 1, 2):
-                            if match.group("color") != "":
-                                color = int(match.group("color"))
-                                if count == 0:
-                                    count = 1
-                                if 30 <= color <= 37:
-                                    charFormat.setForeground(
-                                        self.AnsiColors[(count, color)])
-                                elif 40 <= color <= 47:
-                                    charFormat.setBackground(
-                                        self.AnsiColors[(count, color - 10)])
-                        tc.setCharFormat(charFormat)
-                        self.replEdit.setTextCursor(tc)
+                        self.__setCharFormat(match.group(0)[:-1].split(";"),
+                                             tc)
             elif data[index] == 10:     # \n
                 tc.movePosition(QTextCursor.End)
                 self.replEdit.setTextCursor(tc)
                 self.replEdit.insertPlainText(chr(data[index]))
+                self.__setCharFormat(["0"], tc)     # reset format after a \n
             else:
                 tc.deleteChar()
                 self.replEdit.setTextCursor(tc)
@@ -487,6 +478,118 @@
         
         self.replEdit.ensureCursorVisible()
     
+    def __setCharFormat(self, formatCodes, textCursor):
+        """
+        Private method setting the current text format of the REPL pane based
+        on the passed ANSI codes.
+        
+        Following codes are used:
+        <ul>
+        <li>0: Reset</li>
+        <li>1: Bold font (weight 75)</li>
+        <li>2: Light font (weight 25)</li>
+        <li>3: Italic font</li>
+        <li>4: Underlined font</li>
+        <li>9: Strikeout font</li>
+        <li>21: Bold off (weight 50)</li>
+        <li>22: Light off (weight 50)</li>
+        <li>23: Italic off</li>
+        <li>24: Underline off</li>
+        <li>29: Strikeout off</li>
+        <li>30: foreground Black</li>
+        <li>31: foreground Dark Red</li>
+        <li>32: foreground Dark Green</li>
+        <li>33: foreground Dark Yellow</li>
+        <li>34: foreground Dark Blue</li>
+        <li>35: foreground Dark Magenta</li>
+        <li>36: foreground Dark Cyan</li>
+        <li>37: foreground Light Gray</li>
+        <li>40: background Black</li>
+        <li>41: background Dark Red</li>
+        <li>42: background Dark Green</li>
+        <li>43: background Dark Yellow</li>
+        <li>44: background Dark Blue</li>
+        <li>45: background Dark Magenta</li>
+        <li>46: background Dark Cyan</li>
+        <li>47: background Light Gray</li>
+        <li>53: Overlined font</li>
+        <li>55: Overline off</li>
+        <li>90: bright foreground Dark Gray</li>
+        <li>91: bright foreground Red</li>
+        <li>92: bright foreground Green</li>
+        <li>93: bright foreground Yellow</li>
+        <li>94: bright foreground Blue</li>
+        <li>95: bright foreground Magenta</li>
+        <li>96: bright foreground Cyan</li>
+        <li>97: bright foreground White</li>
+        <li>100: bright background Dark Gray</li>
+        <li>101: bright background Red</li>
+        <li>102: bright background Green</li>
+        <li>103: bright background Yellow</li>
+        <li>104: bright background Blue</li>
+        <li>105: bright background Magenta</li>
+        <li>106: bright background Cyan</li>
+        <li>107: bright background White</li>
+        </ul>
+        
+        @param formatCodes list of format codes
+        @type list of str
+        @param textCursor reference to the text cursor
+        @type QTextCursor
+        """
+        if not formatCodes:
+            # empty format codes list is treated as a reset
+            formatCodes = ["0"]
+        
+        charFormat = textCursor.charFormat()
+        for formatCode in formatCodes:
+            try:
+                formatCode = int(formatCode)
+            except ValueError:
+                # ignore non digit values
+                continue
+            
+            if formatCode == 0:
+                charFormat.setFontWeight(50)
+                charFormat.setFontItalic(False)
+                charFormat.setFontUnderline(False)
+                charFormat.setFontStrikeOut(False)
+                charFormat.setFontOverline(False)
+                charFormat.setForeground(self.DefaultForeground)
+                charFormat.setBackground(self.DefaultBackground)
+            elif formatCode == 1:
+                charFormat.setFontWeight(75)
+            elif formatCode == 2:
+                charFormat.setFontWeight(25)
+            elif formatCode == 3:
+                charFormat.setFontItalic(True)
+            elif formatCode == 4:
+                charFormat.setFontUnderline(True)
+            elif formatCode == 9:
+                charFormat.setFontStrikeOut(True)
+            elif formatCode in (21, 22):
+                charFormat.setFontWeight(50)
+            elif formatCode == 23:
+                charFormat.setFontItalic(False)
+            elif formatCode == 24:
+                charFormat.setFontUnderline(False)
+            elif formatCode == 29:
+                charFormat.setFontStrikeOut(False)
+            elif formatCode == 53:
+                charFormat.setFontOverline(True)
+            elif formatCode == 55:
+                charFormat.setFontOverline(False)
+            elif formatCode in (30, 31, 32, 33, 34, 35, 36, 37):
+                charFormat.setForeground(self.AnsiColors[formatCode - 30])
+            elif formatCode in (40, 41, 42, 43, 44, 45, 46, 47):
+                charFormat.setBackground(self.AnsiColors[formatCode - 40])
+            elif formatCode in (90, 91, 92, 93, 94, 95, 96, 97):
+                charFormat.setForeground(self.AnsiColors[formatCode - 80])
+            elif formatCode in (100, 101, 102, 103, 104, 105, 106, 107):
+                charFormat.setBackground(self.AnsiColors[formatCode - 90])
+        
+        textCursor.setCharFormat(charFormat)
+    
     def __doZoom(self, value):
         """
         Private slot to zoom the REPL pane.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/MicroPython/MicroPythonSerialPort.py	Thu Jul 18 20:30:03 2019 +0200
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a QSerialPort with additional functionality.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QIODevice
+from PyQt5.QtSerialPort import QSerialPort
+
+
+class MicroPythonSerialPort(QSerialPort):
+    """
+    Class implementing a QSerialPort with additional functionality
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(MicroPythonSerialPort, self).__init__(parent)
+        
+        self.__connected = False
+    
+    def openSerialLink(self, port):
+        """
+        Public method to open a serial link to a given serial port.
+        
+        @param port port name to connect to
+        @type str
+        @return flag indicating success
+        @rtype bool
+        """
+        self.setPortName(port)
+        if self.open(QIODevice.ReadWrite):
+            self.setDataTerminalReady(True)
+            # 115.200 baud, 8N1
+            self.setBaudRate(115200)
+            self.setDataBits(QSerialPort.Data8)
+            self.setParity(QSerialPort.NoParity)
+            self.setStopBits(QSerialPort.OneStop)
+            
+            self.__connected = True
+            return True
+        else:
+            return False
+    
+    def closeSerialLink(self):
+        """
+        Public method to close the open serial connection.
+        """
+        if self.__connceted:
+            self.close()
+            
+            self.__connected = False
+    
+    def isConnected(self):
+        """
+        Public method to get the connection state.
+        
+        @return flag indicating the connection state
+        @rtype bool
+        """
+        return self.__connected
+    
+    def readUntil(self, expected=b"\n", size=None):
+        """
+        Public method to read data until an expected sequence is found
+        (default: \n) or a specific size is exceeded.
+        
+        @param expected expected bytes sequence
+        @type bytes
+        @param size maximum data to be read
+        @type int
+        @return bytes read from the device including the expected sequence
+        @rtype bytes
+        """
+        data = bytearray()
+        while True:
+            if self.waitForReadyRead():
+                c = bytes(self.read(1))
+                if c:
+                    data += c
+                    if data.endswith(expected):
+                        break
+                    if size is not None and len(data) >= size:
+                        break
+                else:
+                    break
+            else:
+                break
+        
+        return bytes(data)

eric ide

mercurial