eric6/MicroPython/MicroPythonReplWidget.py

branch
micropython
changeset 7058
bdd583f96e96
parent 7054
fb84d8489bc1
child 7059
a8fad276cbd5
equal deleted inserted replaced
7054:fb84d8489bc1 7058:bdd583f96e96
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import re 12 import re
13 13
14 from PyQt5.QtCore import pyqtSlot, Qt, QPoint, QEvent 14 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QPoint, QEvent, QIODevice
15 from PyQt5.QtGui import QColor, QKeySequence, QTextCursor 15 from PyQt5.QtGui import QColor, QKeySequence, QTextCursor
16 from PyQt5.QtWidgets import ( 16 from PyQt5.QtWidgets import (
17 QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy) 17 QWidget, QMenu, QApplication, QHBoxLayout, QSpacerItem, QSizePolicy)
18 try: 18 try:
19 from PyQt5.QtSerialPort import QSerialPortInfo # __IGNORE_WARNING__ 19 from PyQt5.QtSerialPort import QSerialPort
20 HAS_QTSERIALPORT = True 20 HAS_QTSERIALPORT = True
21 except ImportError: 21 except ImportError:
22 HAS_QTSERIALPORT = False 22 HAS_QTSERIALPORT = False
23 23
24 from E5Gui.E5ZoomWidget import E5ZoomWidget 24 from E5Gui.E5ZoomWidget import E5ZoomWidget
25 from E5Gui import E5MessageBox
25 26
26 from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget 27 from .Ui_MicroPythonReplWidget import Ui_MicroPythonReplWidget
27 28
28 from . import MicroPythonDevices 29 from . import MicroPythonDevices
29 30
32 33
33 34
34 class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget): 35 class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget):
35 """ 36 """
36 Class implementing the MicroPython REPL widget. 37 Class implementing the MicroPython REPL widget.
38
39 @signal dataReceived(data) emitted to send data received via the serial
40 connection for further processing
37 """ 41 """
38 ZoomMin = -10 42 ZoomMin = -10
39 ZoomMax = 20 43 ZoomMax = 20
44
45 DeviceTypeRole = Qt.UserRole
46 DevicePortRole = Qt.UserRole + 1
47
48 dataReceived = pyqtSignal(bytes)
40 49
41 def __init__(self, parent=None): 50 def __init__(self, parent=None):
42 """ 51 """
43 Constructor 52 Constructor
44 53
78 87
79 self.__serial = None 88 self.__serial = None
80 self.__device = None 89 self.__device = None
81 self.setConnected(False) 90 self.setConnected(False)
82 91
92 self.__replRunning = False
93 self.__plotterRunning = False
94 self.__fileManagerRunning = False
95
83 if not HAS_QTSERIALPORT: 96 if not HAS_QTSERIALPORT:
84 self.replEdit.setHtml(self.tr( 97 self.replEdit.setHtml(self.tr(
85 "<h3>The QtSerialPort package is not available.<br/>" 98 "<h3>The QtSerialPort package is not available.<br/>"
86 "MicroPython support is deactivated.</h3>")) 99 "MicroPython support is deactivated.</h3>"))
87 self.setEnabled(False) 100 self.setEnabled(False)
111 self.deviceTypeComboBox.addItem("", "") 124 self.deviceTypeComboBox.addItem("", "")
112 devices = MicroPythonDevices.getFoundDevices() 125 devices = MicroPythonDevices.getFoundDevices()
113 if devices: 126 if devices:
114 self.deviceInfoLabel.setText( 127 self.deviceInfoLabel.setText(
115 self.tr("%n supported device(s) detected.", n=len(devices))) 128 self.tr("%n supported device(s) detected.", n=len(devices)))
129
130 index = 0
116 for device in sorted(devices): 131 for device in sorted(devices):
132 index += 1
117 self.deviceTypeComboBox.addItem( 133 self.deviceTypeComboBox.addItem(
118 self.tr("{0} at {1}".format(device[1], device[2])), 134 self.tr("{0} at {1}".format(device[1], device[2])))
119 device[0]) 135 self.deviceTypeComboBox.setItemData(
136 index, device[0], self.DeviceTypeRole)
137 self.deviceTypeComboBox.setItemData(
138 index, device[2], self.DevicePortRole)
139
120 else: 140 else:
121 self.deviceInfoLabel.setText( 141 self.deviceInfoLabel.setText(
122 self.tr("No supported devices detected.")) 142 self.tr("No supported devices detected."))
123 143
124 self.on_deviceTypeComboBox_activated(0) 144 self.on_deviceTypeComboBox_activated(0)
129 Private slot handling the selection of a device type. 149 Private slot handling the selection of a device type.
130 150
131 @param index index of the selected device 151 @param index index of the selected device
132 @type int 152 @type int
133 """ 153 """
134 deviceType = self.deviceTypeComboBox.itemData(index) 154 deviceType = self.deviceTypeComboBox.itemData(
155 index, self.DeviceTypeRole)
135 self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon( 156 self.deviceIconLabel.setPixmap(MicroPythonDevices.getDeviceIcon(
136 deviceType, False)) 157 deviceType, False))
137 158
138 self.__device = MicroPythonDevices.getDevice(deviceType) 159 self.__device = MicroPythonDevices.getDevice(deviceType, self)
139 if self.__device: 160 self.__device.setButtons()
140 actions = self.__device.supportedActions()
141 else:
142 actions = tuple()
143 self.__setActionButtons(actions)
144 161
145 @pyqtSlot() 162 @pyqtSlot()
146 def on_checkButton_clicked(self): 163 def on_checkButton_clicked(self):
147 """ 164 """
148 Private slot to check for connected devices. 165 Private slot to check for connected devices.
149 """ 166 """
150 self.__populateDeviceTypeComboBox() 167 self.__populateDeviceTypeComboBox()
151 168
152 def __setActionButtons(self, actions): 169 def setActionButtons(self, **kwargs):
153 """ 170 """
154 Private method to set the enabled state of the various action buttons. 171 Public method to set the enabled state of the various action buttons.
155 172
156 @param actions tuple of supported actions out of "repl", "run", 173 @param kwargs keyword arguments containg the enabled states (keys are
157 "files", "chart" 174 'run', 'repl', 'files', 'chart'
158 @type tuple of str 175 @type dict
159 """ 176 """
160 self.runButton.setEnabled("run" in actions) 177 if "run" in kwargs:
161 self.replButton.setEnabled("repl" in actions) 178 self.runButton.setEnabled(kwargs["run"])
162 self.filesButton.setEnabled("files" in actions) 179 if "repl" in kwargs:
163 self.chartButton.setEnabled("chart" in actions) 180 self.replButton.setEnabled(kwargs["repl"])
181 if "files" in kwargs:
182 self.filesButton.setEnabled(kwargs["files"])
183 if "chart" in kwargs:
184 self.chartButton.setEnabled(kwargs["chart"])
164 185
165 @pyqtSlot(QPoint) 186 @pyqtSlot(QPoint)
166 def __showContextMenu(self, pos): 187 def __showContextMenu(self, pos):
167 """ 188 """
168 Privat slot to show the REPL context menu. 189 Privat slot to show the REPL context menu.
200 @pyqtSlot() 221 @pyqtSlot()
201 def on_replButton_clicked(self): 222 def on_replButton_clicked(self):
202 """ 223 """
203 Private slot to connect to the selected device and start a REPL. 224 Private slot to connect to the selected device and start a REPL.
204 """ 225 """
205 # TODO: not implemented yet 226 if not self.__device:
206 raise NotImplementedError 227 E5MessageBox.critical(
228 self,
229 self.tr("No device attached"),
230 self.tr("""Please ensure the device is plugged inti your"""
231 """ computer.\n\nIt must have a version of"""
232 """ MicroPython (or CircuitPython) flashed onto it"""
233 """ before the REPL will work.\n\nFinally press the"""
234 """ device's reset button and wait a few seconds"""
235 """ before trying again."""))
236 return
237
238 if self.__replRunning:
239 self.dataReceived.disconnect(self.__processData)
240 self.on_disconnectButton_clicked()
241 self.__replRunning = False
242 self.__device.setRepl(False)
243 else:
244 ok, reason = self.__device.canStartRepl()
245 if not ok:
246 E5MessageBox.warning(
247 self,
248 self.tr("Start REPL"),
249 self.tr("""<p>The REPL cannot be started.</p><p>Reason:"""
250 """ {0}</p>""").format(reason))
251 return
252
253 if not self.__serial:
254 self.replEdit.clear()
255 self.dataReceived.connect(self.__processData)
256 self.__openSerialLink()
257 if self.__serial:
258 if self.__device.forceInterrupt():
259 # send a Ctrl-B (exit raw mode)
260 self.__serial.write(b'\x02')
261 # send Ctrl-C (keyboard interrupt)
262 self.__serial.write(b'\x03')
207 263
208 @pyqtSlot() 264 @pyqtSlot()
209 def on_disconnectButton_clicked(self): 265 def on_disconnectButton_clicked(self):
210 """ 266 """
211 Private slot to disconnect from the currently connected device. 267 Private slot to disconnect from the currently connected device.
212 """ 268 """
213 # TODO: not implemented yet 269 if self.__replRunning:
214 raise NotImplementedError 270 self.on_replButton_clicked()
271
272 # TODO: add more
273
274 def __disconnect(self):
275 """
276 Private slot to disconnect the serial connection.
277 """
278 self.__closeSerialLink()
279 self.setConnected(False)
215 280
216 def __activatePlotter(self): 281 def __activatePlotter(self):
217 """ 282 """
218 Private method to activate a data plotter widget. 283 Private method to activate a data plotter widget.
219 """ 284 """
369 if value < self.__currentZoom: 434 if value < self.__currentZoom:
370 self.replEdit.zoomOut(self.__currentZoom - value) 435 self.replEdit.zoomOut(self.__currentZoom - value)
371 elif value > self.__currentZoom: 436 elif value > self.__currentZoom:
372 self.replEdit.zoomIn(value - self.__currentZoom) 437 self.replEdit.zoomIn(value - self.__currentZoom)
373 self.__currentZoom = value 438 self.__currentZoom = value
439
440 def __getCurrentPort(self):
441 """
442 Private method to determine the port path of the selected device.
443
444 @return path of the port of the selected device
445 @rtype str
446 """
447 portName = self.deviceTypeComboBox.itemData(
448 self.deviceTypeComboBox.currentIndex(),
449 self.DevicePortRole)
450
451 if Globals.isWindowsPlatform():
452 # return unchanged
453 return portName
454 else:
455 # return with device path prepended
456 return "/dev/{0}".format(portName)
457
458 def __openSerialLink(self):
459 """
460 Private method to open a serial link to the selected device.
461 """
462 port = self.__getCurrentPort()
463 self.__serial = QSerialPort()
464 self.__serial.setPortName(port)
465 if self.__serial.open(QIODevice.ReadWrite):
466 self.__serial.setDataTerminalReady(True)
467 # 115.200 baud, 8N1
468 self.__serial.setBaudRate(115200)
469 self.__serial.setDataBits(QSerialPort.Data8)
470 self.__serial.setParity(QSerialPort.NoParity)
471 self.__serial.setStopBits(QSerialPort.OneStop)
472 self.__serial.readyRead.connect(self.__readSerial)
473 else:
474 E5MessageBox.warning(
475 self,
476 self.tr("Serial Device Connect"),
477 self.tr("""<p>Cannot connect to device at serial port"""
478 """ <b>{0}</b>.</p>""").format(port))
479 self.__serial = None
480
481 def __closeSerialLink(self):
482 """
483 Private method to close the open serial connection.
484 """
485 if self.__serial:
486 self.__serial.close()
487 self.__serial = None
488
489 @pyqtSlot()
490 def __readSerial(self):
491 """
492 Private slot to read all available serial data and emit it with the
493 "dataReceived" signal for further processing.
494 """
495 data = bytes(self.__serial.readAll())
496 self.dataReceived.emit(data)

eric ide

mercurial