eric6/MicroPython/MicroPythonReplWidget.py

branch
micropython
changeset 7103
aea236dc8002
parent 7095
8e10acb1cd85
child 7108
4f6133a01c6a
equal deleted inserted replaced
7102:5e77aa4671e6 7103:aea236dc8002
12 import re 12 import re
13 13
14 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QPoint, QEvent 14 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QPoint, QEvent
15 from PyQt5.QtGui import QColor, QKeySequence, QTextCursor, QBrush 15 from PyQt5.QtGui import QColor, QKeySequence, QTextCursor, QBrush
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 QTextEdit
18 ) 19 )
19 20
20 from E5Gui.E5ZoomWidget import E5ZoomWidget 21 from E5Gui.E5ZoomWidget import E5ZoomWidget
21 from E5Gui import E5MessageBox, E5FileDialog 22 from E5Gui import E5MessageBox, E5FileDialog
22 from E5Gui.E5Application import e5App 23 from E5Gui.E5Application import e5App
133 17: QBrush(QColor(255, 255, 255)), 134 17: QBrush(QColor(255, 255, 255)),
134 }, 135 },
135 } 136 }
136 137
137 138
138 # TODO: make wrapping an option for the repl edit
139 # TODO: add a connect button or make the disconnect button with changing icon (see IRC)
140 class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget): 139 class MicroPythonReplWidget(QWidget, Ui_MicroPythonReplWidget):
141 """ 140 """
142 Class implementing the MicroPython REPL widget. 141 Class implementing the MicroPython REPL widget.
143 142
144 @signal dataReceived(data) emitted to send data received via the serial 143 @signal dataReceived(data) emitted to send data received via the serial
173 self.checkButton.setIcon(UI.PixmapCache.getIcon("question")) 172 self.checkButton.setIcon(UI.PixmapCache.getIcon("question"))
174 self.runButton.setIcon(UI.PixmapCache.getIcon("start")) 173 self.runButton.setIcon(UI.PixmapCache.getIcon("start"))
175 self.replButton.setIcon(UI.PixmapCache.getIcon("terminal")) 174 self.replButton.setIcon(UI.PixmapCache.getIcon("terminal"))
176 self.filesButton.setIcon(UI.PixmapCache.getIcon("filemanager")) 175 self.filesButton.setIcon(UI.PixmapCache.getIcon("filemanager"))
177 self.chartButton.setIcon(UI.PixmapCache.getIcon("chart")) 176 self.chartButton.setIcon(UI.PixmapCache.getIcon("chart"))
178 self.disconnectButton.setIcon(UI.PixmapCache.getIcon("disconnect")) 177 self.connectButton.setIcon(UI.PixmapCache.getIcon("linkConnect"))
179 178
180 self.__zoomLayout = QHBoxLayout() 179 self.__zoomLayout = QHBoxLayout()
181 spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, 180 spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
182 QSizePolicy.Minimum) 181 QSizePolicy.Minimum)
183 self.__zoomLayout.addSpacerItem(spacerItem) 182 self.__zoomLayout.addSpacerItem(spacerItem)
201 self.__interface = MicroPythonCommandsInterface(self) 200 self.__interface = MicroPythonCommandsInterface(self)
202 self.__device = None 201 self.__device = None
203 self.__connected = False 202 self.__connected = False
204 self.setConnected(False) 203 self.setConnected(False)
205 204
206 # TODO: replace these by checking the button states
207 self.__replRunning = False
208 self.__plotterRunning = False
209 self.__fileManagerRunning = False
210
211 if not HAS_QTSERIALPORT: 205 if not HAS_QTSERIALPORT:
212 self.replEdit.setHtml(self.tr( 206 self.replEdit.setHtml(self.tr(
213 "<h3>The QtSerialPort package is not available.<br/>" 207 "<h3>The QtSerialPort package is not available.<br/>"
214 "MicroPython support is deactivated.</h3>")) 208 "MicroPython support is deactivated.</h3>"))
215 self.setEnabled(False) 209 self.setEnabled(False)
268 Private slot to handle a change in preferences. 262 Private slot to handle a change in preferences.
269 """ 263 """
270 self.__colorScheme = Preferences.getMicroPython("ColorScheme") 264 self.__colorScheme = Preferences.getMicroPython("ColorScheme")
271 265
272 self.__font = Preferences.getEditorOtherFonts("MonospacedFont") 266 self.__font = Preferences.getEditorOtherFonts("MonospacedFont")
273
274 self.replEdit.setFontFamily(self.__font.family()) 267 self.replEdit.setFontFamily(self.__font.family())
275 self.replEdit.setFontPointSize(self.__font.pointSize()) 268 self.replEdit.setFontPointSize(self.__font.pointSize())
269
270 if Preferences.getMicroPython("ReplLineWrap"):
271 self.replEdit.setLineWrapMode(QTextEdit.WidgetWidth)
272 else:
273 self.replEdit.setLineWrapMode(QTextEdit.NoWrap)
276 274
277 def commandsInterface(self): 275 def commandsInterface(self):
278 """ 276 """
279 Public method to get a reference to the commands interface object. 277 Public method to get a reference to the commands interface object.
280 278
365 if self.__fileManagerWidget: 363 if self.__fileManagerWidget:
366 self.__fileManagerWidget.deviceConnectedLed.setOn(connected) 364 self.__fileManagerWidget.deviceConnectedLed.setOn(connected)
367 365
368 self.deviceTypeComboBox.setEnabled(not connected) 366 self.deviceTypeComboBox.setEnabled(not connected)
369 367
370 self.disconnectButton.setEnabled(connected) 368 if connected:
369 self.connectButton.setIcon(
370 UI.PixmapCache.getIcon("linkDisconnect"))
371 self.connectButton.setToolTip(self.tr(
372 "Press to disconnect the current device"))
373 else:
374 self.connectButton.setIcon(
375 UI.PixmapCache.getIcon("linkConnect"))
376 self.connectButton.setToolTip(self.tr(
377 "Press to connect the selected device"))
371 378
372 def __showNoDeviceMessage(self): 379 def __showNoDeviceMessage(self):
373 """ 380 """
374 Private method to show a message dialog indicating a missing device. 381 Private method to show a message dialog indicating a missing device.
375 """ 382 """
384 """ before trying again.""")) 391 """ before trying again."""))
385 392
386 @pyqtSlot(bool) 393 @pyqtSlot(bool)
387 def on_replButton_clicked(self, checked): 394 def on_replButton_clicked(self, checked):
388 """ 395 """
389 Private slot to connect to enable or disable the REPL widget connecting 396 Private slot to connect to enable or disable the REPL widget.
390 or disconnecting from the device. 397
398 If the selected device is not connected yet, this will be done now.
391 399
392 @param checked state of the button 400 @param checked state of the button
393 @type bool 401 @type bool
394 """ 402 """
395 if not self.__device: 403 if not self.__device:
415 # send a Ctrl-B (exit raw mode) 423 # send a Ctrl-B (exit raw mode)
416 self.__interface.write(b'\x02') 424 self.__interface.write(b'\x02')
417 # send Ctrl-C (keyboard interrupt) 425 # send Ctrl-C (keyboard interrupt)
418 self.__interface.write(b'\x03') 426 self.__interface.write(b'\x03')
419 427
420 self.__replRunning = True
421 self.__device.setRepl(True) 428 self.__device.setRepl(True)
422 self.replEdit.setFocus(Qt.OtherFocusReason) 429 self.replEdit.setFocus(Qt.OtherFocusReason)
423 else: 430 else:
424 self.__interface.dataReceived.disconnect(self.__processData) 431 self.__interface.dataReceived.disconnect(self.__processData)
425 if not self.__plotterRunning and not self.__fileManagerRunning: 432 if (not self.chartButton.isChecked() and
433 not self.filesButton.isChecked()):
426 self.__disconnectFromDevice() 434 self.__disconnectFromDevice()
427 self.__replRunning = False
428 self.__device.setRepl(False) 435 self.__device.setRepl(False)
429 self.replButton.setChecked(checked) 436 self.replButton.setChecked(checked)
430 437
431 @pyqtSlot() 438 @pyqtSlot()
432 def on_disconnectButton_clicked(self): 439 def on_connectButton_clicked(self):
433 """ 440 """
434 Private slot to disconnect from the currently connected device. 441 Private slot to connect to the selected device or disconnect from the
435 """ 442 currently connected device.
436 self.__disconnectFromDevice() 443 """
444 if self.__connected:
445 self.__disconnectFromDevice()
446 else:
447 self.__connectToDevice()
437 448
438 @pyqtSlot() 449 @pyqtSlot()
439 def __clear(self): 450 def __clear(self):
440 """ 451 """
441 Private slot to clear the REPL pane. 452 Private slot to clear the REPL pane.
779 @pyqtSlot() 790 @pyqtSlot()
780 def on_runButton_clicked(self): 791 def on_runButton_clicked(self):
781 """ 792 """
782 Private slot to execute the script of the active editor on the 793 Private slot to execute the script of the active editor on the
783 selected device. 794 selected device.
795
796 If the REPL is not active yet, it will be activated, which might cause
797 an unconnected device to be connected.
784 """ 798 """
785 if not self.__device: 799 if not self.__device:
786 self.__showNoDeviceMessage() 800 self.__showNoDeviceMessage()
787 return 801 return
788 802
810 self.tr("Run Script"), 824 self.tr("Run Script"),
811 self.tr("""<p>Cannot run script.</p><p>Reason:""" 825 self.tr("""<p>Cannot run script.</p><p>Reason:"""
812 """ {0}</p>""").format(reason)) 826 """ {0}</p>""").format(reason))
813 return 827 return
814 828
815 if not self.__replRunning: 829 if not self.replButton.isChecked():
816 # switch on the REPL 830 # activate on the REPL
817 self.on_replButton_clicked(True) 831 self.on_replButton_clicked(True)
818 if self.__replRunning: 832 if self.replButton.isChecked():
819 self.__device.runScript(script) 833 self.__device.runScript(script)
820 834
821 @pyqtSlot() 835 @pyqtSlot()
822 def on_openButton_clicked(self): 836 def on_openButton_clicked(self):
823 """ 837 """
853 @pyqtSlot(bool) 867 @pyqtSlot(bool)
854 def on_chartButton_clicked(self, checked): 868 def on_chartButton_clicked(self, checked):
855 """ 869 """
856 Private slot to open a chart view to plot data received from the 870 Private slot to open a chart view to plot data received from the
857 connected device. 871 connected device.
872
873 If the selected device is not connected yet, this will be done now.
858 874
859 @param checked state of the button 875 @param checked state of the button
860 @type bool 876 @type bool
861 """ 877 """
862 if not HAS_QTCHART: 878 if not HAS_QTCHART:
894 # send a Ctrl-B (exit raw mode) 910 # send a Ctrl-B (exit raw mode)
895 self.__interface.write(b'\x02') 911 self.__interface.write(b'\x02')
896 # send Ctrl-C (keyboard interrupt) 912 # send Ctrl-C (keyboard interrupt)
897 self.__interface.write(b'\x03') 913 self.__interface.write(b'\x03')
898 914
899 self.__plotterRunning = True
900 self.__device.setPlotter(True) 915 self.__device.setPlotter(True)
901 else: 916 else:
902 if self.__chartWidget.isDirty(): 917 if self.__chartWidget.isDirty():
903 res = E5MessageBox.okToClearData( 918 res = E5MessageBox.okToClearData(
904 self, 919 self,
912 self.__interface.dataReceived.disconnect( 927 self.__interface.dataReceived.disconnect(
913 self.__chartWidget.processData) 928 self.__chartWidget.processData)
914 self.__chartWidget.dataFlood.disconnect( 929 self.__chartWidget.dataFlood.disconnect(
915 self.handleDataFlood) 930 self.handleDataFlood)
916 931
917 if not self.__replRunning and not self.__fileManagerRunning: 932 if (not self.replButton.isChecked() and
933 not self.filesButton.isChecked()):
918 self.__disconnectFromDevice() 934 self.__disconnectFromDevice()
919 935
920 self.__plotterRunning = False
921 self.__device.setPlotter(False) 936 self.__device.setPlotter(False)
922 self.__ui.removeSideWidget(self.__chartWidget) 937 self.__ui.removeSideWidget(self.__chartWidget)
923 938
924 self.__chartWidget.deleteLater() 939 self.__chartWidget.deleteLater()
925 self.__chartWidget = None 940 self.__chartWidget = None
929 @pyqtSlot() 944 @pyqtSlot()
930 def handleDataFlood(self): 945 def handleDataFlood(self):
931 """ 946 """
932 Public slot handling a data flood from the device. 947 Public slot handling a data flood from the device.
933 """ 948 """
934 self.on_disconnectButton_clicked() 949 self.on_connectButton_clicked()
935 self.__device.handleDataFlood() 950 self.__device.handleDataFlood()
936 951
937 @pyqtSlot(bool) 952 @pyqtSlot(bool)
938 def on_filesButton_clicked(self, checked): 953 def on_filesButton_clicked(self, checked):
939 """ 954 """
940 Private slot to open a file manager window to the connected device. 955 Private slot to open a file manager window to the connected device.
956
957 If the selected device is not connected yet, this will be done now.
941 958
942 @param checked state of the button 959 @param checked state of the button
943 @type bool 960 @type bool
944 """ 961 """
945 if not self.__device: 962 if not self.__device:
966 UI.PixmapCache.getIcon("filemanager"), 983 UI.PixmapCache.getIcon("filemanager"),
967 self.tr("μPy Files")) 984 self.tr("μPy Files"))
968 self.__ui.showSideWidget(self.__fileManagerWidget) 985 self.__ui.showSideWidget(self.__fileManagerWidget)
969 986
970 self.__device.setFileManager(True) 987 self.__device.setFileManager(True)
971 self.__fileManagerRunning = True
972 988
973 self.__fileManagerWidget.start() 989 self.__fileManagerWidget.start()
974 else: 990 else:
975 self.__fileManagerWidget.stop() 991 self.__fileManagerWidget.stop()
992
993 if (not self.replButton.isChecked() and
994 not self.chartButton.isChecked()):
995 self.__disconnectFromDevice()
996
997 self.__device.setFileManager(False)
976 self.__ui.removeSideWidget(self.__fileManagerWidget) 998 self.__ui.removeSideWidget(self.__fileManagerWidget)
977 999
978 self.__device.setFileManager(False)
979 self.__fileManagerRunning = False
980 self.__fileManagerWidget.deleteLater() 1000 self.__fileManagerWidget.deleteLater()
981 self.__fileManagerWidget = None 1001 self.__fileManagerWidget = None

eric ide

mercurial