parent
76ade9f5cd
commit
7069a3ff81
@ -0,0 +1,119 @@ |
|||||||
|
#!/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): |
||||||
|
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, Close', 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(gex.TrxRawUSB(), load_units=False) |
||||||
|
read_ini = client.ini_read() |
||||||
|
client.close() |
||||||
|
|
||||||
|
self.editor.setPlainText(read_ini) |
||||||
|
self.highlight.rehighlight() |
||||||
|
|
||||||
|
def gexSync(self): |
||||||
|
new_txt = self.editor.toPlainText() |
||||||
|
self.editor.setPlainText("") |
||||||
|
self.editor.repaint() |
||||||
|
|
||||||
|
client = gex.Client(gex.TrxRawUSB(), load_units=False) |
||||||
|
client.ini_write(new_txt) |
||||||
|
read_ini = client.ini_read() |
||||||
|
client.close() |
||||||
|
|
||||||
|
self.editor.setPlainText(read_ini) |
||||||
|
self.highlight.rehighlight() |
||||||
|
|
||||||
|
def gexPersist(self): |
||||||
|
client = gex.Client(gex.TrxRawUSB(), load_units=False) |
||||||
|
client.ini_persist() |
||||||
|
client.close() |
||||||
|
self.close() |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
app = QtGui.QApplication(sys.argv) |
||||||
|
editor = GexIniEditor() |
||||||
|
|
||||||
|
# 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