Sun, 07 Jul 2019 18:48:17 +0200
Started implementing the MicroPython support.
--- a/eric6.e4p Sun Jul 07 14:58:07 2019 +0200 +++ b/eric6.e4p Sun Jul 07 18:48:17 2019 +0200 @@ -454,6 +454,9 @@ <Source>eric6/IconEditor/__init__.py</Source> <Source>eric6/IconEditor/cursors/__init__.py</Source> <Source>eric6/IconEditor/cursors/cursors_rc.py</Source> + <Source>eric6/MicroPython/MicroPythonDevices.py</Source> + <Source>eric6/MicroPython/MicroPythonReplWidget.py</Source> + <Source>eric6/MicroPython/__init__.py</Source> <Source>eric6/MultiProject/AddProjectDialog.py</Source> <Source>eric6/MultiProject/MultiProject.py</Source> <Source>eric6/MultiProject/MultiProjectBrowser.py</Source> @@ -1832,6 +1835,7 @@ <Form>eric6/HexEdit/HexEditReplaceWidget.ui</Form> <Form>eric6/HexEdit/HexEditSearchWidget.ui</Form> <Form>eric6/IconEditor/IconSizeDialog.ui</Form> + <Form>eric6/MicroPython/MicroPythonReplWidget.ui</Form> <Form>eric6/MultiProject/AddProjectDialog.ui</Form> <Form>eric6/MultiProject/PropertiesDialog.ui</Form> <Form>eric6/Network/IRC/IrcChannelEditDialog.ui</Form>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/MicroPythonDevices.py Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing some utility functions and the MicroPythonDevice base +class. +""" + +from __future__ import unicode_literals + +import logging + +from PyQt5.QtCore import QObject + +import Globals +import UI.PixmapCache + + +SupportedBoards = { + "esp": { + "ids": [ + (0x1A86, 0x7523), # HL-340 + (0x10C4, 0xEA60), # CP210x + (0x0403, 0x6015), # Sparkfun ESP32 VID, PID + ], + "description": "ESP8266, ESP32", + "icon": "esp32Device", + }, + + "adafruit": { + "ids": [ + (0x239A, 0x8015), # Adafruit Feather M0 CircuitPython + (0x239A, 0x8023), # Adafruit Feather M0 Express CircuitPython + (0x239A, 0x801B), # Adafruit Feather M0 Express CircuitPython + (0x239A, 0x8014), # Adafruit Metro M0 CircuitPython + (0x239A, 0x8019), # Adafruit CircuitPlayground + # Express CircuitPython + (0x239A, 0x801D), # Adafruit Gemma M0 + (0x239A, 0x801F), # Adafruit Trinket M0 + (0x239A, 0x8012), # Adafruit ItsyBitsy M0 + (0x239A, 0x8021), # Adafruit Metro M4 + (0x239A, 0x8025), # Adafruit Feather RadioFruit + (0x239A, 0x8026), # Adafruit Feather M4 + (0x239A, 0x8028), # Adafruit pIRKey M0 + (0x239A, 0x802A), # Adafruit Feather 52840 + (0x239A, 0x802C), # Adafruit Itsy M4 + (0x239A, 0x802E), # Adafruit CRICKit M0 + (0x239A, 0xD1ED), # Adafruit HalloWing M0 + (0x239A, 0x8030), # Adafruit NeoTrellis M4 + (0x239A, 0x8032), # Grand Central + (0x2B04, 0xC00C), # Particle Argon + (0x2B04, 0xC00D), # Particle Boron + (0x2B04, 0xC00E), # Particle Xenon + (0x239A, 0x8034), # future board + (0x239A, 0x8036), # future board + (0x239A, 0x8038), # future board + (0x239A, 0x803A), # future board + (0x239A, 0x803C), # future board + (0x239A, 0x803E), # future board + (0x239A, None), # Any Adafruit Boards + ], + "description": "Adafruit CircuitPython", + "icon": "adafruitDevice", + }, + + "bbc_microbit": { + "ids": [ + (0x0D28, 0x0204), # micro:bit USB VID, PID + ], + "description": "BBC micro:bit", + "icon": "microbitDevice", + }, +} + + +def getSupportedDevices(): + """ + Function to get a list of supported MicroPython devices. + + @return set of tuples with the board type and description + @rtype set of tuples of (str, str) + """ + boards = [] + for board in SupportedBoards: + boards.append( + (board, SupportedBoards[board]["description"])) + return boards + + +def getFoundDevices(): + """ + Function to check the serial ports for supported MicroPython devices. + + @return set of tuples with the board type, a description and the serial + port it is connected at + @rtype set of tuples of (str, str, str) + """ + from PyQt5.QtSerialPort import QSerialPortInfo + + foundDevices = [] + + availablePorts = QSerialPortInfo.availablePorts() + for port in availablePorts: + vid = port.vendorIdentifier() + pid = port.productIdentifier() + for board in SupportedBoards: + if ((vid, pid) in SupportedBoards[board]["ids"] or + (vid, None) in SupportedBoards[board]["ids"]): + foundDevices.append( + (board, SupportedBoards[board]["description"], + port.portName())) + break + else: + logging.debug("Unknown device: (0x%04x:0x%04x)", vid, pid) + + return foundDevices + + +def getDeviceIcon(boardName, iconFormat=True): + """ + Function to get the icon for the given board. + + @param boardName name of the board + @type str + @param iconFormat flag indicating to get an icon or a pixmap + @type bool + @return icon for the board (iconFormat == True) or + a pixmap (iconFormat == False) + @rtype QIcon or QPixmap + """ + if boardName in SupportedBoards: + iconName = SupportedBoards[boardName]["icon"] + else: + # return a generic MicroPython icon + iconName = "micropython48" + + if iconFormat: + return UI.PixmapCache.getIcon(iconName) + else: + return UI.PixmapCache.getPixmap(iconName) + + +def getDevice(deviceType): + """ + Public method to instantiate a specific MicroPython device interface. + + @param deviceType type of the device interface + @type str + @return instantiated device interface + @rtype MicroPythonDevice + """ + # TODO: not implemented yet + return None + + +class MicroPythonDevice(QObject): + """ + Base class for the more specific MicroPython devices. + """ + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent object + @type QObject + """ + super(MicroPythonDevice, self).__init__(parent) + + def supportedActions(self): + """ + Public method to get the names of the supported actions. + + @return tuple of supported actions out of "repl", "run", "files", + "chart" + @rtype tuple of str + """ + return tuple() + + def findDevice(self, deviceType): + """ + Public method to find the first device of a specific type. + + @param deviceType device type + @type str + @return tuple containing the port the device is connected to and its + serial number + @rtype tuple of (str, str) + """ + from PyQt5.QtSerialPort import QSerialPortInfo + + availablePorts = QSerialPortInfo.availablePorts() + for port in availablePorts: + vid = port.vendorIdentifier() + pid = port.productIdentifier() + for board in SupportedBoards: + if ((vid, pid) in SupportedBoards[board] or + (vid, None) in SupportedBoards[board]): + portName = port.portName() + serialNumber = port.serialNumber() + return (self.__portPath(portName), serialNumber) + + return (None, None) + + def __portPath(self, portName): + """ + Private method to get the full path of a given port. + + @param portName name of the port the path shall be determined for + @type str + @return full port path + @rtype str + """ + if Globals.isWindowsPlatform(): + # return name unchanged + return portName + else: + # assume Posix system (Linux or macOS) + return "/dev/{0}".format(portName)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/MicroPythonReplWidget.py Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,373 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the MicroPython REPL widget. +""" + +from __future__ import unicode_literals + +import re + +from PyQt5.QtCore import pyqtSlot, Qt, QPoint, QEvent +from PyQt5.QtGui import QColor, QKeySequence, QTextCursor +from PyQt5.QtWidgets import ( + QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy) +try: + from PyQt5.QtSerialPort import QSerialPortInfo # __IGNORE_WARNING__ + HAS_QTSERIALPORT = True +except ImportError: + HAS_QTSERIALPORT = False + +from E5Gui.E5ZoomWidget import E5ZoomWidget + +from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget + +from . import MicroPythonDevices + +import Globals +import UI.PixmapCache + + +class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget): + """ + Class implementing the MicroPython REPL widget. + """ + ZoomMin = -10 + ZoomMax = 20 + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget + @type QWidget + """ + super(MicroPythonReplWidget, self).__init__(parent) + self.setupUi(self) + + self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( + "", False)) + self.checkButton.setIcon(UI.PixmapCache.getIcon("question")) + self.runButton.setIcon(UI.PixmapCache.getIcon("start")) + self.replButton.setIcon(UI.PixmapCache.getIcon("terminal")) + self.filesButton.setIcon(UI.PixmapCache.getIcon("filemanager")) + self.chartButton.setIcon(UI.PixmapCache.getIcon("chart")) + self.disconnectButton.setIcon(UI.PixmapCache.getIcon("disconnect")) + + self.__zoomLayout = QHBoxLayout() + spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, + QSizePolicy.Minimum) + self.__zoomLayout.addSpacerItem(spacerItem) + + self.__zoom0 = self.replEdit.fontPointSize() + self.__zoomWidget = E5ZoomWidget( + UI.PixmapCache.getPixmap("zoomOut"), + UI.PixmapCache.getPixmap("zoomIn"), + UI.PixmapCache.getPixmap("zoomReset"), self) + self.__zoomLayout.addWidget(self.__zoomWidget) + self.layout().insertLayout( + self.layout().count() - 1, + self.__zoomLayout) + self.__zoomWidget.setMinimum(self.ZoomMin) + self.__zoomWidget.setMaximum(self.ZoomMax) + self.__zoomWidget.valueChanged.connect(self.__doZoom) + self.__currentZoom = 0 + + self.__serial = None + self.__device = None + self.setConnected(False) + + if not HAS_QTSERIALPORT: + self.replEdit.setHtml(self.tr( + "<h3>The QtSerialPort package is not available.<br/>" + "MicroPython support is deactivated.</h3>")) + self.setEnabled(False) + return + + self.__vt100Re = re.compile( + r'(?P<count>[\d]*)(;?[\d]*)*(?P<action>[ABCDKm])') + + self.__populateDeviceTypeComboBox() + + self.replEdit.setAcceptRichText(False) + self.replEdit.setUndoRedoEnabled(False) + self.replEdit.setContextMenuPolicy(Qt.CustomContextMenu) + + self.replEdit.installEventFilter(self) + + self.replEdit.customContextMenuRequested.connect( + self.__showContextMenu) + + def __populateDeviceTypeComboBox(self): + """ + Private method to populate the device type selector. + """ + self.deviceTypeComboBox.clear() + self.deviceInfoLabel.clear() + + self.deviceTypeComboBox.addItem("", "") + devices = MicroPythonDevices.getFoundDevices() + if devices: + self.deviceInfoLabel.setText( + self.tr("%n supported device(s) detected.", n=len(devices))) + for device in sorted(devices): + self.deviceTypeComboBox.addItem( + self.tr("{0} at {1}".format(device[1], device[2])), + device[0]) + else: + self.deviceInfoLabel.setText( + self.tr("No supported devices detected.")) + + self.on_deviceTypeComboBox_activated(0) + + @pyqtSlot(int) + def on_deviceTypeComboBox_activated(self, index): + """ + Private slot handling the selection of a device type. + + @param index index of the selected device + @type int + """ + deviceType = self.deviceTypeComboBox.itemData(index) + self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( + deviceType, False)) + + self.__device = MicroPythonDevices.getDevice(deviceType) + if self.__device: + actions = self.__device.supportedActions() + else: + actions = tuple() + self.__setActionButtons(actions) + + @pyqtSlot() + def on_checkButton_clicked(self): + """ + Private slot to check for connected devices. + """ + self.__populateDeviceTypeComboBox() + + def __setActionButtons(self, actions): + """ + Private method to set the enabled state of the various action buttons. + + @param actions tuple of supported actions out of "repl", "run", + "files", "chart" + @type tuple of str + """ + self.runButton.setEnabled("run" in actions) + self.replButton.setEnabled("repl" in actions) + self.filesButton.setEnabled("files" in actions) + self.chartButton.setEnabled("chart" in actions) + + @pyqtSlot(QPoint) + def __showContextMenu(self, pos): + """ + Privat slot to show the REPL context menu. + + @param pos position to show the menu at + @type QPoint + """ + if Globals.isMacPlatform(): + copyKeys = QKeySequence(Qt.CTRL + Qt.Key_C) + pasteKeys = QKeySequence(Qt.CTRL + Qt.Key_V) + else: + copyKeys = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_C) + pasteKeys = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_V) + menu = QMenu(self) + menu.addAction(self.tr("Copy"), self.replEdit.copy, copyKeys) + menu.addAction(self.tr("Paste"), self.__paste, pasteKeys) + menu.exec_(self.replEdit.mapToGlobal(pos)) + + def setConnected(self, connected): + """ + Public method to set the connection status LED. + + @param connected connection state + @type bool + """ + if connected: + self.deviceConnectedLed.setColor(QColor(Qt.green)) + else: + self.deviceConnectedLed.setColor(QColor(Qt.red)) + + self.deviceTypeComboBox.setEnabled(not connected) + + self.disconnectButton.setEnabled(connected) + + @pyqtSlot() + def on_replButton_clicked(self): + """ + Private slot to connect to the selected device and start a REPL. + """ + # TODO: not implemented yet + raise NotImplementedError + + @pyqtSlot() + def on_disconnectButton_clicked(self): + """ + Private slot to disconnect from the currently connected device. + """ + # TODO: not implemented yet + raise NotImplementedError + + def __activatePlotter(self): + """ + Private method to activate a data plotter widget. + """ + # TODO: not implemented yet + raise NotImplementedError + + def __deactivatePlotter(self): + """ + Private method to deactivate the plotter widget. + """ + # TODO: not implemented yet + + @pyqtSlot() + def __paste(self): + """ + Private slot to perform a paste operation. + """ + clipboard = QApplication.clipboard() + if clipboard and clipboard.text(): + pasteText = clipboard.text().replace('\n\r', '\r') + pasteText = pasteText.replace('\n', '\r') + self.__serial and self.__serial.write(pasteText.encode("utf-8")) + + def eventFilter(self, obj, evt): + """ + Public method to process events for the REPL pane. + + @param obj reference to the object the event was meant for + @type QObject + @param evt reference to the event object + @type QEvent + @return flag to indicate that the event was handled + @rtype bool + """ + if obj is self.replEdit and evt.type() == QEvent.KeyPress: + # handle the key press event on behalve of the REPL pane + key = evt.key() + msg = bytes(evt.text(), 'utf8') + if key == Qt.Key_Backspace: + msg = b'\b' + elif key == Qt.Key_Delete: + msg = b'\x1B[\x33\x7E' + elif key == Qt.Key_Up: + msg = b'\x1B[A' + elif key == Qt.Key_Down: + msg = b'\x1B[B' + elif key == Qt.Key_Right: + msg = b'\x1B[C' + elif key == Qt.Key_Left: + msg = b'\x1B[D' + elif key == Qt.Key_Home: + msg = b'\x1B[H' + elif key == Qt.Key_End: + msg = b'\x1B[F' + elif ((Globals.isMacPlatform() and + evt.modifiers() == Qt.MetaModifier) or + (not Globals.isMacPlatform() and + evt.modifiers() == Qt.ControlModifier)): + if Qt.Key_A <= key <= Qt.Key_Z: + # devices treat an input of \x01 as Ctrl+A, etc. + msg = bytes([1 + key - Qt.Key_A]) + elif (evt.modifiers() == Qt.ControlModifier | Qt.ShiftModifier or + (Globals.isMacPlatform() and + evt.modifiers() == Qt.ControlModifier)): + if key == Qt.Key_C: + self.replEdit.copy() + msg = b'' + elif key == Qt.Key_V: + self.__paste() + msg = b'' + self.__serial and self.__serial.write(msg) + return True + + else: + # standard event processing + return super(MicroPythonReplWidget, self).eventFilter(obj, evt) + + def __processData(self, data): + """ + Private slot to process bytes received from the device. + + @param data bytes received from the device + @type bytes + """ + tc = self.replEdit.textCursor() + # the text cursor must be on the last line + while tc.movePosition(QTextCursor.Down): + pass + + index = 1 + while index < len(data): + if data[index] == 8: # \b + tc.movePosition(QTextCursor.Left) + self.replEdit.setTextCursor(tc) + elif data[index] == 13: # \r + pass + elif (len(data) > index + 1 and + data[index] == 27 and + 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")) + if match: + # move to last position in control sequence + # ++ will be done at end of loop + index += match.end() - 1 + + if match.group("count") == "": + count = 1 + else: + count = int(match.group("count")) + + action = match.group("action") + if action == "A": # up + tc.movePosition(QTextCursor.Up, n=count) + self.replEdit.setTextCursor(tc) + elif action == "B": # down + tc.movePosition(QTextCursor.Down, n=count) + self.replEdit.setTextCursor(tc) + elif action == "C": # right + tc.movePosition(QTextCursor.Right, n=count) + self.replEdit.setTextCursor(tc) + elif action == "D": # left + tc.movePosition(QTextCursor.Left, n=count) + self.replEdit.setTextCursor(tc) + elif action == "K": # delete things + if match.group("count") == "": # delete to eol + tc.movePosition(QTextCursor.EndOfLine, + mode=QTextCursor.KeepAnchor) + tc.removeSelectedText() + self.replEdit.setTextCursor(tc) + # TODO: add handling of 'm' (colors) + elif data[index] == 10: # \n + tc.movePosition(QTextCursor.End) + self.replEdit.setTextCursor(tc) + self.replEdit.insertPlainText(chr(data[index])) + else: + tc.deleteChar() + self.replEdit.setTextCursor(tc) + self.replEdit.insertPlainText(chr(data[index])) + + index += 1 + + self.replEdit.ensureCursorVisible() + + def __doZoom(self, value): + """ + Private slot to zoom the REPL pane. + + @param value zoom value + @param int + """ + if value < self.__currentZoom: + self.replEdit.zoomOut(self.__currentZoom - value) + elif value > self.__currentZoom: + self.replEdit.zoomIn(value - self.__currentZoom) + self.__currentZoom = value
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/MicroPythonReplWidget.ui Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MicroPythonReplWidget</class> + <widget class="QWidget" name="MicroPythonReplWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>456</width> + <height>548</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="deviceIconLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" colspan="3"> + <widget class="QLabel" name="deviceInfoLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="deviceTypeComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="E5Led" name="deviceConnectedLed" native="true"/> + </item> + <item row="0" column="1"> + <widget class="QToolButton" name="checkButton"> + <property name="toolTip"> + <string>Press to check for connected devices</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QToolButton" name="runButton"> + <property name="toolTip"> + <string>Press to run the current script on the selected device</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="replButton"> + <property name="toolTip"> + <string>Press to open a terminal (REPL) on the selected device</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="filesButton"> + <property name="toolTip"> + <string>Press to open a file manager on the selected device (REPL must be disconnected first)</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="chartButton"> + <property name="toolTip"> + <string>Press to open a chart window to display data receive from the selected device</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="disconnectButton"> + <property name="toolTip"> + <string>Press to disconnect the selected device</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTextEdit" name="replEdit"/> + </item> + <item> + <widget class="QLabel" name="statusLabel"/> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>E5Led</class> + <extends>QWidget</extends> + <header>E5Gui/E5Led.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>deviceTypeComboBox</tabstop> + <tabstop>checkButton</tabstop> + <tabstop>runButton</tabstop> + <tabstop>replButton</tabstop> + <tabstop>filesButton</tabstop> + <tabstop>chartButton</tabstop> + <tabstop>disconnectButton</tabstop> + <tabstop>replEdit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/MicroPython/__init__.py Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing an interface to MicroPython devices like esp32. +""" + +from __future__ import unicode_literals
--- a/eric6/Preferences/ConfigurationPages/InterfacePage.py Sun Jul 07 14:58:07 2019 +0200 +++ b/eric6/Preferences/ConfigurationPages/InterfacePage.py Sun Jul 07 18:48:17 2019 +0200 @@ -86,6 +86,8 @@ # right side self.codeDocumentationViewerCheckBox.setChecked( Preferences.getUI("ShowCodeDocumentationViewer")) + self.microPythonCheckBox.setChecked( + Preferences.getUI("ShowMicroPython")) self.pypiCheckBox.setChecked( Preferences.getUI("ShowPyPIPackageManager")) self.condaCheckBox.setChecked( @@ -175,6 +177,9 @@ "ShowCodeDocumentationViewer", self.codeDocumentationViewerCheckBox.isChecked()) Preferences.setUI( + "ShowMicroPython", + self.microPythonCheckBox.isChecked()) + Preferences.setUI( "ShowPyPIPackageManager", self.pypiCheckBox.isChecked()) Preferences.setUI(
--- a/eric6/Preferences/ConfigurationPages/InterfacePage.ui Sun Jul 07 14:58:07 2019 +0200 +++ b/eric6/Preferences/ConfigurationPages/InterfacePage.ui Sun Jul 07 18:48:17 2019 +0200 @@ -445,6 +445,16 @@ </property> </widget> </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="microPythonCheckBox"> + <property name="toolTip"> + <string>Select to activate the MicroPython widget</string> + </property> + <property name="text"> + <string>MicroPython</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -518,6 +528,7 @@ <tabstop>fileBrowserCheckBox</tabstop> <tabstop>symbolsCheckBox</tabstop> <tabstop>codeDocumentationViewerCheckBox</tabstop> + <tabstop>microPythonCheckBox</tabstop> <tabstop>pypiCheckBox</tabstop> <tabstop>condaCheckBox</tabstop> <tabstop>cooperationCheckBox</tabstop>
--- a/eric6/Preferences/__init__.py Sun Jul 07 14:58:07 2019 +0200 +++ b/eric6/Preferences/__init__.py Sun Jul 07 18:48:17 2019 +0200 @@ -168,6 +168,7 @@ "ShowCondaPackageManager": True, # right side "ShowCooperation": True, # right side "ShowIrc": True, # right side + "ShowMicroPython": True, # right side "ShowNumbersViewer": True, # bottom side "ViewProfiles2": { "edit": [ @@ -1995,7 +1996,7 @@ "ShowCodeDocumentationViewer", "ShowPyPIPackageManager", "ShowCondaPackageManager", "ShowCooperation", "ShowIrc", "ShowTemplateViewer", "ShowFileBrowser", "ShowSymbolsViewer", - "ShowNumbersViewer", "UseNativeMenuBar"]: + "ShowNumbersViewer", "ShowMicroPython", "UseNativeMenuBar"]: return toBool(prefClass.settings.value( "UI/" + key, prefClass.uiDefaults[key])) elif key in ["TabViewManagerFilenameLength", "CaptionFilenameLength",
--- a/eric6/UI/UserInterface.py Sun Jul 07 14:58:07 2019 +0200 +++ b/eric6/UI/UserInterface.py Sun Jul 07 18:48:17 2019 +0200 @@ -907,6 +907,15 @@ UI.PixmapCache.getIcon("irc.png"), self.tr("IRC")) + if Preferences.getUI("ShowMicroPython"): + # Create the MicroPython part of the user interface + logging.debug("Creating MicroPython Widget...") + from MicroPython.MicroPythonReplWidget import MicroPythonReplWidget + self.microPythonRepl = MicroPythonReplWidget(self) + self.rToolbox.addItem(self.microPythonRepl, + UI.PixmapCache.getIcon("micropython"), + self.tr("MicroPython")) + #################################################### ## Populate the bottom toolbox #################################################### @@ -1095,6 +1104,15 @@ self.irc, UI.PixmapCache.getIcon("irc.png"), self.tr("IRC")) + if Preferences.getUI("ShowMicroPython"): + # Create the MicroPython part of the user interface + logging.debug("Creating MicroPython Widget...") + from MicroPython.MicroPythonReplWidget import MicroPythonReplWidget + self.microPythonRepl = MicroPythonReplWidget(self) + self.rightSidebar.addTab( + self.microPythonRepl, UI.PixmapCache.getIcon("micropython"), + self.tr("MicroPython")) + #################################################### ## Populate the bottom side bar ####################################################
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/chart.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,17 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + .ColorScheme-Highlight { + color:#3daee9; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 4 3 L 4 9 L 4 10 L 4 15 L 4 16 L 4 17 L 3 17 L 3 18 L 4 18 L 4 19 L 5 19 L 5 18 L 18.292969 18 L 19 18 L 19 17.292969 L 19 17 L 18.707031 17 L 17.958984 16.251953 L 17.130859 15.423828 L 15.130859 13.423828 L 15.126953 13.427734 L 15.121094 13.423828 L 12.998047 15.546875 L 11.703125 14.251953 L 10.875 13.423828 L 8.875 11.423828 L 8.8730469 11.423828 L 7 11.423828 L 6 11.423828 L 5 11.423828 L 5 11 L 5 10 L 6 10 L 7 10 L 8.8730469 10 L 8.875 10 L 10.875 8 L 11.703125 7.171875 L 12.998047 5.8769531 L 15.121094 8 L 15.126953 7.9960938 L 15.130859 8 L 17.130859 6 L 17.958984 5.171875 L 19 4.1308594 L 18.292969 3.4238281 L 17.251953 4.4648438 L 16.423828 5.2929688 L 15.126953 6.5898438 L 13 4.4648438 L 12.998047 4.4667969 L 12.996094 4.4648438 L 11.46875 5.9921875 L 5 5.9921875 L 5 5 L 5 3 L 4 3 z M 5 6 L 11.460938 6 L 10.996094 6.4648438 L 10.167969 7.2929688 L 8.4609375 9 L 7 9 L 6 9 L 5 9 L 5 6 z M 5 12.423828 L 6 12.423828 L 7 12.423828 L 8.4609375 12.423828 L 10.167969 14.130859 L 10.996094 14.958984 L 11.460938 15.423828 L 5 15.423828 L 5 15 L 5 12.423828 z M 15.126953 14.833984 L 16.423828 16.130859 L 17.251953 16.958984 L 17.292969 17 L 5 17 L 5 16 L 5 15.431641 L 11.46875 15.431641 L 12.996094 16.958984 L 12.998047 16.957031 L 13 16.958984 L 15.126953 14.833984 z " + class="ColorScheme-Text" + /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/disconnect.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,22 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + .ColorScheme-NegativeText { + color:#da4453; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 18.292969 3 L 14.792969 6.5 L 13.292969 5 L 11.292969 7 L 9.5 8.7929688 L 11 10.292969 L 11.707031 11 L 13.207031 12.5 L 15 10.707031 L 17 8.7070312 L 15.5 7.2070312 L 19 3.7070312 L 18.292969 3 z M 8.7929688 9.5 L 8 10.292969 L 6 12.292969 L 7.5 13.792969 L 3 18.292969 L 3.7070312 19 L 8.2070312 14.5 L 9.7070312 16 L 11.707031 14 L 12.5 13.207031 L 11 11.707031 L 10.292969 11 L 8.7929688 9.5 z " + class="ColorScheme-Text" + /> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 14.833984 14 L 14 14.833984 L 15.666016 16.5 L 14 18.166016 L 14.833984 19 L 16.5 17.333984 L 18.166016 19 L 19 18.166016 L 17.333984 16.5 L 19 14.833984 L 18.166016 14 L 16.5 15.666016 L 14.833984 14 z " + class="ColorScheme-NegativeText" + /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/filemanager.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="22" + height="22" + viewBox="0 0 22 22" + id="svg7925" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="system-file-manager.svg"> + <defs + id="defs7927"> + <linearGradient + id="linearGradient4274" + inkscape:collect="always"> + <stop + id="stop4276" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop4278" + offset="1" + style="stop-color:#ffffff;stop-opacity:0.48760331" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4274" + id="linearGradient8485" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.69224711,0,0,0.69224711,-264.27155,1668.4354)" + x1="390.57144" + y1="498.298" + x2="442.57144" + y2="498.298" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4393" + id="linearGradient4399" + x1="419.4624" + y1="499.23697" + x2="432.57144" + y2="523.79797" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.34616832,0,0,0.34616832,-141.20459,864.54218)" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4393"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop4395" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop4397" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4172-5" + id="linearGradient4178" + y1="548.88599" + y2="495.30789" + x2="397.2283" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.34616832,0,0,0.34605565,-133.24271,862.87242)" + x1="434.16153" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4172-5"> + <stop + style="stop-color:#127bdc;stop-opacity:1" + id="stop4174-6" /> + <stop + offset="1" + style="stop-color:#64b4f4;stop-opacity:1" + id="stop4176-6" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4227" + id="linearGradient4225" + gradientUnits="userSpaceOnUse" + x1="396.57144" + y1="498.798" + x2="426.57144" + y2="511.798" + gradientTransform="matrix(0.34616832,0,0,0.34616832,-133.24271,864.50368)" /> + <linearGradient + id="linearGradient4227" + inkscape:collect="always"> + <stop + id="stop4229" + offset="0" + style="stop-color:#f5f5f5;stop-opacity:1" /> + <stop + id="stop4231" + offset="1" + style="stop-color:#f9f9f9;stop-opacity:1" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(0.34616832,0,0,0.34616832,-133.24271,862.81136)" + inkscape:collect="always" + xlink:href="#linearGradient4291" + id="linearGradient4297" + x1="388.57144" + y1="487.798" + x2="416.57144" + y2="507.798" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4291"> + <stop + style="stop-color:#1d5e8c;stop-opacity:1" + offset="0" + id="stop4293" /> + <stop + style="stop-color:#2675a7;stop-opacity:1" + offset="1" + id="stop4295" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4274" + id="linearGradient8603" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.34616832,0,0,0.34616832,-133.24271,2893.0497)" + x1="390.57144" + y1="498.298" + x2="442.57144" + y2="498.298" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4274" + id="linearGradient8603-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.34616832,0,0,0.34616832,-135.15325,827.3248)" + x1="390.57144" + y1="498.298" + x2="442.57144" + y2="498.298" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="14.052007" + inkscape:cy="13.482364" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-nodes="true" + inkscape:object-nodes="true"> + <inkscape:grid + type="xygrid" + id="grid8605" /> + </sodipodi:namedview> + <metadata + id="metadata7930"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Livello 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1030.3622)"> + <path + inkscape:connector-curvature="0" + style="fill:url(#linearGradient4297);fill-opacity:1" + id="rect4180" + d="m 1,1042.3622 20,0.501 0,-8.501 -10,0 -1.588488,-2.0013 -8.411512,0 z" + sodipodi:nodetypes="ccccccc" /> + <rect + ry="1.0000174" + rx="0.9128685" + y="1036.3622" + x="2" + height="5.0000172" + width="18" + id="rect4223" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4225);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <path + inkscape:connector-curvature="0" + style="fill:url(#linearGradient4178)" + d="m 9,1038.3622 -2,1 -6,0 -0.0895868,1.0011 -5e-7,9.5003 c 0,0.2699 0.244354,0.4954 0.4919395,0.4954 l 19.0105828,0.01 c 0.255387,0.01 0.500065,-0.1978 0.500065,-0.5 L 21,1038.3622 Z" + id="rect4113" + sodipodi:nodetypes="cccccscccc" /> + <path + inkscape:connector-curvature="0" + style="opacity:0.3;fill:#ffffff;fill-opacity:1;fill-rule:evenodd" + id="path4224" + d="m 1,1039.3622 -0.0895868,1.0011 7.0895868,0 1,-2 -2,0.9989 z" + sodipodi:nodetypes="cccccc" /> + <path + inkscape:connector-curvature="0" + style="opacity:0.3;fill:#ffffff;fill-opacity:1;fill-rule:evenodd" + id="path4196" + d="m 9.411512,1032.3622 0.588488,3 11,0 0,-1 -10,0 z" + sodipodi:nodetypes="cccccc" /> + <path + style="opacity:0.09899998;fill:url(#linearGradient4399);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 0.9104132,1040.3633 7.0895868,-0 1,-2 12,0 0,11.6563 -10.385049,0 z" + id="path4383" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccc" /> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.85;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient8603);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 2.9121094,1036.3622 c -0.5057292,0 -0.9121094,0.446 -0.9121094,1 l 0,1 c 0,-0.554 0.4063802,-1 0.9121094,-1 l 16.0878906,0 c 0.505729,0 0.912109,0.446 0.912109,1 l 0,-1 c 0,-0.554 -0.40638,-1 -0.912109,-1 z" + id="rect8607" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sscsscsss" /> + <path + style="fill:#000000;color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.3;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 20.992188 8 L 20.912109 18.505859 C 20.912109 18.808059 20.667496 19.015859 20.412109 19.005859 L 1.4023438 18.996094 C 1.1547582 18.996094 0.91015625 18.771853 0.91015625 18.501953 L 0.91015625 19.501953 C 0.91015625 19.771853 1.1547582 19.996094 1.4023438 19.996094 L 20.412109 20.005859 C 20.667496 20.015859 20.912109 19.808059 20.912109 19.505859 L 21 8 L 20.992188 8 z " + transform="translate(0,1030.3622)" + id="path4164" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/question.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,14 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 10.96875 3 C 9.21782 3 7.56187 3.4216019 6 4.2636719 L 6.7773438 6.0507812 C 7.5582637 5.6672613 8.2600456 5.3933025 8.8847656 5.2265625 C 9.5177356 5.0598325 10.162692 4.9765625 10.820312 4.9765625 C 11.806753 4.9765625 12.563734 5.1968119 13.089844 5.6386719 C 13.615944 6.0805419 13.878906 6.7136725 13.878906 7.5390625 C 13.878906 7.9809325 13.822111 8.369625 13.707031 8.703125 C 13.591951 9.036605 13.394724 9.369635 13.115234 9.703125 C 12.835754 10.036615 12.247563 10.586476 11.351562 11.353516 C 10.251483 12.356846 9.10046 14.139 9 16 L 11 16 L 10.994141 15.96875 C 10.994141 15.21007 11.125612 14.601138 11.388672 14.142578 C 11.659952 13.675688 12.201805 13.087576 13.015625 12.378906 C 14.010295 11.536836 14.673153 10.903726 15.001953 10.478516 C 15.338993 10.053316 15.589506 9.6041363 15.753906 9.1289062 C 15.918406 8.6536762 16 8.1071944 16 7.4902344 C 16 6.0728944 15.55227 4.97119 14.65625 4.1875 C 13.76024 3.39546 12.53061 3 10.96875 3 z M 9 17 L 9 19 L 11 19 L 11 17 L 9 17 z " + class="ColorScheme-Text" + /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/start.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,14 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="m 4 4 0 14 L 18 11 Z" + class="ColorScheme-Text" + /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/icons/default/terminal.svg Sun Jul 07 18:48:17 2019 +0200 @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg id="svg2987" width="16px" height="16px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs id="defs2989"> + <linearGradient id="linearGradient2880" x1="16.143" x2="16.143" y1="4" y2="44" gradientTransform="matrix(.23078 0 0 .15386 2.4612 4.8074)" gradientUnits="userSpaceOnUse"> + <stop id="stop2225-6-4-7-2" style="stop-color:#fff" offset="0"/> + <stop id="stop2229-2-5-5-8" style="stop-color:#fff;stop-opacity:0" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2883" x1="16.626" x2="20.055" y1="15.298" y2="24.628" gradientTransform="matrix(.30476 0 0 .32156 .68572 1.0807)" gradientUnits="userSpaceOnUse"> + <stop id="stop2687-1-9-0-2" style="stop-color:#fff" offset="0"/> + <stop id="stop2689-5-4-3-1" style="stop-color:#fff;stop-opacity:0" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2886" x1="16" x2="16" y1="27.045" y2="16" gradientTransform="matrix(.34286 0 0 .36364 -.42808 -.81818)" gradientUnits="userSpaceOnUse" xlink:href="#linearGradient3680-6"/> + <linearGradient id="linearGradient3680-6"> + <stop id="stop3682-4" style="stop-color:#dcdcdc" offset="0"/> + <stop id="stop3684-8" style="stop-color:#fff" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2890" x1="20" x2="20" y1="28" y2="26" gradientTransform="matrix(.375 0 0 .375 -.74978 -.5)" gradientUnits="userSpaceOnUse" xlink:href="#linearGradient3680-6"/> + <linearGradient id="linearGradient2894" x1="12.579" x2="12.213" y1="2.9165" y2="47.279" gradientTransform="matrix(.28855 0 0 .25608 1.0743 2.6116)" gradientUnits="userSpaceOnUse"> + <stop id="stop2240-1-6-7-0" style="stop-color:#fff" offset="0"/> + <stop id="stop2242-7-3-7-2" style="stop-color:#fff;stop-opacity:0" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2897" x1="23.071" x2="23.071" y1="36.047" y2="33.296" gradientTransform="matrix(.33032 0 0 .32624 .074248 1.9649)" gradientUnits="userSpaceOnUse"> + <stop id="stop2225-6-4-7" style="stop-color:#fff" offset="0"/> + <stop id="stop2229-2-5-5" style="stop-color:#fff;stop-opacity:0" offset="1"/> + </linearGradient> + <radialGradient id="radialGradient2900" cx="7.4957" cy="8.4498" r="20" gradientTransform="matrix(0 .47178 -.86826 -1.9907e-8 15.337 1.0829)" gradientUnits="userSpaceOnUse"> + <stop id="stop3790-0-0" style="stop-color:#505050" offset="0"/> + <stop id="stop3792-0-2" style="stop-color:#141414" offset="1"/> + </radialGradient> + <linearGradient id="linearGradient2902" x1="16.143" x2="16.143" y1="4" y2="44" gradientTransform="matrix(.28207 0 0 .20514 1.2304 3.5766)" gradientUnits="userSpaceOnUse"> + <stop id="stop3796-3-0" style="stop-color:#323232" offset="0"/> + <stop id="stop3798-8-6" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2983" x1="24" x2="24" y1="7.96" y2="43.865" gradientTransform="matrix(.3333 0 0 .32429 .00075427 .06858)" gradientUnits="userSpaceOnUse"> + <stop id="stop4324-9-7" style="stop-color:#f0f0f0" offset="0"/> + <stop id="stop2860-4-4" style="stop-color:#d7d7d8" offset=".085525"/> + <stop id="stop2862-5-9" style="stop-color:#b2b2b3" offset=".92166"/> + <stop id="stop4326-1-1" style="stop-color:#979798" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient2985" x1="10.014" x2="10.014" y1="44.96" y2="2.8765" gradientTransform="matrix(.31912 0 0 .29298 .34112 1.4648)" gradientUnits="userSpaceOnUse"> + <stop id="stop4334-7-6" style="stop-color:#595959" offset="0"/> + <stop id="stop4336-8-0" style="stop-color:#b3b3b3" offset="1"/> + </linearGradient> + </defs> + <metadata id="metadata2992"> + <rdf:RDF> + <cc:Work rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> + <dc:title/> + </cc:Work> + </rdf:RDF> + </metadata> + <g id="layer1"> + <rect id="rect2551-5-8" x=".50107" y="2.5011" width="14.998" height="11.998" rx="1" ry="1" style="fill-rule:evenodd;fill:url(#linearGradient2983);stroke-linecap:round;stroke-linejoin:round;stroke-width:1.0021;stroke:url(#linearGradient2985)"/> + <rect id="rect1314-3-3" x="2.5" y="4.5" width="11" height="8" rx="0" ry="0" style="color:#000000;fill:url(#radialGradient2900);stroke-linecap:round;stroke-linejoin:round;stroke:url(#linearGradient2902)"/> + <rect id="rect2221-3-8" x="1.501" y="3.5011" width="12.998" height="9.9979" rx="0" ry="0" style="fill:none;opacity:.4;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.0021;stroke:url(#linearGradient2897)"/> + <rect id="rect2556-8-5" x="1.5019" y="3.5019" width="12.996" height="9.9963" style="fill:none;opacity:.8;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.0037;stroke:url(#linearGradient2894)"/> + <path id="path3651-2" d="m9.0002 10v-0.75h-3v0.75h3z" style="fill:url(#linearGradient2890)"/> + <path id="path3653-7" d="m3.6862 9-0.68571-0.72727 1.5429-1.2727-1.5429-1.2727 0.68571-0.72727 2.3143 2-2.3143 2z" style="fill:url(#linearGradient2886)"/> + <path id="path3333-3-0" d="m1.6667 3c-0.36824 0-0.66667 0.31488-0.66667 0.70342v5.145c9.545e-4 0.043283 0.018837 0.084214 0.049602 0.11286 0.030769 0.028643 0.071495 0.04238 0.1123 0.037874l13.714-2.2911c0.070547-0.011738 0.12282-0.075385 0.12381-0.15073v-2.8539c0-0.38854-0.29843-0.70342-0.66667-0.70342h-12.667z" style="fill-rule:evenodd;fill:url(#linearGradient2883);opacity:.2"/> + <rect id="rect1314-3-3-9" x="3.5" y="5.5" width="9" height="6" rx="0" ry="0" style="color:#000000;fill:none;opacity:.1;stroke-linecap:round;stroke-linejoin:round;stroke:url(#linearGradient2880)"/> + </g> +</svg>