2013-12-05 14:16:09 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2013-12-10 06:29:31 +00:00
|
|
|
import os
|
2013-12-05 14:16:09 +00:00
|
|
|
import threading
|
2013-12-20 14:22:03 +00:00
|
|
|
from string import printable
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2014-04-04 14:04:10 +00:00
|
|
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
2015-03-13 08:06:15 +00:00
|
|
|
from serial import SerialException, serial_for_url
|
|
|
|
from select import error as SelectError
|
|
|
|
from socket import error as SocketError
|
|
|
|
import time
|
|
|
|
from os.path import getsize
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
from colourterm import SettingsDialog, ConnectDialog, SComboBox, Highlight, from_utf8, translate, \
|
2013-12-10 07:43:03 +00:00
|
|
|
create_default_highlights
|
2013-12-05 14:16:09 +00:00
|
|
|
from colourterm.cwebview import CWebView
|
2015-03-13 08:06:15 +00:00
|
|
|
from colourterm.xmodem import XMODEM1k, XMODEM
|
2013-12-05 14:16:09 +00:00
|
|
|
|
|
|
|
|
2013-12-20 14:22:03 +00:00
|
|
|
class MessageType(object):
|
|
|
|
"""
|
|
|
|
An enumeration for message types
|
|
|
|
"""
|
|
|
|
Info = 1
|
|
|
|
Question = 2
|
|
|
|
Warning = 3
|
|
|
|
Critical = 4
|
|
|
|
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
class UiMainWindow(object):
|
|
|
|
def __init__(self):
|
|
|
|
"""
|
|
|
|
Just to satisfy PEP8/PyLint
|
|
|
|
"""
|
|
|
|
self.central_widget = None
|
|
|
|
self.central_layout = None
|
|
|
|
self.output_browser = None
|
|
|
|
self.send_layout = None
|
|
|
|
self.send_combobox = None
|
|
|
|
self.send_button = None
|
|
|
|
self.status_bar = None
|
|
|
|
self.tool_bar = None
|
|
|
|
self.open_action = None
|
|
|
|
self.close_action = None
|
|
|
|
self.capture_action = None
|
2015-03-13 08:06:15 +00:00
|
|
|
self.xmodem_action = None
|
2013-12-10 12:55:20 +00:00
|
|
|
self.follow_action = None
|
|
|
|
self.configure_action = None
|
|
|
|
self.exit_action = None
|
2014-04-04 14:04:10 +00:00
|
|
|
self.clear_action = None
|
|
|
|
self.find_widget = None
|
|
|
|
self.find_layout = None
|
|
|
|
self.find_combobox = None
|
|
|
|
self.find_action = None
|
2013-12-10 12:55:20 +00:00
|
|
|
|
|
|
|
def setup_ui(self, main_window):
|
|
|
|
"""
|
|
|
|
Set up the user interface
|
|
|
|
"""
|
|
|
|
main_window.setObjectName(from_utf8('MainWindow'))
|
|
|
|
main_window.resize(800, 600)
|
2016-09-30 08:32:20 +00:00
|
|
|
main_window.setWindowIcon(QtGui.QIcon(':/icons/colourterm-icon.ico'))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.central_widget = QtGui.QWidget(main_window)
|
|
|
|
self.central_widget.setObjectName(from_utf8('central_widget'))
|
|
|
|
self.central_layout = QtGui.QVBoxLayout(self.central_widget)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.central_layout.setSpacing(0)
|
2014-04-10 06:43:56 +00:00
|
|
|
self.central_layout.setContentsMargins(0, 0, 0, 0)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.central_layout.setObjectName(from_utf8('central_layout'))
|
|
|
|
self.find_widget = QtGui.QWidget(main_window)
|
|
|
|
self.find_widget.setVisible(False)
|
|
|
|
self.find_widget.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)
|
|
|
|
self.find_widget.setObjectName(from_utf8('find_widget'))
|
|
|
|
self.find_layout = QtGui.QHBoxLayout(self.find_widget)
|
2014-04-10 06:43:56 +00:00
|
|
|
self.find_layout.setContentsMargins(0, 2, 0, 2)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.find_layout.setObjectName(from_utf8('find_layout'))
|
|
|
|
self.find_combobox = SComboBox(self.find_widget)
|
|
|
|
self.find_combobox.setEditable(True)
|
|
|
|
self.find_combobox.setEnabled(True)
|
|
|
|
self.find_combobox.setObjectName(from_utf8('find_combobox'))
|
|
|
|
self.find_layout.addWidget(self.find_combobox)
|
|
|
|
self.central_layout.addWidget(self.find_widget)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.output_browser = CWebView(self.central_widget)
|
|
|
|
self.output_browser.setHtml('<html><head><style>body { color: %s; font-family: monospace; margin: 0; '
|
|
|
|
'padding: 0; }</style></head><body><pre></pre></body></html>' %
|
|
|
|
str(QtGui.QApplication.palette().color(QtGui.QPalette.Text).name()))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.output_browser.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
|
|
|
|
self.output_browser.setObjectName(from_utf8('output_browser'))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.central_layout.addWidget(self.output_browser)
|
|
|
|
self.send_layout = QtGui.QHBoxLayout()
|
|
|
|
self.send_layout.setSpacing(8)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.send_layout.setContentsMargins(0, 4, 0, 0)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.send_layout.setObjectName(from_utf8('sendLayout'))
|
|
|
|
self.send_combobox = SComboBox(self.central_widget)
|
|
|
|
self.send_combobox.setEditable(True)
|
|
|
|
self.send_combobox.setEnabled(False)
|
|
|
|
self.send_combobox.setObjectName(from_utf8('sendComboBox'))
|
|
|
|
self.send_layout.addWidget(self.send_combobox)
|
|
|
|
self.send_button = QtGui.QPushButton(self.central_widget)
|
|
|
|
size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
|
|
|
|
size_policy.setHorizontalStretch(0)
|
|
|
|
size_policy.setVerticalStretch(0)
|
|
|
|
size_policy.setHeightForWidth(self.send_button.sizePolicy().hasHeightForWidth())
|
|
|
|
self.send_button.setSizePolicy(size_policy)
|
|
|
|
self.send_button.setMaximumSize(QtCore.QSize(100, 16777215))
|
2014-01-16 13:48:37 +00:00
|
|
|
self.send_button.setEnabled(False)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.send_button.setObjectName(from_utf8('sendButton'))
|
|
|
|
self.send_layout.addWidget(self.send_button)
|
|
|
|
self.central_layout.addLayout(self.send_layout)
|
|
|
|
main_window.setCentralWidget(self.central_widget)
|
|
|
|
self.status_bar = QtGui.QStatusBar(main_window)
|
|
|
|
self.status_bar.setObjectName(from_utf8('status_bar'))
|
|
|
|
main_window.setStatusBar(self.status_bar)
|
|
|
|
self.tool_bar = QtGui.QToolBar(main_window)
|
|
|
|
self.tool_bar.setMovable(False)
|
|
|
|
self.tool_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
|
|
|
|
self.tool_bar.setFloatable(False)
|
|
|
|
self.tool_bar.setObjectName(from_utf8('tool_bar'))
|
|
|
|
main_window.addToolBar(QtCore.Qt.TopToolBarArea, self.tool_bar)
|
|
|
|
self.open_action = QtGui.QAction(main_window)
|
|
|
|
connect_icon = QtGui.QIcon()
|
|
|
|
connect_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/network-connect.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.open_action.setIcon(connect_icon)
|
|
|
|
self.open_action.setObjectName(from_utf8('open_action'))
|
2014-01-16 13:48:37 +00:00
|
|
|
self.open_action.setShortcut(QtGui.QKeySequence.Open)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.close_action = QtGui.QAction(main_window)
|
|
|
|
disconnect_icon = QtGui.QIcon()
|
|
|
|
disconnect_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/network-disconnect.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.close_action.setIcon(disconnect_icon)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.close_action.setShortcut(QtGui.QKeySequence.Close)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.close_action.setObjectName(from_utf8('close_action'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.find_action = QtGui.QAction(main_window)
|
|
|
|
find_icon = QtGui.QIcon()
|
|
|
|
find_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/find.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.find_action.setIcon(find_icon)
|
|
|
|
self.find_action.setShortcut(QtGui.QKeySequence.Find)
|
|
|
|
self.find_action.setCheckable(True)
|
|
|
|
self.find_action.setChecked(False)
|
|
|
|
self.find_action.setObjectName(from_utf8('find_action'))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.capture_action = QtGui.QAction(main_window)
|
|
|
|
capture_icon = QtGui.QIcon()
|
|
|
|
capture_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/capture-to-disk.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.capture_action.setIcon(capture_icon)
|
|
|
|
self.capture_action.setCheckable(True)
|
|
|
|
self.capture_action.setChecked(False)
|
|
|
|
self.capture_action.setObjectName(from_utf8('capture_action'))
|
|
|
|
self.follow_action = QtGui.QAction(main_window)
|
2014-01-16 13:48:37 +00:00
|
|
|
self.follow_action.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.SHIFT + QtCore.Qt.Key_F))
|
2013-12-10 12:55:20 +00:00
|
|
|
follow_icon = QtGui.QIcon()
|
|
|
|
follow_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/follow-output.png')),
|
2013-12-10 07:43:03 +00:00
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.follow_action.setIcon(follow_icon)
|
|
|
|
self.follow_action.setCheckable(True)
|
|
|
|
self.follow_action.setChecked(True)
|
|
|
|
self.follow_action.setObjectName(from_utf8('follow_action'))
|
2014-01-16 13:48:37 +00:00
|
|
|
self.clear_action = QtGui.QAction(main_window)
|
|
|
|
clear_icon = QtGui.QIcon()
|
|
|
|
clear_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/clear.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.clear_action.setIcon(clear_icon)
|
|
|
|
self.clear_action.setObjectName(from_utf8('clear_action'))
|
|
|
|
self.clear_action.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Backspace))
|
2015-03-13 08:06:15 +00:00
|
|
|
|
|
|
|
self.xmodem_action = QtGui.QAction(main_window)
|
|
|
|
xmodem_icon = QtGui.QIcon()
|
|
|
|
xmodem_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/move-up.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.xmodem_action.setIcon(xmodem_icon)
|
|
|
|
self.xmodem_action.setObjectName(from_utf8('xmodem_action'))
|
|
|
|
self.xmodem_action.setShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.SHIFT + QtCore.Qt.Key_X))
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
self.configure_action = QtGui.QAction(main_window)
|
|
|
|
configure_icon = QtGui.QIcon()
|
|
|
|
configure_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/configure.png')),
|
2013-12-10 07:43:03 +00:00
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.configure_action.setIcon(configure_icon)
|
|
|
|
self.configure_action.setObjectName(from_utf8('configure_action'))
|
|
|
|
self.exit_action = QtGui.QAction(main_window)
|
|
|
|
exit_icon = QtGui.QIcon()
|
|
|
|
exit_icon.addPixmap(QtGui.QPixmap(from_utf8(':/toolbar/application-exit.png')),
|
|
|
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
|
|
self.exit_action.setIcon(exit_icon)
|
|
|
|
self.exit_action.setObjectName(from_utf8('exit_action'))
|
|
|
|
self.tool_bar.addAction(self.open_action)
|
|
|
|
self.tool_bar.addAction(self.close_action)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.tool_bar.addSeparator()
|
|
|
|
self.tool_bar.addAction(self.find_action)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.tool_bar.addAction(self.capture_action)
|
|
|
|
self.tool_bar.addAction(self.follow_action)
|
2014-01-16 13:48:37 +00:00
|
|
|
self.tool_bar.addAction(self.clear_action)
|
2015-03-13 08:06:15 +00:00
|
|
|
self.tool_bar.addAction(self.xmodem_action)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.tool_bar.addSeparator()
|
2013-12-10 12:55:20 +00:00
|
|
|
self.tool_bar.addAction(self.configure_action)
|
|
|
|
self.tool_bar.addAction(self.exit_action)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
self.retranslate_ui(main_window)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def retranslate_ui(self, main_window):
|
|
|
|
"""
|
|
|
|
Translate the user interface
|
|
|
|
"""
|
|
|
|
main_window.setWindowTitle(translate('MainWindow', 'ColourTerm'))
|
|
|
|
self.send_button.setText(translate('MainWindow', 'Send'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.tool_bar.setWindowTitle(translate('MainWindow', 'Tool Bar'))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.open_action.setText(translate('MainWindow', 'Open...'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.open_action.setToolTip(translate('MainWindow',
|
|
|
|
'Open (%s)' % QtGui.QKeySequence(QtGui.QKeySequence.Open).toString()))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.close_action.setText(translate('MainWindow', 'Close'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.close_action.setToolTip(translate('MainWindow',
|
|
|
|
'Close (%s)' % QtGui.QKeySequence(QtGui.QKeySequence.Close).toString()))
|
|
|
|
self.find_action.setText(translate('MainWindow', 'Find'))
|
|
|
|
self.find_action.setToolTip(translate('MainWindow',
|
|
|
|
'Find (%s)' % QtGui.QKeySequence(QtGui.QKeySequence.Find).toString()))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.capture_action.setText(translate('MainWindow', 'Capture'))
|
|
|
|
self.capture_action.setToolTip(translate('MainWindow', 'Capture to File'))
|
|
|
|
self.follow_action.setText(translate('MainWindow', '&Follow'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.follow_action.setToolTip(translate('MainWindow', 'Follow (Ctrl+Shift+F)'))
|
2014-01-16 13:48:37 +00:00
|
|
|
self.clear_action.setText(translate('MainWindow', 'Clear'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.clear_action.setToolTip(translate('MainWindow', 'Clear (Ctrl+BkSpace)'))
|
2015-03-13 08:06:15 +00:00
|
|
|
self.xmodem_action.setText(translate('MainWindow', 'Xmodem'))
|
|
|
|
self.xmodem_action.setToolTip(translate('MainWindow', 'Send a file via Xmodem (Ctrl+Shift+X)'))
|
2013-12-10 12:55:20 +00:00
|
|
|
self.configure_action.setText(translate('MainWindow', 'Configure...'))
|
|
|
|
self.configure_action.setToolTip(translate('MainWindow', 'Configure...'))
|
|
|
|
self.exit_action.setText(translate('MainWindow', 'Exit'))
|
2014-04-04 14:04:10 +00:00
|
|
|
self.exit_action.setToolTip(translate('MainWindow', 'Exit (Alt+F4)'))
|
2013-12-05 14:16:09 +00:00
|
|
|
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
class MainWindow(QtGui.QMainWindow, UiMainWindow):
|
2013-12-05 14:16:09 +00:00
|
|
|
updateOutput = QtCore.pyqtSignal(str)
|
2013-12-20 14:22:03 +00:00
|
|
|
showMessage = QtCore.pyqtSignal(str, str, int)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
|
|
|
def __init__(self):
|
2013-12-10 12:55:20 +00:00
|
|
|
super(MainWindow, self).__init__()
|
|
|
|
self.terminal_lines = []
|
|
|
|
self.max_lines = 5000
|
|
|
|
self.setup_ui(self)
|
2013-12-05 14:16:09 +00:00
|
|
|
self.device = None
|
2013-12-10 12:55:20 +00:00
|
|
|
self.device_closed = True
|
|
|
|
self.follow_output = True
|
|
|
|
self.capture_file = None
|
|
|
|
self.capture_filename = u''
|
|
|
|
self.highlights = self.load_highlights()
|
2015-03-13 08:06:15 +00:00
|
|
|
self.disable_output = False
|
|
|
|
self.xmodem_send_progress_window = None
|
2013-12-05 14:16:09 +00:00
|
|
|
if not self.highlights:
|
|
|
|
self.highlights = create_default_highlights()
|
2013-12-10 12:55:20 +00:00
|
|
|
self.settings_dialog = SettingsDialog()
|
|
|
|
self.connect_dialog = ConnectDialog(self)
|
|
|
|
self.open_action.triggered.connect(self.on_open_action_triggered)
|
|
|
|
self.close_action.triggered.connect(self.on_close_action_triggered)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.find_action.triggered.connect(self.on_find_action_toggled)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.capture_action.toggled.connect(self.on_capture_action_toggled)
|
|
|
|
self.follow_action.toggled.connect(self.on_follow_action_toggled)
|
2014-01-16 13:48:37 +00:00
|
|
|
self.clear_action.triggered.connect(self.on_clear_action_triggered)
|
2015-03-13 08:06:15 +00:00
|
|
|
self.xmodem_action.triggered.connect(self.on_xmodem_action_triggered)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.configure_action.triggered.connect(self.on_configure_action_triggered)
|
|
|
|
self.exit_action.triggered.connect(self.close)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.find_combobox.keyPressed.connect(self.on_find_combobox_key_pressed)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.send_combobox.keyPressed.connect(self.on_send_combobox_key_pressed)
|
|
|
|
self.send_button.clicked.connect(self.on_send_button_clicked)
|
|
|
|
self.output_browser.page().mainFrame().contentsSizeChanged.connect(self.on_contents_size_changed)
|
2014-04-04 14:04:10 +00:00
|
|
|
self.output_browser.scrolled.connect(self.on_output_browser_scrolled)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.updateOutput.connect(self.on_update_output)
|
2013-12-20 14:22:03 +00:00
|
|
|
self.showMessage.connect(self.on_show_message)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
|
|
|
def close(self):
|
2013-12-10 12:55:20 +00:00
|
|
|
if not self.device_closed:
|
|
|
|
self.device_closed = True
|
|
|
|
if self.capture_file:
|
|
|
|
self.capture_file.flush()
|
|
|
|
self.capture_file.close()
|
2013-12-05 14:16:09 +00:00
|
|
|
return QtGui.QMainWindow.close(self)
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def document_body(self):
|
2013-12-20 14:22:03 +00:00
|
|
|
return self.output_browser.page().mainFrame().documentElement().findFirst(u'pre')
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def receive_text(self):
|
2013-12-05 14:16:09 +00:00
|
|
|
output = ''
|
2013-12-10 12:55:20 +00:00
|
|
|
while not self.device_closed:
|
2013-12-20 14:22:03 +00:00
|
|
|
try:
|
2015-03-13 08:06:15 +00:00
|
|
|
if self.disable_output:
|
|
|
|
time.sleep(0.5)
|
|
|
|
continue
|
2013-12-20 14:22:03 +00:00
|
|
|
output += self.device.read(1)
|
|
|
|
except SerialException as e:
|
|
|
|
self.showMessage.emit(u'Port Error', u'Error reading from serial port: %s' % e, MessageType.Critical)
|
|
|
|
self.on_close_action_triggered()
|
|
|
|
continue
|
2013-12-05 14:16:09 +00:00
|
|
|
if output.endswith('\r\n'):
|
2013-12-10 12:55:20 +00:00
|
|
|
#self.terminal_lines.append(output.strip('\r\n'))
|
|
|
|
#if len(self.terminal_lines) > self.max_lines:
|
|
|
|
# self.terminal_lines = self.terminal_lines[-self.max_lines:]
|
2013-12-05 14:16:09 +00:00
|
|
|
# self.refreshOutput()
|
|
|
|
#else:
|
|
|
|
self.updateOutput.emit(output.strip('\r\n'))
|
|
|
|
output = ''
|
2014-05-29 08:50:55 +00:00
|
|
|
elif output.endswith('\n'):
|
|
|
|
self.updateOutput.emit(output.strip('\n'))
|
|
|
|
output = ''
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_open_action_triggered(self):
|
|
|
|
self.connect_dialog.update_port_combobox()
|
2013-12-10 14:14:32 +00:00
|
|
|
settings = QtCore.QSettings()
|
2013-12-11 07:20:24 +00:00
|
|
|
self.connect_dialog.set_port(unicode(settings.value(u'previous-port', u'').toString()))
|
2013-12-10 12:55:20 +00:00
|
|
|
if self.connect_dialog.exec_() == QtGui.QDialog.Accepted:
|
|
|
|
if not self.device_closed:
|
|
|
|
self.device_closed = True
|
2013-12-05 14:16:09 +00:00
|
|
|
self.device.close()
|
|
|
|
try:
|
2014-03-20 15:43:19 +00:00
|
|
|
port = self.connect_dialog.get_port()
|
|
|
|
settings.setValue(u'previous-port', port)
|
|
|
|
if isinstance(port, basestring) and port.startswith('COM'):
|
|
|
|
try:
|
|
|
|
# On Windows ports are 0-based, so strip the COM and subtract 1 from the port number
|
|
|
|
port = int(port[3:]) - 1
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
QtGui.QMessageBox.critical(self, 'Error opening port', 'Error: Port is not valid')
|
|
|
|
return
|
2015-03-09 11:29:16 +00:00
|
|
|
self.device = serial_for_url(
|
|
|
|
url=port,
|
2013-12-10 12:55:20 +00:00
|
|
|
baudrate=self.connect_dialog.get_baud(),
|
|
|
|
bytesize=self.connect_dialog.get_data_bits(),
|
|
|
|
parity=self.connect_dialog.get_parity(),
|
|
|
|
stopbits=self.connect_dialog.get_stop_bits(),
|
2014-03-20 13:25:14 +00:00
|
|
|
timeout=1,
|
2013-12-10 12:55:20 +00:00
|
|
|
xonxoff=self.connect_dialog.get_software_handshake(),
|
|
|
|
rtscts=self.connect_dialog.get_hardware_handshake(),
|
2015-03-09 11:29:16 +00:00
|
|
|
dsrdtr=None,
|
|
|
|
do_not_open=False
|
2013-12-05 14:16:09 +00:00
|
|
|
)
|
2013-12-10 12:55:20 +00:00
|
|
|
self.device_closed = False
|
2013-12-05 14:16:09 +00:00
|
|
|
if not self.device.isOpen():
|
|
|
|
self.device.open()
|
2013-12-10 12:55:20 +00:00
|
|
|
output_thread = threading.Thread(target=self.receive_text)
|
|
|
|
output_thread.start()
|
|
|
|
except SerialException as e:
|
2013-12-05 14:16:09 +00:00
|
|
|
QtGui.QMessageBox.critical(self, 'Error opening port', e.args[0])
|
2013-12-10 12:55:20 +00:00
|
|
|
self.send_combobox.setEnabled(not self.device_closed)
|
2014-01-16 13:48:37 +00:00
|
|
|
self.send_button.setEnabled(not self.device_closed)
|
2013-12-10 12:55:20 +00:00
|
|
|
if self.send_combobox.isEnabled():
|
|
|
|
self.send_combobox.setFocus()
|
2014-03-20 15:42:20 +00:00
|
|
|
self.status_bar.showMessage('Connected to %s at %s baud' % (self.device.port, self.device.baudrate))
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_close_action_triggered(self):
|
|
|
|
self.device_closed = True
|
2014-01-16 13:48:37 +00:00
|
|
|
if self.device and self.device.isOpen():
|
2013-12-05 14:16:09 +00:00
|
|
|
self.device.close()
|
2013-12-10 12:55:20 +00:00
|
|
|
self.send_combobox.setEnabled(not self.device_closed)
|
2014-01-16 13:48:37 +00:00
|
|
|
self.send_button.setEnabled(not self.device_closed)
|
2014-04-10 06:43:56 +00:00
|
|
|
self.status_bar.showMessage('')
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2014-04-04 14:04:10 +00:00
|
|
|
def on_find_action_toggled(self, enabled):
|
|
|
|
self.find_widget.setVisible(enabled)
|
|
|
|
if enabled:
|
|
|
|
self.find_combobox.setFocus()
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_capture_action_toggled(self, enabled):
|
|
|
|
if enabled and not self.capture_file:
|
|
|
|
if self.capture_filename:
|
|
|
|
base_dir = os.path.basename(self.capture_filename)
|
2013-12-05 14:16:09 +00:00
|
|
|
else:
|
2013-12-10 12:55:20 +00:00
|
|
|
base_dir = u''
|
|
|
|
self.capture_filename = QtGui.QFileDialog.getSaveFileName(self, u'Capture To File', base_dir,
|
|
|
|
u'Text files (*.txt *.log);;All files (*)')
|
|
|
|
self.capture_file = open(self.capture_filename, u'w')
|
|
|
|
self.status_bar.showMessage(self.capture_filename)
|
|
|
|
elif self.capture_file and not enabled:
|
|
|
|
self.capture_filename = u''
|
|
|
|
self.capture_file.flush()
|
|
|
|
self.capture_file.close()
|
|
|
|
self.capture_file = None
|
|
|
|
self.status_bar.clearMessage()
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_follow_action_toggled(self, enabled):
|
|
|
|
self.follow_output = enabled
|
2013-12-05 14:16:09 +00:00
|
|
|
if enabled:
|
2013-12-10 12:55:20 +00:00
|
|
|
self.output_browser.page().mainFrame().scroll(
|
|
|
|
0, self.output_browser.page().mainFrame().contentsSize().height())
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2014-01-16 13:48:37 +00:00
|
|
|
def on_clear_action_triggered(self):
|
|
|
|
elements = self.output_browser.page().mainFrame().findAllElements('div')
|
|
|
|
for element in elements:
|
|
|
|
element.removeFromDocument()
|
|
|
|
del elements
|
|
|
|
|
2015-03-13 08:06:15 +00:00
|
|
|
def xmodem_callback(self, total_packets, success_count, error_count):
|
|
|
|
if self.xmodem_send_progress_window:
|
|
|
|
self.xmodem_send_progress_window.setValue(success_count)
|
|
|
|
print total_packets, success_count, error_count
|
|
|
|
|
|
|
|
def on_xmodem_action_triggered(self):
|
|
|
|
file_dialog = QtGui.QFileDialog()
|
|
|
|
if file_dialog.exec_():
|
|
|
|
self.disable_output = True
|
|
|
|
try:
|
|
|
|
upload_file = file_dialog.selectedFiles()[0]
|
|
|
|
file_size = getsize(upload_file)
|
|
|
|
self.device.flushInput()
|
|
|
|
self.device.flushOutput()
|
|
|
|
xmodem_transfer = XMODEM(self.getc, self.putc, mode='xmodem1k', pad='\xff')
|
|
|
|
stream = open(upload_file, 'rb')
|
|
|
|
self.xmodem_send_progress_window = QtGui.QProgressDialog(u'Sending File...', u'', 0, 0)
|
|
|
|
self.xmodem_send_progress_window.setCancelButton(None)
|
|
|
|
self.xmodem_send_progress_window.setMinimum(0)
|
|
|
|
self.xmodem_send_progress_window.setMaximum(file_size/1024)
|
|
|
|
self.xmodem_send_progress_window.setValue(0)
|
|
|
|
self.xmodem_send_progress_window.setModal(True)
|
|
|
|
self.xmodem_send_progress_window.show()
|
|
|
|
success = xmodem_transfer.send(stream, retry=200, callback=self.xmodem_callback)
|
|
|
|
print success
|
|
|
|
finally:
|
|
|
|
if self.xmodem_send_progress_window:
|
|
|
|
self.xmodem_send_progress_window.close()
|
|
|
|
self.xmodem_send_progress_window = None
|
|
|
|
self.disable_output = False
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_configure_action_triggered(self):
|
|
|
|
self.settings_dialog.set_highlights(self.highlights)
|
|
|
|
self.settings_dialog.exec_()
|
|
|
|
self.highlights = self.settings_dialog.highlights()
|
|
|
|
self.save_highlights(self.highlights)
|
|
|
|
self.refresh_output()
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2014-04-04 14:04:10 +00:00
|
|
|
def on_find_combobox_key_pressed(self, key):
|
|
|
|
if key == QtCore.Qt.Key_Return or key == QtCore.Qt.Key_Enter:
|
|
|
|
self.output_browser.findText(
|
|
|
|
self.find_combobox.currentText(),
|
|
|
|
QtWebKit.QWebPage.HighlightAllOccurrences | QtWebKit.QWebPage.FindWrapsAroundDocument
|
|
|
|
)
|
2014-05-29 08:50:55 +00:00
|
|
|
elif key == QtCore.Qt.Key_Escape:
|
|
|
|
if not self.find_combobox.currentText() and self.find_widget.isVisible():
|
|
|
|
self.find_action.setChecked(not self.find_action.isChecked())
|
|
|
|
else:
|
|
|
|
self.find_combobox.clearEditText()
|
|
|
|
self.output_browser.findText("")
|
2014-04-04 14:04:10 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_send_combobox_key_pressed(self, key):
|
2013-12-05 14:16:09 +00:00
|
|
|
if key == QtCore.Qt.Key_Return or key == QtCore.Qt.Key_Enter:
|
2013-12-10 12:55:20 +00:00
|
|
|
self.on_send_button_clicked()
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_send_button_clicked(self):
|
2013-12-05 14:16:09 +00:00
|
|
|
if self.device.isOpen():
|
2013-12-10 12:55:20 +00:00
|
|
|
output = str(self.send_combobox.currentText())
|
|
|
|
self.send_combobox.insertItem(0, output)
|
|
|
|
self.send_combobox.setCurrentIndex(0)
|
|
|
|
self.send_combobox.clearEditText()
|
2013-12-10 07:43:03 +00:00
|
|
|
self.device.write(output + '\r\n')
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_contents_size_changed(self, size):
|
|
|
|
if self.follow_output:
|
|
|
|
self.output_browser.page().mainFrame().scroll(0, size.height())
|
|
|
|
self.output_browser.update()
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_update_output(self, output):
|
|
|
|
#self.terminal_lines.append(output)
|
|
|
|
if self.capture_file:
|
|
|
|
self.capture_file.write(output + '\n')
|
|
|
|
self.capture_file.flush()
|
|
|
|
#if len(self.terminal_lines) > 5000:
|
|
|
|
# self.terminal_lines = self.terminal_lines[-5000:]
|
2013-12-05 14:16:09 +00:00
|
|
|
# self.refreshOutput()
|
|
|
|
#else:
|
2013-12-10 12:55:20 +00:00
|
|
|
output = self.style_output(output)
|
|
|
|
self.document_body().appendInside(output)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def on_output_browser_scrolled(self):
|
|
|
|
scroll_value = self.output_browser.page().mainFrame().scrollBarValue(QtCore.Qt.Vertical)
|
|
|
|
scroll_max = self.output_browser.page().mainFrame().scrollBarMaximum(QtCore.Qt.Vertical)
|
|
|
|
if scroll_value < scroll_max:
|
|
|
|
self.on_follow_action_toggled(False)
|
|
|
|
self.follow_action.setChecked(False)
|
2013-12-05 14:16:09 +00:00
|
|
|
else:
|
2013-12-10 12:55:20 +00:00
|
|
|
self.on_follow_action_toggled(True)
|
|
|
|
self.follow_action.setChecked(True)
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-20 14:22:03 +00:00
|
|
|
def on_show_message(self, title, message, type_=MessageType.Info):
|
|
|
|
if type_ == MessageType.Info:
|
|
|
|
QtGui.QMessageBox.information(self, title, message)
|
|
|
|
elif type_ == MessageType.Question:
|
|
|
|
QtGui.QMessageBox.question(self, title, message)
|
|
|
|
elif type_ == MessageType.Warning:
|
|
|
|
QtGui.QMessageBox.warning(self, title, message)
|
|
|
|
elif type_ == MessageType.Critical:
|
|
|
|
QtGui.QMessageBox.critical(self, title, message)
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def refresh_output(self):
|
|
|
|
elements = self.output_browser.page().mainFrame().findAllElements('div')
|
2013-12-05 14:16:09 +00:00
|
|
|
lines = [unicode(element.toPlainText()) for element in elements]
|
2013-12-10 12:55:20 +00:00
|
|
|
pre = self.output_browser.page().mainFrame().findFirstElement('pre')
|
2013-12-05 14:16:09 +00:00
|
|
|
pre.setInnerXml('')
|
|
|
|
for line in lines:
|
2013-12-10 12:55:20 +00:00
|
|
|
output = self.style_output(line)
|
|
|
|
self.document_body().appendInside(output)
|
|
|
|
self.output_browser.page().mainFrame().scroll(0, self.output_browser.page().mainFrame().contentsSize().height())
|
|
|
|
self.output_browser.update()
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def style_output(self, output):
|
|
|
|
style = u'font-family: \'Ubuntu Mono\', monospace; '
|
2013-12-05 14:16:09 +00:00
|
|
|
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:
|
2013-12-10 07:43:03 +00:00
|
|
|
try:
|
2013-12-20 14:22:03 +00:00
|
|
|
output = u'<div style="%s">%s</div>' % (style, self.filter_printable(output))
|
|
|
|
#output = u'<div style="%s">%s</div>' % (style, unicode(output, u'utf-8'))
|
2013-12-10 07:43:03 +00:00
|
|
|
except TypeError:
|
|
|
|
output = u'<div style="%s">%s</div>' % (style, output)
|
2013-12-05 14:16:09 +00:00
|
|
|
else:
|
|
|
|
output = u'<div>%s</div>' % output
|
2013-12-10 06:29:31 +00:00
|
|
|
return output
|
2013-12-05 14:16:09 +00:00
|
|
|
|
2013-12-20 14:22:03 +00:00
|
|
|
def filter_printable(self, output):
|
|
|
|
printable_output = u''
|
|
|
|
for char in output:
|
|
|
|
if char not in printable:
|
|
|
|
printable_output += u'\\x{:02x}'.format(ord(char))
|
2014-04-04 14:04:10 +00:00
|
|
|
else:
|
2013-12-20 14:22:03 +00:00
|
|
|
printable_output += unicode(char, u'utf8')
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def save_highlights(self, highlights):
|
2013-12-05 14:16:09 +00:00
|
|
|
settings = QtCore.QSettings()
|
|
|
|
settings.setValue(u'highlights/count', len(highlights))
|
|
|
|
for index, highlight in enumerate(highlights):
|
|
|
|
settings.beginGroup(u'highlight-%s' % index)
|
|
|
|
settings.setValue(u'pattern', highlight.pattern)
|
|
|
|
settings.setValue(u'foreground', highlight.foreground)
|
|
|
|
if highlight.background:
|
|
|
|
settings.setValue(u'background', highlight.background)
|
|
|
|
else:
|
|
|
|
if settings.contains(u'background'):
|
|
|
|
settings.remove(u'background')
|
|
|
|
settings.endGroup()
|
|
|
|
|
2013-12-10 12:55:20 +00:00
|
|
|
def load_highlights(self):
|
2013-12-05 14:16:09 +00:00
|
|
|
settings = QtCore.QSettings()
|
|
|
|
highlight_count = settings.value(u'highlights/count', 0).toInt()[0]
|
|
|
|
highlights = []
|
|
|
|
for index in range(highlight_count):
|
2013-12-10 12:55:20 +00:00
|
|
|
settings.beginGroup(u'highlight-%s' % index)
|
|
|
|
pattern = unicode(settings.value(u'pattern', u'').toString())
|
|
|
|
foreground = unicode(settings.value(u'foreground', u'').toString())
|
2013-12-05 14:16:09 +00:00
|
|
|
background = None
|
2013-12-10 12:55:20 +00:00
|
|
|
if settings.contains(u'background'):
|
|
|
|
background = unicode(settings.value(u'background', u'').toString())
|
2013-12-05 14:16:09 +00:00
|
|
|
settings.endGroup()
|
|
|
|
highlights.append(Highlight(pattern, foreground, background))
|
|
|
|
return highlights
|
2015-03-13 08:06:15 +00:00
|
|
|
|
|
|
|
def getc(self, size, timeout=1):
|
|
|
|
"""
|
|
|
|
Read a byte (usually a character) from the serial port
|
|
|
|
|
|
|
|
:param timeout:
|
|
|
|
:param size:
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
data = self.device.read(size)
|
|
|
|
except SerialException as se:
|
|
|
|
if 'interrupted system call' in str(se.args[0]).lower():
|
|
|
|
data = self.device.read(size)
|
|
|
|
else:
|
|
|
|
raise Exception(str(se))
|
|
|
|
except (SelectError, SocketError) as se:
|
|
|
|
if se.args[0] == 4:
|
|
|
|
data = self.device.read(size)
|
|
|
|
else:
|
|
|
|
raise Exception(str(se))
|
|
|
|
return data or None
|
|
|
|
|
|
|
|
def putc(self, data, timeout=1):
|
|
|
|
"""
|
|
|
|
Send a byte (usually a character) to the serial port
|
|
|
|
|
|
|
|
:param timeout:
|
|
|
|
:param data:
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self.device.write(data)
|
|
|
|
except SerialException as se:
|
|
|
|
self.error(u'Got a SerialException: %s', se)
|
|
|
|
if 'interrupted system call' in str(se.args[0]).lower():
|
|
|
|
self.device.write(data)
|
|
|
|
else:
|
|
|
|
raise Exception(str(se))
|
|
|
|
except (SelectError, SocketError) as se:
|
|
|
|
if se.args[0] == 4:
|
|
|
|
self.device.write(data)
|
|
|
|
else:
|
|
|
|
self.error(u'Got a SocketError or SelectError: %s', se)
|
|
|
|
raise Exception(str(se))
|
2016-09-30 08:32:20 +00:00
|
|
|
return None
|