commit
50b9942ecf
@ -0,0 +1,128 @@ |
||||
#!/bin/env python3 |
||||
|
||||
import gex |
||||
|
||||
import sys |
||||
from PyQt4 import QtGui, QtCore |
||||
import ini_syntax |
||||
|
||||
class GexIniEditor(QtGui.QMainWindow): |
||||
""" |
||||
Gexync is a GEX ini file editor. |
||||
The editor loads the INI file through the communication interface |
||||
without having to mount the virtual filesystem, which is unreliable |
||||
on some systems. |
||||
|
||||
This utility allows live editing of the UNITS.INI file. |
||||
|
||||
On save, a new version is loaded with formatting and error messages |
||||
generated by GEX, as if the virtual config filesystem was re-mounted. |
||||
|
||||
The editor does not keep GEX claimed, instead does so only when needed. |
||||
This allows testing of the current configuration without having to close |
||||
and reopen the editor. |
||||
""" |
||||
|
||||
def __init__(self, xferLambda): |
||||
self.xferLambda = xferLambda |
||||
|
||||
self.filenum = int(sys.argv[1]) if len(sys.argv)>1 else 0 |
||||
|
||||
super().__init__() |
||||
self.initUI() |
||||
# TODO let user pick GEX device if multiple |
||||
|
||||
def initToolbar(self): |
||||
icon = self.style().standardIcon(QtGui.QStyle.SP_BrowserReload) |
||||
loadAction = QtGui.QAction(icon, 'Reload', self) |
||||
loadAction.setShortcut('Ctrl+O') |
||||
loadAction.triggered.connect(self.gexLoad) |
||||
|
||||
icon = self.style().standardIcon(QtGui.QStyle.SP_DialogSaveButton) |
||||
syncAction = QtGui.QAction(icon, 'Write Changes', self) |
||||
syncAction.setShortcut('Ctrl+S') |
||||
syncAction.triggered.connect(self.gexSync) |
||||
|
||||
icon = self.style().standardIcon(QtGui.QStyle.SP_DialogOkButton) |
||||
persAction = QtGui.QAction(icon, 'Persist', self) |
||||
persAction.setShortcut('Ctrl+P') |
||||
persAction.triggered.connect(self.gexPersist) |
||||
|
||||
self.toolbar = self.addToolBar('Toolbar') |
||||
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) |
||||
self.toolbar.addAction(loadAction) |
||||
self.toolbar.addAction(syncAction) |
||||
self.toolbar.addSeparator() |
||||
self.toolbar.addAction(persAction) |
||||
|
||||
def initEditor(self): |
||||
self.editor = QtGui.QPlainTextEdit() |
||||
|
||||
# Editor background and text color |
||||
pal = QtGui.QPalette() |
||||
bgc = QtGui.QColor(0xFFFFF6) |
||||
pal.setColor(QtGui.QPalette.Base, bgc) |
||||
textc = QtGui.QColor(0x000000) |
||||
pal.setColor(QtGui.QPalette.Text, textc) |
||||
self.editor.setPalette(pal) |
||||
# Font |
||||
font = QtGui.QFont('Liberation Mono', 12) |
||||
font.setStyleHint(QtGui.QFont.TypeWriter) |
||||
self.editor.setFont(font) |
||||
# Initial size |
||||
self.highlight = ini_syntax.IniHighlighter(self.editor.document()) |
||||
|
||||
def initUI(self): |
||||
self.setWindowTitle('GEX config file editor') |
||||
self.initToolbar() |
||||
self.initEditor() |
||||
self.setCentralWidget(self.editor) |
||||
self.show() |
||||
|
||||
self.gexLoad() |
||||
|
||||
def gexLoad(self): |
||||
self.editor.setPlainText("") |
||||
self.editor.repaint() |
||||
|
||||
client = gex.Client(self.xferLambda(), load_units=False) |
||||
read_ini = client.ini_read(self.filenum) |
||||
client.close() |
||||
|
||||
self.editor.setPlainText(read_ini) |
||||
self.highlight.rehighlight() |
||||
self.setWindowTitle('GEX config file editor') |
||||
|
||||
def gexSync(self): |
||||
new_txt = self.editor.toPlainText() |
||||
self.editor.setPlainText("") |
||||
self.editor.repaint() |
||||
|
||||
client = gex.Client(self.xferLambda(), load_units=False) |
||||
client.ini_write(new_txt) |
||||
read_ini = client.ini_read(self.filenum) |
||||
client.close() |
||||
|
||||
self.editor.setPlainText(read_ini) |
||||
self.highlight.rehighlight() |
||||
self.setWindowTitle('*GEX config file editor') |
||||
|
||||
def gexPersist(self): |
||||
client = gex.Client(self.xferLambda(), load_units=False) |
||||
client.ini_persist() |
||||
client.close() |
||||
self.setWindowTitle('GEX config file editor') |
||||
|
||||
if __name__ == '__main__': |
||||
app = QtGui.QApplication(sys.argv) |
||||
editor = GexIniEditor(lambda: gex.TrxRawUSB()) |
||||
# editor = GexIniEditor(lambda: gex.TrxSerialThread(port='/dev/ttyUSB1', |
||||
# baud=57600)) |
||||
|
||||
# centered resize |
||||
w = 800 |
||||
h = 900 |
||||
ss = app.desktop().availableGeometry().size() |
||||
editor.setGeometry(int(ss.width() / 2 - w / 2), int(ss.height() / 2 - h / 2), w, h) |
||||
|
||||
sys.exit(app.exec_()) |
@ -0,0 +1,81 @@ |
||||
# syntax.py |
||||
|
||||
# This is a companion file to gexync.py |
||||
|
||||
# based on https://wiki.python.org/moin/PyQt/Python%20syntax%20highlighting |
||||
|
||||
from PyQt4.QtCore import QRegExp |
||||
from PyQt4.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter |
||||
|
||||
def format(color, style=''): |
||||
"""Return a QTextCharFormat with the given attributes. |
||||
""" |
||||
_color = QColor() |
||||
_color.setNamedColor(color) |
||||
|
||||
_format = QTextCharFormat() |
||||
_format.setForeground(_color) |
||||
if 'bold' in style: |
||||
_format.setFontWeight(QFont.Bold) |
||||
if 'italic' in style: |
||||
_format.setFontItalic(True) |
||||
|
||||
return _format |
||||
|
||||
|
||||
# Syntax styles that can be shared by all languages |
||||
STYLES = { |
||||
'operator': format('red'), |
||||
'string': format('magenta'), |
||||
'comment': format('#6C8A70', 'italic'), |
||||
'key': format('#008AFF'), |
||||
'numbers': format('brown'), |
||||
'section': format('black', 'bold'), |
||||
} |
||||
|
||||
class IniHighlighter (QSyntaxHighlighter): |
||||
# Python braces |
||||
def __init__(self, document): |
||||
QSyntaxHighlighter.__init__(self, document) |
||||
|
||||
rules = [ |
||||
(r'=', 0, STYLES['operator']), |
||||
(r'\b[YN]\b', 0, STYLES['numbers']), |
||||
|
||||
# Double-quoted string, possibly containing escape sequences |
||||
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']), |
||||
# Single-quoted string, possibly containing escape sequences |
||||
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']), |
||||
|
||||
# Numeric literals |
||||
(r'\b[+-]?[0-9]+\b', 0, STYLES['numbers']), |
||||
(r'\b[+-]?0[xX][0-9A-Fa-f]+\b', 0, STYLES['numbers']), |
||||
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']), |
||||
|
||||
# From '#' until a newline |
||||
(r'#[^\n]*', 0, STYLES['comment']), |
||||
|
||||
(r'^\[.+\]', 0, STYLES['section']), |
||||
|
||||
(r'^[a-zA-Z0-9_-]+\s?=', 0, STYLES['key']), |
||||
] |
||||
|
||||
# Build a QRegExp for each pattern |
||||
self.rules = [(QRegExp(pat), index, fmt) |
||||
for (pat, index, fmt) in rules] |
||||
|
||||
def highlightBlock(self, text): |
||||
"""Apply syntax highlighting to the given block of text. |
||||
""" |
||||
# Do other syntax formatting |
||||
for expression, nth, format in self.rules: |
||||
index = expression.indexIn(text, 0) |
||||
|
||||
while index >= 0: |
||||
# We actually want the index of the nth match |
||||
index = expression.pos(nth) |
||||
length = len(expression.cap(nth)) |
||||
self.setFormat(index, length, format) |
||||
index = expression.indexIn(text, index + length) |
||||
|
||||
self.setCurrentBlockState(0) |
Loading…
Reference in new issue