# -*- coding: utf-8 -*- import threading from PyQt4 import QtCore, QtGui from serial import Serial, SerialException from colourterm import SettingsDialog, ConnectDialog, SComboBox, Highlight, fromUtf8, translate, create_default_highlights from colourterm.cwebview import CWebView class Ui_MainWindow(object): def setupUi(self, mainWindow): mainWindow.setObjectName(fromUtf8('MainWindow')) mainWindow.resize(800, 600) self.centralwidget = QtGui.QWidget(mainWindow) self.centralwidget.setObjectName(fromUtf8('centralwidget')) self.centralLayout = QtGui.QVBoxLayout(self.centralwidget) self.centralLayout.setSpacing(8) self.centralLayout.setContentsMargins(0, 0, 0, 8) self.centralLayout.setObjectName(fromUtf8('centralLayout')) self.outputBrowser = CWebView(self.centralwidget) self.outputBrowser.setHtml('
' % str(QtGui.QApplication.palette().color(QtGui.QPalette.Text).name())) self.outputBrowser.setObjectName(fromUtf8('outputBrowser')) self.centralLayout.addWidget(self.outputBrowser) self.sendLayout = QtGui.QHBoxLayout() self.sendLayout.setSpacing(8) self.sendLayout.setObjectName(fromUtf8('sendLayout')) self.sendComboBox = SComboBox(self.centralwidget) self.sendComboBox.setEditable(True) self.sendComboBox.setEnabled(False) self.sendComboBox.setObjectName(fromUtf8('sendComboBox')) self.sendLayout.addWidget(self.sendComboBox) self.sendButton = QtGui.QPushButton(self.centralwidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sendButton.sizePolicy().hasHeightForWidth()) self.sendButton.setSizePolicy(sizePolicy) self.sendButton.setMaximumSize(QtCore.QSize(100, 16777215)) self.sendButton.setObjectName(fromUtf8('sendButton')) self.sendLayout.addWidget(self.sendButton) self.centralLayout.addLayout(self.sendLayout) mainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtGui.QStatusBar(mainWindow) self.statusbar.setObjectName(fromUtf8('statusbar')) mainWindow.setStatusBar(self.statusbar) self.toolBar = QtGui.QToolBar(mainWindow) self.toolBar.setMovable(False) self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.toolBar.setFloatable(False) self.toolBar.setObjectName(fromUtf8('toolBar')) mainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) self.openAction = QtGui.QAction(mainWindow) connectIcon = QtGui.QIcon() connectIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/network-connect.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.openAction.setIcon(connectIcon) self.openAction.setObjectName(fromUtf8('openAction')) self.closeAction = QtGui.QAction(mainWindow) disconnectIcon = QtGui.QIcon() disconnectIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/network-disconnect.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.closeAction.setIcon(disconnectIcon) self.closeAction.setObjectName(fromUtf8('closeAction')) self.captureAction = QtGui.QAction(mainWindow) captureIcon = QtGui.QIcon() captureIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/capture-to-disk.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.captureAction.setIcon(captureIcon) self.captureAction.setCheckable(True) self.captureAction.setChecked(False) self.captureAction.setObjectName(fromUtf8('captureAction')) self.followAction = QtGui.QAction(mainWindow) self.followAction.setShortcut(QtCore.Qt.Key_F) followIcon = QtGui.QIcon() followIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/follow-output.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.followAction.setIcon(followIcon) self.followAction.setCheckable(True) self.followAction.setChecked(True) self.followAction.setObjectName(fromUtf8('followAction')) self.configureAction = QtGui.QAction(mainWindow) configureIcon = QtGui.QIcon() configureIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/configure.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.configureAction.setIcon(configureIcon) self.configureAction.setObjectName(fromUtf8('configureAction')) self.exitAction = QtGui.QAction(mainWindow) exitIcon = QtGui.QIcon() exitIcon.addPixmap(QtGui.QPixmap(fromUtf8(':/toolbar/application-exit.png')), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.exitAction.setIcon(exitIcon) self.exitAction.setObjectName(fromUtf8('exitAction')) self.toolBar.addAction(self.openAction) self.toolBar.addAction(self.closeAction) self.toolBar.addAction(self.captureAction) self.toolBar.addAction(self.followAction) self.toolBar.addAction(self.configureAction) self.toolBar.addAction(self.exitAction) self.retranslateUi(mainWindow) QtCore.QMetaObject.connectSlotsByName(mainWindow) def retranslateUi(self, mainWindow): mainWindow.setWindowTitle(translate('MainWindow', 'ColourTerm')) self.sendButton.setText(translate('MainWindow', 'Send')) self.toolBar.setWindowTitle(translate('MainWindow', 'toolBar')) self.openAction.setText(translate('MainWindow', 'Open...')) self.openAction.setToolTip(translate('MainWindow', 'Open...')) self.closeAction.setText(translate('MainWindow', 'Close')) self.closeAction.setToolTip(translate('MainWindow', 'Close')) self.captureAction.setText(translate('MainWindow', 'Capture')) self.captureAction.setToolTip(translate('MainWindow', 'Capture to File')) self.followAction.setText(translate('MainWindow', '&Follow')) self.configureAction.setText(translate('MainWindow', 'Configure...')) self.configureAction.setToolTip(translate('MainWindow', 'Configure...')) self.exitAction.setText(translate('MainWindow', 'Exit')) class MainWindow(QtGui.QMainWindow, Ui_MainWindow): updateOutput = QtCore.pyqtSignal(str) def __init__(self): QtGui.QMainWindow.__init__(self) self.terminalLines = [] self.maxLines = 5000 self.setupUi(self) self.device = None self.deviceClosed = True self.followOutput = True self.captureFile = None self.captureFileName = u'' self.highlights = self.loadHighlights() if not self.highlights: self.highlights = create_default_highlights() self.settingsDialog = SettingsDialog() self.connectDialog = ConnectDialog() self.openAction.triggered.connect(self.onOpenActionTriggered) self.closeAction.triggered.connect(self.onCloseActionTriggered) self.captureAction.toggled.connect(self.onCaptureActionToggled) self.followAction.toggled.connect(self.onFollowActionToggled) self.configureAction.triggered.connect(self.onConfigureActionTriggered) self.exitAction.triggered.connect(self.close) self.sendComboBox.keyPressed.connect(self.onSendComboBoxKeyPressed) self.sendButton.clicked.connect(self.onSendButtonClicked) self.outputBrowser.page().mainFrame().contentsSizeChanged.connect(self.onContentsSizeChanged) self.outputBrowser.onScroll.connect(self.onOutputBrowserScrolled) self.updateOutput.connect(self.onUpdateOutput) def close(self): if not self.deviceClosed: self.deviceClosed = True if self.captureFile: self.captureFile.flush() self.captureFile.close() return QtGui.QMainWindow.close(self) def documentBody(self): return self.outputBrowser.page().mainFrame().documentElement().findFirst('pre') def receiveText(self): output = '' while not self.deviceClosed: output += self.device.read(1) if output.endswith('\r\n'): #self.terminalLines.append(output.strip('\r\n')) #if len(self.terminalLines) > self.maxLines: # self.terminalLines = self.terminalLines[-self.maxLines:] # self.refreshOutput() #else: self.updateOutput.emit(output.strip('\r\n')) output = '' def onOpenActionTriggered(self): if self.connectDialog.exec_() == QtGui.QDialog.Accepted: if not self.deviceClosed: self.deviceClosed = True self.device.close() try: self.device = Serial( port=self.connectDialog.getPort(), baudrate=self.connectDialog.getBaud(), bytesize=self.connectDialog.getDataBits(), parity=self.connectDialog.getParity(), stopbits=self.connectDialog.getStopBits(), timeout=0, xonxoff=self.connectDialog.getSoftwareHandshake(), rtscts=self.connectDialog.getHardwareHandshake(), dsrdtr=None ) self.deviceClosed = False if not self.device.isOpen(): self.device.open() outputThread = threading.Thread(target=self.receiveText) outputThread.start() except SerialException, e: QtGui.QMessageBox.critical(self, 'Error opening port', e.args[0]) self.sendComboBox.setEnabled(not self.deviceClosed) if self.sendComboBox.isEnabled(): self.sendComboBox.setFocus() def onCloseActionTriggered(self): self.deviceClosed = True if self.device.isOpen(): self.device.close() self.sendComboBox.setEnabled(not self.deviceClosed) def onCaptureActionToggled(self, enabled): if enabled and not self.captureFile: if self.captureFileName: baseDir = os.path.basename(self.captureFileName) else: baseDir = u'' self.captureFileName = QtGui.QFileDialog.getSaveFileName(self, u'Capture To File', baseDir, u'Text files (*.txt *.log);;All files (*)') self.captureFile = open(self.captureFileName, u'w') self.statusbar.showMessage(self.captureFileName) elif self.captureFile and not enabled: self.captureFileName = u'' self.captureFile.flush() self.captureFile.close() self.captureFile = None self.statusbar.clearMessage() def onFollowActionToggled(self, enabled): self.followOutput = enabled if enabled: self.outputBrowser.page().mainFrame().scroll(0, self.outputBrowser.page().mainFrame().contentsSize().height()) def onConfigureActionTriggered(self): self.settingsDialog.setHighlights(self.highlights) self.settingsDialog.exec_() self.highlights = self.settingsDialog.highlights() self.saveHighlights(self.highlights) self.refreshOutput() def onSendComboBoxKeyPressed(self, key): if key == QtCore.Qt.Key_Return or key == QtCore.Qt.Key_Enter: self.onSendButtonClicked() def onSendButtonClicked(self): if self.device.isOpen(): output = str(self.sendComboBox.currentText()) self.sendComboBox.insertItem(0, output) self.sendComboBox.setCurrentIndex(0) self.sendComboBox.clearEditText() self.device.write(output + u'\r\n') def onContentsSizeChanged(self, size): if self.followOutput: self.outputBrowser.page().mainFrame().scroll(0, size.height()) self.outputBrowser.update() def onUpdateOutput(self, output): #self.terminalLines.append(output) if self.captureFile: self.captureFile.write(output + u'\n') self.captureFile.flush() #if len(self.terminalLines) > 5000: # self.terminalLines = self.terminalLines[-5000:] # self.refreshOutput() #else: output = self.styleOutput(output) self.documentBody().appendInside(output) def onOutputBrowserScrolled(self): scrollValue = self.outputBrowser.page().mainFrame().scrollBarValue(QtCore.Qt.Vertical) scrollMax = self.outputBrowser.page().mainFrame().scrollBarMaximum(QtCore.Qt.Vertical) if scrollValue < scrollMax: self.onFollowActionToggled(False) self.followAction.setChecked(False) else: self.onFollowActionToggled(True) self.followAction.setChecked(True) def refreshOutput(self): elements = self.outputBrowser.page().mainFrame().findAllElements('div') lines = [unicode(element.toPlainText()) for element in elements] pre = self.outputBrowser.page().mainFrame().findFirstElement('pre') pre.setInnerXml('') for line in lines: output = self.styleOutput(line) self.documentBody().appendInside(output) self.outputBrowser.page().mainFrame().scroll(0, self.outputBrowser.page().mainFrame().contentsSize().height()) self.outputBrowser.update() def styleOutput(self, output): style = u'font-family: Ubuntu Mono; ' if not output: output = u' ' for highlight in self.highlights: if highlight.regex.search(output): if highlight.foreground: style = u'%scolor: %s; ' % (style, highlight.foreground) if highlight.background: style = u'%sbackground-color: %s; ' % (style, highlight.background) break if style: try: output = u'