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:
Alexander Zaft
2023-04-21 11:05:16 +02:00
parent f491625dd1
commit 5283b06cb7
7 changed files with 136 additions and 12 deletions

View File

@ -29,6 +29,9 @@ from os import path
# Add import path for inplace usage
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.cfg_editor.mainwindow import MainWindow
@ -38,7 +41,11 @@ def main(argv=None):
parser.add_argument('-f', '--file', help='Configuration file to open.')
args = parser.parse_args()
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()
return app.exec()

View File

@ -24,6 +24,8 @@ import configparser
from collections import OrderedDict
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.utils import get_all_children_with_names, \
get_all_items, get_interface_class_from_name, get_module_class_from_name, \
@ -41,7 +43,7 @@ SECTIONS = {NODE: 'description',
MODULE: 'class'}
def write_config(file_name, tree_widget):
def write_legacy_config(file_name, tree_widget):
itms = get_all_items(tree_widget)
itm_lines = OrderedDict()
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:
configfile.write('\n'.join(itm_lines.values()))
def read_config(file_path):
def read_legacy_config(file_path):
# TODO datatype of params and properties
node = TreeWidgetItem(NODE)
ifs = TreeWidgetItem(name='Interfaces')
@ -147,6 +148,119 @@ def read_config(file_path):
node = get_comments(node, ifs, mods, file_path)
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):
value = config.get(section, option)

View File

@ -39,9 +39,10 @@ COMMENT = 'comment'
class MainWindow(QMainWindow):
def __init__(self, file_path=None, parent=None):
def __init__(self, file_path=None, log=None, parent=None):
super().__init__(parent)
loadUi(self, 'mainwindow.ui')
self.log = log
self.tabWidget.currentChanged.connect(self.tab_relevant_btns_disable)
if file_path is None:
self.tab_relevant_btns_disable(-1)
@ -179,7 +180,7 @@ class MainWindow(QMainWindow):
QMessageBox.StandardButton.Save)
def new_node(self, name, file_path=None):
node = NodeDisplay(file_path)
node = NodeDisplay(file_path, self.log)
if node.created:
node.tree_widget.currentItemChanged.connect(self.disable_btns)
self.tabWidget.setCurrentIndex(self.tabWidget.addTab(node, name))

View File

@ -26,10 +26,12 @@ from frappy.gui.cfg_editor.utils import loadUi
class NodeDisplay(QWidget):
def __init__(self, file_path=None, parent=None):
def __init__(self, file_path=None, log=None, parent=None):
super().__init__(parent)
loadUi(self, 'node_display.ui')
self.log = log
self.saved = bool(file_path)
self.tree_widget.log = log
self.created = self.tree_widget.set_file(file_path)
self.tree_widget.save_status_changed.connect(self.change_save_status)
self.tree_widget.currentItemChanged.connect(self.set_scroll_area)

View File

@ -99,8 +99,8 @@ def get_file_paths(widget, open_file=True):
dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
dialog.setFileMode(QFileDialog.FileMode.AnyFile)
dialog.setWindowTitle(title)
dialog.setNameFilter('*.cfg')
dialog.setDefaultSuffix('.cfg')
dialog.setNameFilter('*.py')
dialog.setDefaultSuffix('.py')
dialog.exec()
return dialog.selectedFiles()

View File

@ -102,7 +102,7 @@ class TreeWidget(QTreeWidget):
self.file_path = file_path
if self.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)
return True
self.file_path = None
@ -277,7 +277,7 @@ class TreeWidget(QTreeWidget):
file_name = self.file_path
if not self.file_path or save_as:
file_name = get_file_paths(self, False)[-1]
if file_name[-4:] == '.cfg':
if file_name[-3:] == '.py':
self.file_path = file_name
write_config(self.file_path, self)
self.emit_save_status_changed(True)

View File

@ -228,7 +228,7 @@ def get_widget(datatype, readonly=False, parent=None):
TupleOf: TupleWidget,
StructOf: StructWidget,
ArrayOf: ArrayWidget,
}.get(datatype.__class__)(datatype, readonly, parent)
}.get(datatype.__class__, TextWidget)(datatype, readonly, parent)
# TODO: handle NoneOr