Add Stopgap handling of cfg files in cfg-editor
Basic parsing and writing of python config files. Does not preserve comments, can't transform the old format to the new one. This is just so the executable that is delivered actually does something with the new config files. Change-Id: I4bb8310e1af7a05f90dd426dfa629080583aff66 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30935 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
This commit is contained in:
@ -29,6 +29,9 @@ from os import path
|
|||||||
# Add import path for inplace usage
|
# Add import path for inplace usage
|
||||||
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))
|
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from mlzlog import ColoredConsoleHandler
|
||||||
|
|
||||||
from frappy.gui.qt import QApplication
|
from frappy.gui.qt import QApplication
|
||||||
from frappy.gui.cfg_editor.mainwindow import MainWindow
|
from frappy.gui.cfg_editor.mainwindow import MainWindow
|
||||||
|
|
||||||
@ -38,7 +41,11 @@ def main(argv=None):
|
|||||||
parser.add_argument('-f', '--file', help='Configuration file to open.')
|
parser.add_argument('-f', '--file', help='Configuration file to open.')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
app = QApplication(argv)
|
app = QApplication(argv)
|
||||||
window = MainWindow(args.file)
|
logger = logging.getLogger('gui')
|
||||||
|
console = ColoredConsoleHandler()
|
||||||
|
console.setLevel(logging.INFO)
|
||||||
|
logger.addHandler(console)
|
||||||
|
window = MainWindow(args.file, log=logger)
|
||||||
window.show()
|
window.show()
|
||||||
return app.exec()
|
return app.exec()
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ import configparser
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from configparser import NoOptionError
|
from configparser import NoOptionError
|
||||||
|
|
||||||
|
from frappy.config import load_config
|
||||||
|
from frappy.datatypes import StringType, EnumType
|
||||||
from frappy.gui.cfg_editor.tree_widget_item import TreeWidgetItem
|
from frappy.gui.cfg_editor.tree_widget_item import TreeWidgetItem
|
||||||
from frappy.gui.cfg_editor.utils import get_all_children_with_names, \
|
from frappy.gui.cfg_editor.utils import get_all_children_with_names, \
|
||||||
get_all_items, get_interface_class_from_name, get_module_class_from_name, \
|
get_all_items, get_interface_class_from_name, get_module_class_from_name, \
|
||||||
@ -41,7 +43,7 @@ SECTIONS = {NODE: 'description',
|
|||||||
MODULE: 'class'}
|
MODULE: 'class'}
|
||||||
|
|
||||||
|
|
||||||
def write_config(file_name, tree_widget):
|
def write_legacy_config(file_name, tree_widget):
|
||||||
itms = get_all_items(tree_widget)
|
itms = get_all_items(tree_widget)
|
||||||
itm_lines = OrderedDict()
|
itm_lines = OrderedDict()
|
||||||
value_str = '%s = %s'
|
value_str = '%s = %s'
|
||||||
@ -79,8 +81,7 @@ def write_config(file_name, tree_widget):
|
|||||||
with open(file_name, 'w', encoding='utf-8') as configfile:
|
with open(file_name, 'w', encoding='utf-8') as configfile:
|
||||||
configfile.write('\n'.join(itm_lines.values()))
|
configfile.write('\n'.join(itm_lines.values()))
|
||||||
|
|
||||||
|
def read_legacy_config(file_path):
|
||||||
def read_config(file_path):
|
|
||||||
# TODO datatype of params and properties
|
# TODO datatype of params and properties
|
||||||
node = TreeWidgetItem(NODE)
|
node = TreeWidgetItem(NODE)
|
||||||
ifs = TreeWidgetItem(name='Interfaces')
|
ifs = TreeWidgetItem(name='Interfaces')
|
||||||
@ -147,6 +148,119 @@ def read_config(file_path):
|
|||||||
node = get_comments(node, ifs, mods, file_path)
|
node = get_comments(node, ifs, mods, file_path)
|
||||||
return node, ifs, mods
|
return node, ifs, mods
|
||||||
|
|
||||||
|
def fmt_value(param, dt):
|
||||||
|
if isinstance(dt, StringType):
|
||||||
|
return repr(param)
|
||||||
|
if isinstance(dt, EnumType):
|
||||||
|
try:
|
||||||
|
return int(param)
|
||||||
|
except ValueError:
|
||||||
|
return repr(param)
|
||||||
|
return param
|
||||||
|
|
||||||
|
def fmt_param(param, pnp):
|
||||||
|
props = pnp[param.name]
|
||||||
|
if isinstance(props, list):
|
||||||
|
dt = props[0].datatype
|
||||||
|
else:
|
||||||
|
dt = props.datatype
|
||||||
|
|
||||||
|
if param.childCount() > 1 or (param.childCount() == 1 and param.child(0).name != 'value'):
|
||||||
|
values = []
|
||||||
|
main_value = param.get_value()
|
||||||
|
if main_value:
|
||||||
|
values.append(fmt_value(main_value, dt))
|
||||||
|
for i in range(param.childCount()):
|
||||||
|
prop = param.child(i)
|
||||||
|
propdt = props[1][prop.name].datatype
|
||||||
|
propv = fmt_value(prop.get_value(), propdt)
|
||||||
|
values.append(f'{prop.name} = {propv}')
|
||||||
|
values = f'Param({", ".join(values)})'
|
||||||
|
else:
|
||||||
|
values = fmt_value(param.get_value(), dt)
|
||||||
|
return f' {param.name} = {values},'
|
||||||
|
|
||||||
|
def write_config(file_name, tree_widget):
|
||||||
|
"""stopgap python config writing. assumes no comments are in the cfg."""
|
||||||
|
itms = get_all_items(tree_widget)
|
||||||
|
for itm in itms:
|
||||||
|
if itm.kind == 'comment':
|
||||||
|
print('comments are broken right now. not writing a file. exiting...')
|
||||||
|
return
|
||||||
|
lines = []
|
||||||
|
root = tree_widget.topLevelItem(0)
|
||||||
|
eq_id = root.name
|
||||||
|
description = root.get_value()
|
||||||
|
iface = root.child(0).child(0).name
|
||||||
|
|
||||||
|
lines.append(f'Node(\'{eq_id}\',\n {repr(description)},\n \'{iface}\',')
|
||||||
|
for i in range(2, root.childCount()):
|
||||||
|
lines.append(fmt_param(root.child(i), {}))
|
||||||
|
lines.append(')')
|
||||||
|
mods = root.child(1)
|
||||||
|
for i in range(mods.childCount()):
|
||||||
|
lines.append('\n')
|
||||||
|
mod = mods.child(i)
|
||||||
|
params_and_props = {}
|
||||||
|
params_and_props.update(mod.properties)
|
||||||
|
params_and_props.update(mod.parameters)
|
||||||
|
descr = None
|
||||||
|
for i in range(mod.childCount()):
|
||||||
|
if mod.child(i).name == 'description':
|
||||||
|
descr = mod.child(i)
|
||||||
|
break
|
||||||
|
lines.append(f'Mod(\'{mod.name}\',\n \'{mod.get_value()}\',\n \'{descr.get_value()}\',')
|
||||||
|
for j in range(mod.childCount()):
|
||||||
|
if j == i:
|
||||||
|
continue
|
||||||
|
lines.append(fmt_param(mod.child(j), params_and_props))
|
||||||
|
lines.append(')')
|
||||||
|
|
||||||
|
with open(file_name, 'w', encoding='utf-8') as configfile:
|
||||||
|
configfile.write('\n'.join(lines))
|
||||||
|
|
||||||
|
def read_config(file_path, log):
|
||||||
|
config = load_config(file_path, log)
|
||||||
|
node = TreeWidgetItem(NODE)
|
||||||
|
ifs = TreeWidgetItem(name='Interfaces')
|
||||||
|
mods = TreeWidgetItem(name='Modules')
|
||||||
|
node.addChild(ifs)
|
||||||
|
node.addChild(mods)
|
||||||
|
nodecfg = config.pop('node', {})
|
||||||
|
node.set_name(nodecfg['equipment_id'])
|
||||||
|
node.set_value(nodecfg['description'])
|
||||||
|
node.parameters = get_params(NODE)
|
||||||
|
node.properties = get_props(NODE)
|
||||||
|
|
||||||
|
iface = TreeWidgetItem(INTERFACE, nodecfg['interface'])
|
||||||
|
#act_class = get_interface_class_from_name(section_value)
|
||||||
|
#act_item.set_class_object(act_class)
|
||||||
|
ifs.addChild(iface)
|
||||||
|
for name, modcfg in config.items():
|
||||||
|
act_item = TreeWidgetItem(MODULE, name)
|
||||||
|
mods.addChild(act_item)
|
||||||
|
cls = modcfg.pop('cls')
|
||||||
|
act_item.set_value(cls)
|
||||||
|
act_class = get_module_class_from_name(cls)
|
||||||
|
act_item.set_class_object(act_class)
|
||||||
|
act_item.parameters = get_params(act_class)
|
||||||
|
act_item.properties = get_props(act_class)
|
||||||
|
for param, options in modcfg.items():
|
||||||
|
if not isinstance(options, dict):
|
||||||
|
prop = TreeWidgetItem(PROPERTY, param, str(options))
|
||||||
|
act_item.addChild(prop)
|
||||||
|
else:
|
||||||
|
param = TreeWidgetItem(PARAMETER, param)
|
||||||
|
param.set_value('')
|
||||||
|
act_item.addChild(param)
|
||||||
|
for k, v in options.items():
|
||||||
|
if k == 'value':
|
||||||
|
param.set_value(str(v))
|
||||||
|
else:
|
||||||
|
param.addChild(TreeWidgetItem(PROPERTY, k, str(v)))
|
||||||
|
#node = get_comments(node, ifs, mods, file_path)
|
||||||
|
return node, ifs, mods
|
||||||
|
|
||||||
|
|
||||||
def get_value(config, section, option):
|
def get_value(config, section, option):
|
||||||
value = config.get(section, option)
|
value = config.get(section, option)
|
||||||
|
@ -39,9 +39,10 @@ COMMENT = 'comment'
|
|||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
|
|
||||||
def __init__(self, file_path=None, parent=None):
|
def __init__(self, file_path=None, log=None, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
loadUi(self, 'mainwindow.ui')
|
loadUi(self, 'mainwindow.ui')
|
||||||
|
self.log = log
|
||||||
self.tabWidget.currentChanged.connect(self.tab_relevant_btns_disable)
|
self.tabWidget.currentChanged.connect(self.tab_relevant_btns_disable)
|
||||||
if file_path is None:
|
if file_path is None:
|
||||||
self.tab_relevant_btns_disable(-1)
|
self.tab_relevant_btns_disable(-1)
|
||||||
@ -179,7 +180,7 @@ class MainWindow(QMainWindow):
|
|||||||
QMessageBox.StandardButton.Save)
|
QMessageBox.StandardButton.Save)
|
||||||
|
|
||||||
def new_node(self, name, file_path=None):
|
def new_node(self, name, file_path=None):
|
||||||
node = NodeDisplay(file_path)
|
node = NodeDisplay(file_path, self.log)
|
||||||
if node.created:
|
if node.created:
|
||||||
node.tree_widget.currentItemChanged.connect(self.disable_btns)
|
node.tree_widget.currentItemChanged.connect(self.disable_btns)
|
||||||
self.tabWidget.setCurrentIndex(self.tabWidget.addTab(node, name))
|
self.tabWidget.setCurrentIndex(self.tabWidget.addTab(node, name))
|
||||||
|
@ -26,10 +26,12 @@ from frappy.gui.cfg_editor.utils import loadUi
|
|||||||
|
|
||||||
|
|
||||||
class NodeDisplay(QWidget):
|
class NodeDisplay(QWidget):
|
||||||
def __init__(self, file_path=None, parent=None):
|
def __init__(self, file_path=None, log=None, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
loadUi(self, 'node_display.ui')
|
loadUi(self, 'node_display.ui')
|
||||||
|
self.log = log
|
||||||
self.saved = bool(file_path)
|
self.saved = bool(file_path)
|
||||||
|
self.tree_widget.log = log
|
||||||
self.created = self.tree_widget.set_file(file_path)
|
self.created = self.tree_widget.set_file(file_path)
|
||||||
self.tree_widget.save_status_changed.connect(self.change_save_status)
|
self.tree_widget.save_status_changed.connect(self.change_save_status)
|
||||||
self.tree_widget.currentItemChanged.connect(self.set_scroll_area)
|
self.tree_widget.currentItemChanged.connect(self.set_scroll_area)
|
||||||
|
@ -99,8 +99,8 @@ def get_file_paths(widget, open_file=True):
|
|||||||
dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
|
dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
|
||||||
dialog.setFileMode(QFileDialog.FileMode.AnyFile)
|
dialog.setFileMode(QFileDialog.FileMode.AnyFile)
|
||||||
dialog.setWindowTitle(title)
|
dialog.setWindowTitle(title)
|
||||||
dialog.setNameFilter('*.cfg')
|
dialog.setNameFilter('*.py')
|
||||||
dialog.setDefaultSuffix('.cfg')
|
dialog.setDefaultSuffix('.py')
|
||||||
dialog.exec()
|
dialog.exec()
|
||||||
return dialog.selectedFiles()
|
return dialog.selectedFiles()
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ class TreeWidget(QTreeWidget):
|
|||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
if self.file_path:
|
if self.file_path:
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
self.set_tree(read_config(self.file_path))
|
self.set_tree(read_config(self.file_path, self.log))
|
||||||
self.emit_save_status_changed(True)
|
self.emit_save_status_changed(True)
|
||||||
return True
|
return True
|
||||||
self.file_path = None
|
self.file_path = None
|
||||||
@ -277,7 +277,7 @@ class TreeWidget(QTreeWidget):
|
|||||||
file_name = self.file_path
|
file_name = self.file_path
|
||||||
if not self.file_path or save_as:
|
if not self.file_path or save_as:
|
||||||
file_name = get_file_paths(self, False)[-1]
|
file_name = get_file_paths(self, False)[-1]
|
||||||
if file_name[-4:] == '.cfg':
|
if file_name[-3:] == '.py':
|
||||||
self.file_path = file_name
|
self.file_path = file_name
|
||||||
write_config(self.file_path, self)
|
write_config(self.file_path, self)
|
||||||
self.emit_save_status_changed(True)
|
self.emit_save_status_changed(True)
|
||||||
|
@ -228,7 +228,7 @@ def get_widget(datatype, readonly=False, parent=None):
|
|||||||
TupleOf: TupleWidget,
|
TupleOf: TupleWidget,
|
||||||
StructOf: StructWidget,
|
StructOf: StructWidget,
|
||||||
ArrayOf: ArrayWidget,
|
ArrayOf: ArrayWidget,
|
||||||
}.get(datatype.__class__)(datatype, readonly, parent)
|
}.get(datatype.__class__, TextWidget)(datatype, readonly, parent)
|
||||||
# TODO: handle NoneOr
|
# TODO: handle NoneOr
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user