add cfg-editor
Change-Id: I68b8ba9311ec0487d7a2676095a7170a2447b3d0 Reviewed-on: https://forge.frm2.tum.de/review/20206 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
46
bin/cfg-editor
Executable file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from os import path
|
||||
|
||||
# Add import path for inplace usage
|
||||
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))
|
||||
|
||||
from secop.gui.qt import QApplication
|
||||
from secop.gui.cfg_editor.mainwindow import MainWindow
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-f', '--file', help='Configuration file to open.')
|
||||
args = parser.parse_args()
|
||||
app = QApplication(argv)
|
||||
window = MainWindow(args.file)
|
||||
window.show()
|
||||
return app.exec_()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
0
secop/gui/cfg_editor/__init__.py
Normal file
273
secop/gui/cfg_editor/config_file.py
Normal file
@ -0,0 +1,273 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
import configparser
|
||||
from configparser import NoOptionError
|
||||
from collections import OrderedDict
|
||||
from secop.gui.cfg_editor.tree_widget_item import TreeWidgetItem
|
||||
from secop.gui.cfg_editor.utils import get_all_items, get_params, get_props,\
|
||||
get_all_children_with_names, get_module_class_from_name, \
|
||||
get_interface_class_from_name
|
||||
|
||||
|
||||
NODE = 'node'
|
||||
INTERFACE = 'interface'
|
||||
MODULE = 'module'
|
||||
PARAMETER = 'parameter'
|
||||
PROPERTY = 'property'
|
||||
COMMENT = 'comment'
|
||||
|
||||
SECTIONS = {NODE: 'description',
|
||||
INTERFACE: 'type',
|
||||
MODULE: 'class'}
|
||||
|
||||
|
||||
def write_config(file_name, tree_widget):
|
||||
itms = get_all_items(tree_widget)
|
||||
itm_lines = OrderedDict()
|
||||
value_str = '%s = %s'
|
||||
blank_lines = 0
|
||||
for itm in itms:
|
||||
if itm.kind is None:
|
||||
continue
|
||||
par = itm.parent()
|
||||
value = str(itm.get_value())
|
||||
if itm.kind in SECTIONS:
|
||||
if itm.kind in [MODULE, INTERFACE]:
|
||||
itm_lines[blank_lines] = ''
|
||||
blank_lines += 1
|
||||
value = value.replace('\n\n', '\n.\n')
|
||||
value = value.replace('\n', '\n ')
|
||||
itm_lines[itm] = '[%s %s]\n' % (itm.kind, itm.name) +\
|
||||
value_str % (SECTIONS[itm.kind], value)
|
||||
# TODO params and props
|
||||
elif itm.kind == PARAMETER and value:
|
||||
itm_lines[itm] = value_str % (itm.name, value)
|
||||
elif itm.kind == PROPERTY:
|
||||
prop_name = '.%s' % itm.name
|
||||
if par.kind == PARAMETER:
|
||||
prop_name = par.name + prop_name
|
||||
itm_lines[itm] = value_str % (prop_name, value)
|
||||
elif itm.kind == COMMENT:
|
||||
temp_itm_lines = OrderedDict()
|
||||
for key, dict_value in itm_lines.items():
|
||||
if key == par:
|
||||
value = value.replace('\n', '\n# ')
|
||||
temp_itm_lines[itm] = '# %s' % value
|
||||
temp_itm_lines[key] = dict_value
|
||||
itm_lines.clear()
|
||||
itm_lines.update(temp_itm_lines)
|
||||
with open(file_name, 'w') as configfile:
|
||||
configfile.write('\n'.join(itm_lines.values()))
|
||||
|
||||
|
||||
def read_config(file_path):
|
||||
# TODO datatype of params and properties
|
||||
node = TreeWidgetItem(NODE)
|
||||
ifs = TreeWidgetItem(name='interfaces')
|
||||
mods = TreeWidgetItem(name='modules')
|
||||
node.addChild(ifs)
|
||||
node.addChild(mods)
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(open(file_path))
|
||||
|
||||
for section in config.sections():
|
||||
kind = section.split(' ', 1)[0]
|
||||
name = section.split(' ', 1)[1]
|
||||
try:
|
||||
section_value = get_value(config, section, SECTIONS[kind])
|
||||
section_value = section_value.replace('\n.\n', '\n\n') \
|
||||
if kind == NODE else section_value
|
||||
except NoOptionError:
|
||||
# TODO invalid configuration
|
||||
continue
|
||||
if kind == NODE:
|
||||
node.set_name(name)
|
||||
act_item = node
|
||||
act_item.set_value(section_value)
|
||||
act_item.parameters = get_params(kind)
|
||||
act_item.properties = get_props(kind)
|
||||
else:
|
||||
act_item = TreeWidgetItem(kind, name)
|
||||
act_item.set_value(section_value)
|
||||
if kind == MODULE:
|
||||
mods.addChild(act_item)
|
||||
act_class = get_module_class_from_name(section_value)
|
||||
act_item.set_class_object(act_class)
|
||||
else:
|
||||
act_class = get_interface_class_from_name(section_value)
|
||||
act_item.set_class_object(act_class)
|
||||
ifs.addChild(act_item)
|
||||
act_item.parameters = get_params(act_class)
|
||||
act_item.properties = get_props(act_class)
|
||||
|
||||
# TODO rewrite so Parameters and Properties get class_object and
|
||||
# properties, needed information in act_item.parameters/properties
|
||||
for option in config.options(section):
|
||||
if option != SECTIONS[kind]:
|
||||
if option[0] == '.':
|
||||
prop = TreeWidgetItem(PROPERTY, option[1:],
|
||||
get_value(config, section, option))
|
||||
act_item.addChild(prop)
|
||||
else:
|
||||
separated = option.split('.')
|
||||
act_children = get_all_children_with_names(act_item)
|
||||
# TODO find param / props in params, props and add datatype
|
||||
if separated[0] in act_children:
|
||||
param = act_children[separated[0]]
|
||||
else:
|
||||
param = TreeWidgetItem(PARAMETER, separated[0])
|
||||
act_item.addChild(param)
|
||||
if len(separated) == 1:
|
||||
param.set_value(get_value(config, section, option))
|
||||
else:
|
||||
param.addChild(TreeWidgetItem(PROPERTY,
|
||||
separated[1], get_value(config, section,
|
||||
option)))
|
||||
node = get_comments(node, ifs, mods, file_path)
|
||||
return node, ifs, mods
|
||||
|
||||
|
||||
def get_value(config, section, option):
|
||||
value = config.get(section, option)
|
||||
if value.find('#') != -1:
|
||||
value = value[:value.find('#')]
|
||||
return value
|
||||
|
||||
|
||||
def get_comments(node, ifs, mods, file_path):
|
||||
configfile = open(file_path, 'r')
|
||||
all_lines = configfile.readlines()
|
||||
current_comment = None
|
||||
all_ifs = get_all_children_with_names(ifs)
|
||||
all_mods = get_all_children_with_names(mods)
|
||||
for index, line in enumerate(all_lines):
|
||||
line = line[:-1]
|
||||
if line.startswith('#'):
|
||||
line = line[1:].strip()
|
||||
if index and all_lines[index-1][0] == '#':
|
||||
current_comment.set_value('%s\n%s' % (current_comment.
|
||||
get_value(), line))
|
||||
else:
|
||||
current_comment = TreeWidgetItem(COMMENT, '#', line)
|
||||
next_line = get_next_line(index, all_lines)
|
||||
if not next_line:
|
||||
node.insertChild(0, current_comment)
|
||||
continue
|
||||
insert_comment(index, next_line, all_lines, current_comment,
|
||||
node, all_ifs, all_mods)
|
||||
elif line.find('#') != -1:
|
||||
comment_index = line.find('#')
|
||||
current_comment = TreeWidgetItem(COMMENT, '#',
|
||||
line[comment_index+1:].strip())
|
||||
line = line[:comment_index]
|
||||
insert_comment(index, line, all_lines, current_comment, node,
|
||||
all_ifs, all_mods)
|
||||
return node
|
||||
|
||||
|
||||
def insert_comment(index, line, all_lines, current_comment, node, all_ifs, all_mods):
|
||||
if not insert_section_comment(line, current_comment, node, all_ifs,
|
||||
all_mods):
|
||||
insert_param_prop_comment(index, line, all_lines, current_comment, node,
|
||||
all_ifs, all_mods)
|
||||
|
||||
|
||||
def insert_section_comment(line, current_comment, node, all_ifs, all_mods):
|
||||
try:
|
||||
if line.startswith('[%s' % NODE):
|
||||
node.insertChild(0, current_comment)
|
||||
elif line.startswith('[%s' % INTERFACE):
|
||||
all_ifs[get_name_of_section(line)]. \
|
||||
insertChild(0, current_comment)
|
||||
elif line.startswith('[%s' % MODULE):
|
||||
all_mods[get_name_of_section(line)]. \
|
||||
insertChild(0, current_comment)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
except KeyError:
|
||||
# TODO invalid file
|
||||
pass
|
||||
|
||||
|
||||
def insert_param_prop_comment(index, line, all_lines, current_comment,
|
||||
node, all_ifs, all_mods):
|
||||
try:
|
||||
parent = get_previous_line(index, all_lines)
|
||||
if not parent:
|
||||
# TODO invalid file
|
||||
pass
|
||||
if parent.startswith('[%s' % NODE):
|
||||
parent_item = node
|
||||
elif parent.startswith('[%s' % INTERFACE):
|
||||
parent_item = all_ifs[get_name_of_section(parent)]
|
||||
else:
|
||||
parent_item = all_mods[get_name_of_section(parent)]
|
||||
parent_children = get_all_children_with_names(
|
||||
parent_item)
|
||||
line = line.replace(' ', '')
|
||||
line = line.split('=')[0]
|
||||
dot_i = line.find('.')
|
||||
if dot_i == -1:
|
||||
# parameter
|
||||
parent_children[line].insertChild(
|
||||
0, current_comment)
|
||||
elif dot_i == 0:
|
||||
# .property
|
||||
parent_children[line[1:]].insertChild(
|
||||
0, current_comment)
|
||||
else:
|
||||
# parameter.property
|
||||
sub_childs = get_all_children_with_names(
|
||||
parent_children[line[:dot_i]])
|
||||
sub_childs[line[dot_i + 1:]].insertChild(
|
||||
0, current_comment)
|
||||
except KeyError:
|
||||
# TODO invalid file
|
||||
pass
|
||||
|
||||
|
||||
def get_next_line(index, all_lines):
|
||||
next_index = index + 1
|
||||
try:
|
||||
while all_lines[next_index][0] == '#' or \
|
||||
all_lines[next_index][:-1].strip() == '':
|
||||
next_index += 1
|
||||
except IndexError:
|
||||
return ''
|
||||
return all_lines[next_index][:-1].strip()
|
||||
|
||||
|
||||
def get_previous_line(index, all_lines):
|
||||
prev_index = index - 1
|
||||
try:
|
||||
while all_lines[prev_index].strip()[0] != '[':
|
||||
prev_index -= 1
|
||||
except IndexError:
|
||||
return ''
|
||||
return all_lines[prev_index]
|
||||
|
||||
|
||||
def get_name_of_section(line):
|
||||
line = line[line.find('['):line.rfind(']')]
|
||||
return ' '.join(line.split(' ', 1)[1:])
|
226
secop/gui/cfg_editor/mainwindow.py
Normal file
@ -0,0 +1,226 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
import os
|
||||
from secop.gui.qt import QMainWindow, QMessageBox
|
||||
from secop.gui.cfg_editor.node_display import NodeDisplay
|
||||
from secop.gui.cfg_editor.utils import loadUi, get_file_paths
|
||||
from secop.gui.cfg_editor.widgets import TabBar
|
||||
|
||||
|
||||
# TODO move secop mainwinodw to gui/client and all specific stuff
|
||||
NODE = 'node'
|
||||
MODULE = 'module'
|
||||
INTERFACE = 'interface'
|
||||
PARAMETER = 'parameter'
|
||||
PROPERTY = 'property'
|
||||
COMMENT = 'comment'
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
|
||||
def __init__(self, file_path=None, parent=None):
|
||||
QMainWindow.__init__(self, parent)
|
||||
loadUi(self, 'mainwindow.ui')
|
||||
self.tabWidget.currentChanged.connect(self.tab_relevant_btns_disable)
|
||||
if file_path is None:
|
||||
self.tab_relevant_btns_disable(-1)
|
||||
else:
|
||||
self.duplicate_btn.setEnabled(False)
|
||||
self.delete_btn.setEnabled(False)
|
||||
self.tabWidget.setTabBar(TabBar())
|
||||
self.tabWidget.tabBar().tabCloseRequested.connect(self.close_tab)
|
||||
self.open_file(file_path)
|
||||
self.new_files = 0
|
||||
|
||||
def on_actionNew(self):
|
||||
name = 'unnamed_%i.cfg' % self.new_files if self.new_files else \
|
||||
'unnamed.cfg'
|
||||
self.new_node(name)
|
||||
self.new_files += 1
|
||||
|
||||
def on_actionOpen(self):
|
||||
file_paths = get_file_paths(self)
|
||||
for file_path in file_paths:
|
||||
self.open_file(file_path)
|
||||
|
||||
def on_actionSave(self):
|
||||
self.save_tab(self.tabWidget.currentIndex())
|
||||
|
||||
def on_actionSave_as(self):
|
||||
self.save_tab(self.tabWidget.currentIndex(), True)
|
||||
|
||||
def on_action_Close(self):
|
||||
self.close_tab(self.tabWidget.currentIndex())
|
||||
|
||||
def on_actionQuit(self):
|
||||
self.close()
|
||||
|
||||
def on_actionAbout(self):
|
||||
QMessageBox.about(
|
||||
self, 'About cfg-editor',
|
||||
'''
|
||||
<h2>About cfg-editor</h2>
|
||||
<p style="font-style: italic">
|
||||
(C) 2019 MLZ instrument control
|
||||
</p>
|
||||
<p>
|
||||
cfg-editor is a graphical interface for editing
|
||||
FRAPPY-configuration-files.
|
||||
</p>
|
||||
<h3>Author:</h3>
|
||||
<ul>
|
||||
<li>Copyright (C) 2019
|
||||
<a href="mailto:sandra.seger@frm2.tum.de">Sandra Seger</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
cfg-editor is published under the
|
||||
<a href="http://www.gnu.org/licenses/gpl.html">GPL
|
||||
(GNU General Public License)</a>
|
||||
</p>
|
||||
''')
|
||||
|
||||
def on_add_module(self):
|
||||
self.tabWidget.currentWidget().tree_widget.add_module()
|
||||
|
||||
def on_add_interface(self):
|
||||
self.tabWidget.currentWidget().tree_widget.add_interface()
|
||||
|
||||
def on_add_parameter(self):
|
||||
self.tabWidget.currentWidget().tree_widget.add_parameter()
|
||||
|
||||
def on_add_property(self):
|
||||
self.tabWidget.currentWidget().tree_widget.add_property()
|
||||
|
||||
def on_add_comment(self):
|
||||
self.tabWidget.currentWidget().tree_widget.add_comment()
|
||||
|
||||
def on_duplicate(self):
|
||||
self.tabWidget.currentWidget().tree_widget.duplicate()
|
||||
|
||||
def on_delete(self):
|
||||
self.tabWidget.currentWidget().tree_widget.delete()
|
||||
|
||||
def open_file(self, file_path):
|
||||
for i in range(0, self.tabWidget.count()):
|
||||
if self.tabWidget.widget(i).tree_widget.file_path == file_path:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
return
|
||||
if file_path:
|
||||
self.new_node(os.path.basename(file_path), file_path)
|
||||
|
||||
def close_tab(self, index):
|
||||
if self.tabWidget.widget(index).saved:
|
||||
reply = QMessageBox.Close
|
||||
else:
|
||||
reply = self.show_save_message(self.tabWidget.tabText(index))
|
||||
if reply == QMessageBox.Cancel:
|
||||
return
|
||||
elif reply == QMessageBox.Save:
|
||||
self.save_tab(index)
|
||||
self.tabWidget.removeTab(index)
|
||||
|
||||
def save_tab(self, index, save_as=False):
|
||||
widget = self.tabWidget.widget(index)
|
||||
if widget.tree_widget.save(save_as):
|
||||
self.tabWidget.setTabText(index, os.path.basename(
|
||||
widget.tree_widget.file_path))
|
||||
|
||||
def closeEvent(self, event):
|
||||
if self.tabWidget.count():
|
||||
reply = None
|
||||
for i in range(0, self.tabWidget.count()):
|
||||
if not self.tabWidget.widget(i).saved:
|
||||
reply = self.show_save_message()
|
||||
break
|
||||
if not reply:
|
||||
reply = QMessageBox.Close
|
||||
if reply == QMessageBox.Cancel:
|
||||
event.ignore()
|
||||
return
|
||||
elif reply == QMessageBox.Save:
|
||||
for i in range(0, self.tabWidget.count()):
|
||||
self.save_tab(i)
|
||||
event.accept()
|
||||
|
||||
def show_save_message(self, file_name=''):
|
||||
if file_name:
|
||||
file_name = ' in "%s"' % file_name
|
||||
return QMessageBox.question(self, 'Save file?', '''
|
||||
<h2>Do you want to save changes%s?</h2>
|
||||
<p>
|
||||
Your changes will be lost if you don't save them!
|
||||
</p>
|
||||
''' % file_name,
|
||||
QMessageBox.Cancel | QMessageBox.Close |
|
||||
QMessageBox.Save, QMessageBox.Save)
|
||||
|
||||
def new_node(self, name, file_path=None):
|
||||
node = NodeDisplay(file_path)
|
||||
if node.created:
|
||||
node.tree_widget.currentItemChanged.connect(self.disable_btns)
|
||||
self.tabWidget.setCurrentIndex(self.tabWidget.addTab(node, name))
|
||||
|
||||
def disable_btns(self, current, previous):
|
||||
cur_kind = current.kind if current else None
|
||||
self.add_parameter_btn.setEnabled(True)
|
||||
self.add_property_btn.setEnabled(True)
|
||||
self.add_comment_btn.setEnabled(True)
|
||||
self.duplicate_btn.setEnabled(True)
|
||||
self.delete_btn.setEnabled(True)
|
||||
if cur_kind is None:
|
||||
self.add_parameter_btn.setEnabled(False)
|
||||
self.add_property_btn.setEnabled(False)
|
||||
self.add_comment_btn.setEnabled(False)
|
||||
self.duplicate_btn.setEnabled(False)
|
||||
self.delete_btn.setEnabled(False)
|
||||
elif cur_kind not in [MODULE, INTERFACE]:
|
||||
self.duplicate_btn.setEnabled(False)
|
||||
if cur_kind == NODE:
|
||||
self.duplicate_btn.setEnabled(False)
|
||||
self.delete_btn.setEnabled(False)
|
||||
elif cur_kind == INTERFACE:
|
||||
self.add_parameter_btn.setEnabled(False)
|
||||
elif cur_kind == PARAMETER:
|
||||
self.add_parameter_btn.setEnabled(False)
|
||||
elif cur_kind == PROPERTY:
|
||||
self.add_parameter_btn.setEnabled(False)
|
||||
self.add_property_btn.setEnabled(False)
|
||||
elif cur_kind == COMMENT:
|
||||
self.add_parameter_btn.setEnabled(False)
|
||||
self.add_property_btn.setEnabled(False)
|
||||
self.add_comment_btn.setEnabled(False)
|
||||
|
||||
def tab_relevant_btns_disable(self, index):
|
||||
if index == -1:
|
||||
enable = False
|
||||
self.duplicate_btn.setEnabled(enable)
|
||||
self.delete_btn.setEnabled(enable)
|
||||
else:
|
||||
enable = True
|
||||
self.save_btn.setEnabled(enable)
|
||||
self.add_module_btn.setEnabled(enable)
|
||||
self.add_interface_btn.setEnabled(enable)
|
||||
self.add_parameter_btn.setEnabled(enable)
|
||||
self.add_property_btn.setEnabled(enable)
|
||||
self.add_comment_btn.setEnabled(enable)
|
67
secop/gui/cfg_editor/node_display.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from secop.gui.qt import QWidget, Qt, QHBoxLayout, QSpacerItem, QSizePolicy
|
||||
from secop.gui.cfg_editor.utils import loadUi
|
||||
|
||||
|
||||
class NodeDisplay(QWidget):
|
||||
def __init__(self, file_path=None, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
loadUi(self, 'node_display.ui')
|
||||
self.saved = True if file_path else False
|
||||
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)
|
||||
self.scroll_area_layout.setAlignment(Qt.AlignTop)
|
||||
self.set_scroll_area(self.tree_widget.get_selected_item(), None)
|
||||
self.splitter.setSizes([1, 1])
|
||||
|
||||
def change_save_status(self, saved):
|
||||
self.saved = saved
|
||||
|
||||
def set_scroll_area(self, current, previous):
|
||||
self.remove_all_from_scroll_area(self.scroll_area_layout)
|
||||
self.scroll_area_layout.addWidget(current.widget)
|
||||
for index in range(0, current.childCount()):
|
||||
child_layout = QHBoxLayout()
|
||||
spacer = QSpacerItem(30, 0, QSizePolicy.Fixed,
|
||||
QSizePolicy.Minimum)
|
||||
child_layout.addSpacerItem(spacer)
|
||||
child_layout.addWidget(current.child(index).widget)
|
||||
self.scroll_area_layout.addLayout(child_layout)
|
||||
for sub_index in range(0, current.child(index).childCount()):
|
||||
sub_child_layout = QHBoxLayout()
|
||||
sub_spacer = QSpacerItem(60, 0, QSizePolicy.Fixed,
|
||||
QSizePolicy.Minimum)
|
||||
sub_child_layout.addSpacerItem(sub_spacer)
|
||||
sub_child_layout.addWidget(
|
||||
current.child(index).child(sub_index).widget)
|
||||
self.scroll_area_layout.addLayout(sub_child_layout)
|
||||
|
||||
def remove_all_from_scroll_area(self, layout):
|
||||
for index in range(layout.count()-1, -1, -1):
|
||||
item = layout.itemAt(index)
|
||||
if item.widget():
|
||||
item.widget().setParent(None)
|
||||
elif item.layout():
|
||||
self.remove_all_from_scroll_area(item.layout())
|
222
secop/gui/cfg_editor/tree_widget_item.py
Normal file
@ -0,0 +1,222 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from secop.gui.qt import QTreeWidgetItem, QFont, QWidget, QVBoxLayout, QLabel, \
|
||||
QHBoxLayout, QPushButton, QSize, QSizePolicy, QDialog, QTextEdit, pyqtSignal
|
||||
from secop.gui.cfg_editor.utils import setTreeIcon, setIcon, loadUi, \
|
||||
set_name_edit_style
|
||||
from secop.gui.valuewidgets import get_widget
|
||||
from secop.properties import Property
|
||||
|
||||
NODE = 'node'
|
||||
INTERFACE = 'interface'
|
||||
MODULE = 'module'
|
||||
PARAMETER = 'parameter'
|
||||
PROPERTY = 'property'
|
||||
COMMENT = 'comment'
|
||||
|
||||
|
||||
class TreeWidgetItem(QTreeWidgetItem):
|
||||
def __init__(self, kind=None, name='', value=None, class_object=None,
|
||||
parameters=None, properties=None, parent=None):
|
||||
"""object_class: for interfaces and modules = class
|
||||
for parameter and properties = their objects
|
||||
the datatype passed onto ValueWidget should be on of secop.datatypes"""
|
||||
# TODO: like stated in docstring the datatype for parameters and
|
||||
# properties must be found out through their object
|
||||
QTreeWidgetItem.__init__(self, parent)
|
||||
self.kind = kind
|
||||
self.name = name
|
||||
self.class_object = class_object
|
||||
self.parameters = parameters or {}
|
||||
self.properties = properties or {}
|
||||
if self.kind and self.kind != 'node':
|
||||
setTreeIcon(self, '%s.png' % self.kind)
|
||||
else:
|
||||
setTreeIcon(self, 'empty.png')
|
||||
font = QFont()
|
||||
font.setWeight(QFont.Bold)
|
||||
self.setFont(0, font)
|
||||
self.setText(0, self.name)
|
||||
self.duplicates = 0
|
||||
datatype = None if type(class_object) != Property else \
|
||||
class_object.datatype
|
||||
self.widget = ValueWidget(name, value, datatype, kind)
|
||||
if kind in [NODE, MODULE, INTERFACE]:
|
||||
self.widget.edit_btn.clicked.connect(self.change_name)
|
||||
|
||||
def addChild(self, item):
|
||||
QTreeWidgetItem.addChild(self, item)
|
||||
item.setExpanded(True)
|
||||
|
||||
def duplicate(self):
|
||||
self.duplicates += 1
|
||||
duplicate = TreeWidgetItem(self.kind, '%s_%i' % (self.name,
|
||||
self.duplicates), self.get_value(),
|
||||
self.class_object)
|
||||
self.parent().addChild(duplicate)
|
||||
for i in range(self.childCount()):
|
||||
child = self.child(i)
|
||||
duplicate.addChild(TreeWidgetItem(child.kind,
|
||||
child.name, child.widget.get_value()))
|
||||
for k in range(child.childCount()):
|
||||
sub_child = child.child(k)
|
||||
duplicate.child(i).addChild(TreeWidgetItem(sub_child.kind,
|
||||
sub_child.name,
|
||||
sub_child.widget.get_value(),
|
||||
sub_child.datatype))
|
||||
|
||||
def set_name(self, name):
|
||||
self.name = name
|
||||
self.setText(0, self.name)
|
||||
self.widget.set_name(name)
|
||||
|
||||
def get_value(self):
|
||||
return self.widget.get_value()
|
||||
|
||||
def set_value(self, value):
|
||||
self.widget.set_value(value)
|
||||
|
||||
def set_class_object(self, class_obj, value=''):
|
||||
# TODO: should do stuff here if module or interface class is changed or
|
||||
# datatype
|
||||
self.class_object = class_obj
|
||||
datatype = None if type(self.class_object) != Property else \
|
||||
self.class_object.datatype
|
||||
self.widget.set_datatype(datatype, value)
|
||||
|
||||
def get_children_names(self):
|
||||
children = []
|
||||
for i in range(0, self.childCount()):
|
||||
children.append(self.child(i).name)
|
||||
return children
|
||||
|
||||
def change_name(self):
|
||||
if self.parent():
|
||||
invalid_names = self.parent().get_children_names()
|
||||
invalid_names.remove(self.name)
|
||||
else:
|
||||
invalid_names = ['']
|
||||
dialog = ChangeNameDialog(self.name, invalid_names)
|
||||
new_name = dialog.get_values()
|
||||
if new_name:
|
||||
self.set_name(new_name)
|
||||
|
||||
|
||||
class ValueWidget(QWidget):
|
||||
|
||||
save_status_changed = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, name='', value='', datatype=None, kind='', parent=None):
|
||||
# TODO: implement: change module/interface class
|
||||
QWidget.__init__(self, parent)
|
||||
self.datatype = datatype
|
||||
self.layout = QVBoxLayout()
|
||||
self.name_label = QLabel(name)
|
||||
self.name_label.setStyleSheet('font-weight: bold')
|
||||
self.kind = kind
|
||||
if self.kind in [NODE, MODULE, INTERFACE]:
|
||||
self.edit_btn = QPushButton()
|
||||
setIcon(self.edit_btn, 'edit.png')
|
||||
self.edit_btn.setIconSize(QSize(18, 18))
|
||||
self.edit_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
self.edit_btn.setFlat(True)
|
||||
layout = QHBoxLayout()
|
||||
layout.addWidget(self.name_label)
|
||||
layout.addWidget(self.edit_btn)
|
||||
self.layout.addLayout(layout)
|
||||
else:
|
||||
self.layout.addWidget(self.name_label)
|
||||
# TODO value_display.valueChanged.connect(emit_save_status_changed) ->
|
||||
# implement or map a valueChanged signal for all possible
|
||||
# value_displays:
|
||||
# String = QLineEdit = textChanged
|
||||
# Enum = QComboBox = editTextChanged
|
||||
# Bool = QCheckBox = stateChanged
|
||||
# Int, Float = Q(Double)SpinBox = ValueChanged
|
||||
# Struct, Array = QGroupBox = clicked
|
||||
# Tuple = QFrame = ???
|
||||
if self.kind == PROPERTY and datatype and name != 'datatype':
|
||||
# TODO what to do if property is datatype
|
||||
self.value_display = get_widget(datatype)
|
||||
self.value_display.set_value(value)
|
||||
elif self.kind in [NODE, COMMENT]:
|
||||
self.value_display = QTextEdit()
|
||||
self.value_display.text = self.value_display.toPlainText
|
||||
self.value_display.setText = self.value_display.setPlainText
|
||||
self.value_display.textChanged.connect(self.emit_save_status_changed)
|
||||
self.set_value(value)
|
||||
else:
|
||||
self.value_display = QLabel(value)
|
||||
self.layout.addWidget(self.value_display)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def get_value(self):
|
||||
if self.datatype:
|
||||
return self.value_display.get_value()
|
||||
return self.value_display.text()
|
||||
|
||||
def set_value(self, value):
|
||||
# TODO try block
|
||||
if self.datatype:
|
||||
self.value_display.set_value(value)
|
||||
else:
|
||||
self.value_display.setText(value)
|
||||
|
||||
def set_name(self, name):
|
||||
if name != self.name_label.text():
|
||||
self.emit_save_status_changed(False)
|
||||
self.name_label.setText(name)
|
||||
|
||||
def set_datatype(self, datatype, value=''):
|
||||
if datatype == self.datatype:
|
||||
return
|
||||
# TODO: remove old value_display
|
||||
self.datatype = datatype
|
||||
if self.kind == PROPERTY and datatype:
|
||||
self.value_display = get_widget(datatype)
|
||||
self.value_display.set_value(value)
|
||||
else:
|
||||
self.value_display = QLabel(value)
|
||||
|
||||
def emit_save_status_changed(self, status=False):
|
||||
self.save_status_changed.emit(status)
|
||||
|
||||
|
||||
class ChangeNameDialog(QDialog):
|
||||
def __init__(self, current_name='', invalid_names=None, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
loadUi(self, 'change_name_dialog.ui')
|
||||
self.invalid_names = invalid_names
|
||||
self.name.setText(current_name)
|
||||
self.name.selectAll()
|
||||
# TODO: input mask
|
||||
self.name.textChanged.connect(self.check_input)
|
||||
|
||||
def get_values(self):
|
||||
if self.exec_() == QDialog.Accepted:
|
||||
return self.name.text()
|
||||
return None
|
||||
|
||||
def check_input(self, name):
|
||||
set_name_edit_style(name in self.invalid_names or name == '', self.name,
|
||||
self.button_box)
|
70
secop/gui/cfg_editor/ui/add_dialog.ui
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>add_dialog</class>
|
||||
<widget class="QDialog" name="add_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>250</width>
|
||||
<height>136</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>add dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="add_layout"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>add_dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>add_dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
74
secop/gui/cfg_editor/ui/change_name_dialog.ui
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>change_name_dialog</class>
|
||||
<widget class="QDialog" name="change_name_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>239</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>change name</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="name"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>change_name_dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>change_name_dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
BIN
secop/gui/cfg_editor/ui/icons/add_comment.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
secop/gui/cfg_editor/ui/icons/add_interface.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
secop/gui/cfg_editor/ui/icons/add_module.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
secop/gui/cfg_editor/ui/icons/add_parameter.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
secop/gui/cfg_editor/ui/icons/add_property.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
20
secop/gui/cfg_editor/ui/icons/cfg-editor.qrc
Normal file
@ -0,0 +1,20 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file>delete.png</file>
|
||||
<file>duplicate.png</file>
|
||||
<file>add_comment.png</file>
|
||||
<file>add_interface.png</file>
|
||||
<file>add_module.png</file>
|
||||
<file>add_parameter.png</file>
|
||||
<file>add_property.png</file>
|
||||
<file>comment.png</file>
|
||||
<file>interface.png</file>
|
||||
<file>module.png</file>
|
||||
<file>parameter.png</file>
|
||||
<file>property.png</file>
|
||||
<file>edit.png</file>
|
||||
<file>new.png</file>
|
||||
<file>open.png</file>
|
||||
<file>save.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
secop/gui/cfg_editor/ui/icons/comment.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
secop/gui/cfg_editor/ui/icons/delete.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
secop/gui/cfg_editor/ui/icons/duplicate.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
secop/gui/cfg_editor/ui/icons/edit.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
secop/gui/cfg_editor/ui/icons/empty.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
secop/gui/cfg_editor/ui/icons/interface.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
secop/gui/cfg_editor/ui/icons/module.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
secop/gui/cfg_editor/ui/icons/new.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
secop/gui/cfg_editor/ui/icons/open.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
secop/gui/cfg_editor/ui/icons/parameter.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
secop/gui/cfg_editor/ui/icons/property.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
secop/gui/cfg_editor/ui/icons/save.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
969
secop/gui/cfg_editor/ui/mainwindow.ui
Normal file
@ -0,0 +1,969 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>977</width>
|
||||
<height>799</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>cfg-editor</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_layout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="new_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>create new SEC node</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>new</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/new.png</normaloff>
|
||||
<normalon>:/new.png</normalon>:/new.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="open_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>open file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>open</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/open.png</normaloff>
|
||||
<normalon>:/open.png</normalon>:/open.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="save_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>save</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>save</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/save.png</normaloff>
|
||||
<normalon>:/save.png</normalon>:/save.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_interface_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>add interface</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add interface</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/add_interface.png</normaloff>
|
||||
<normalon>:/add_interface.png</normalon>:/add_interface.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_module_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>add module</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add module</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/add_module.png</normaloff>
|
||||
<normalon>:/add_module.png</normalon>:/add_module.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_parameter_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>add parameter</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add parameter</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/add_parameter.png</normaloff>
|
||||
<normalon>:/add_parameter.png</normalon>:/add_parameter.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_property_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>add property</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add property</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/add_property.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_comment_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>add comment</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add comment</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/add_comment.png</normaloff>:/add_comment.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="duplicate_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>duplicate module or interface</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>duplicate</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/duplicate.png</normaloff>
|
||||
<normalon>:/duplicate.png</normalon>:/duplicate.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="delete_btn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>delete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>delete</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/delete.png</normaloff>
|
||||
<normalon>:/delete.png</normalon>:/delete.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="main_layout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>977</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="actionNew"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="actionSave_as"/>
|
||||
<addaction name="action_Close"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Edit">
|
||||
<property name="title">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
<addaction name="actionAdd_interface"/>
|
||||
<addaction name="actionAdd_module"/>
|
||||
<addaction name="actionAdd_parameter"/>
|
||||
<addaction name="actionAdd_property"/>
|
||||
<addaction name="actionAdd_comment"/>
|
||||
<addaction name="actionDuplicate"/>
|
||||
<addaction name="actionDelete"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menu_Edit"/>
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionNew">
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/new.png</normaloff>
|
||||
<normalon>:/new.png</normalon>:/new.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&New</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create new file</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen">
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/open.png</normaloff>
|
||||
<normalon>:/open.png</normalon>:/open.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Open ...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave">
|
||||
<property name="icon">
|
||||
<iconset resource="icons/cfg-editor.qrc">
|
||||
<normaloff>:/save.png</normaloff>
|
||||
<normalon>:/save.png</normalon>:/save.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Save</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave_as">
|
||||
<property name="text">
|
||||
<string>Save &as ...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_SEC_node">
|
||||
<property name="text">
|
||||
<string>add SEC node</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_interface">
|
||||
<property name="text">
|
||||
<string>add interface</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_module">
|
||||
<property name="text">
|
||||
<string>add module</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_parameter">
|
||||
<property name="text">
|
||||
<string>add parameter</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_property">
|
||||
<property name="text">
|
||||
<string>add property</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_comment">
|
||||
<property name="text">
|
||||
<string>add comment</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDuplicate">
|
||||
<property name="text">
|
||||
<string>duplicate</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Close">
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F4</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="icons/cfg-editor.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>new_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionNew()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>39</x>
|
||||
<y>76</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>3</x>
|
||||
<y>91</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>open_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionOpen()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>132</x>
|
||||
<y>81</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>3</x>
|
||||
<y>45</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>save_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionSave()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>222</x>
|
||||
<y>59</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>0</x>
|
||||
<y>25</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>add_interface_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_interface()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>346</x>
|
||||
<y>83</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>328</x>
|
||||
<y>24</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>add_module_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_module()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>432</x>
|
||||
<y>61</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>401</x>
|
||||
<y>24</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>add_parameter_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_parameter()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>527</x>
|
||||
<y>76</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>499</x>
|
||||
<y>24</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>add_property_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_property()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>644</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>613</x>
|
||||
<y>24</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>add_comment_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_comment()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>732</x>
|
||||
<y>74</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>708</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>duplicate_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_duplicate()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>831</x>
|
||||
<y>87</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>818</x>
|
||||
<y>24</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>delete_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_delete()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>919</x>
|
||||
<y>55</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>909</x>
|
||||
<y>21</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionNew</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionNew()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionOpen</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionOpen()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionSave</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionSave()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionSave_as</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionSave_as()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionQuit</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionQuit()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAbout</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_actionAbout()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAdd_interface</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_interface()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAdd_module</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_module()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAdd_parameter</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_parameter()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAdd_property</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_property()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAdd_comment</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_add_comment()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDuplicate</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_duplicate()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDelete</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_delete()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>action_Close</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>on_action_Close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>488</x>
|
||||
<y>399</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>on_actionNew()</slot>
|
||||
<slot>on_add_module()</slot>
|
||||
<slot>on_add_interface()</slot>
|
||||
<slot>on_add_parameter()</slot>
|
||||
<slot>on_add_property()</slot>
|
||||
<slot>on_add_comment()</slot>
|
||||
<slot>on_actionOpen()</slot>
|
||||
<slot>on_actionSave()</slot>
|
||||
<slot>on_duplicate()</slot>
|
||||
<slot>on_delete()</slot>
|
||||
<slot>on_actionSave_as()</slot>
|
||||
<slot>on_actionQuit()</slot>
|
||||
<slot>on_actionAbout()</slot>
|
||||
<slot>on_action_Close()</slot>
|
||||
</slots>
|
||||
</ui>
|
89
secop/gui/cfg_editor/ui/node_display.ui
Normal file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>945</width>
|
||||
<height>671</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="opaqueResize">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="handleWidth">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="TreeWidget" name="tree_widget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExpandDelay">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>73</width>
|
||||
<height>651</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="scroll_area_layout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TreeWidget</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>secop.gui.cfg_editor.widgets</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
193
secop/gui/cfg_editor/utils.py
Normal file
@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
from os import path, listdir
|
||||
import sys
|
||||
import inspect
|
||||
from secop.gui.qt import uic, QIcon, QSize, QFileDialog, QDialogButtonBox
|
||||
from secop.server import getGeneralConfig
|
||||
from secop.modules import Module
|
||||
from secop.params import Parameter
|
||||
from secop.properties import Property
|
||||
from secop.protocol.interface.tcp import TCPServer
|
||||
|
||||
uipath = path.dirname(__file__)
|
||||
|
||||
|
||||
def loadUi(widget, uiname, subdir='ui'):
|
||||
uic.loadUi(path.join(uipath, subdir, uiname), widget)
|
||||
|
||||
|
||||
def setIcon(widget, icon_name, subdir='ui', icondir='icons'):
|
||||
widget.setIcon(QIcon(path.join(uipath, subdir, icondir, icon_name)))
|
||||
widget.setIconSize(QSize(60, 60))
|
||||
|
||||
|
||||
def set_name_edit_style(invalid, name_edit, button_box=None):
|
||||
if invalid:
|
||||
name_edit.setStyleSheet("color: red")
|
||||
name_edit.setToolTip('Invalid name: name already taken')
|
||||
if button_box:
|
||||
button_box.button(QDialogButtonBox.Ok).setEnabled(False)
|
||||
else:
|
||||
name_edit.setStyleSheet("color: black")
|
||||
name_edit.setToolTip('')
|
||||
if button_box:
|
||||
button_box.button(QDialogButtonBox.Ok).setEnabled(True)
|
||||
|
||||
|
||||
def setTreeIcon(widget, icon_name, subdir='ui', icondir='icons'):
|
||||
widget.setIcon(0, QIcon(path.join(uipath, subdir, icondir, icon_name)))
|
||||
|
||||
|
||||
def setActionIcon(widget, icon_name, subdir='ui', icondir='icons'):
|
||||
widget.setIcon(QIcon(path.join(uipath, subdir, icondir, icon_name)))
|
||||
|
||||
|
||||
def get_subtree_nodes(tree_widget_item):
|
||||
nodes = []
|
||||
nodes.append(tree_widget_item)
|
||||
for i in range(tree_widget_item.childCount()):
|
||||
nodes.extend(get_subtree_nodes(tree_widget_item.child(i)))
|
||||
return nodes
|
||||
|
||||
|
||||
def get_all_children_with_names(tree_widget_item):
|
||||
children = {}
|
||||
for i in range(0, tree_widget_item.childCount()):
|
||||
children[tree_widget_item.child(i).name] = tree_widget_item.child(i)
|
||||
return children
|
||||
|
||||
|
||||
def get_all_items(tree_widget):
|
||||
all_items = []
|
||||
for i in range(tree_widget.topLevelItemCount()):
|
||||
top_item = tree_widget.topLevelItem(i)
|
||||
all_items.extend(get_subtree_nodes(top_item))
|
||||
return all_items
|
||||
|
||||
|
||||
def get_file_paths(widget, open_file=True):
|
||||
dialog = QFileDialog(widget)
|
||||
if open_file:
|
||||
title = 'Open file'
|
||||
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
||||
dialog.setFileMode(QFileDialog.ExistingFiles)
|
||||
else:
|
||||
title = 'Save file'
|
||||
dialog.setAcceptMode(QFileDialog.AcceptSave)
|
||||
dialog.setFileMode(QFileDialog.AnyFile)
|
||||
dialog.setWindowTitle(title)
|
||||
dialog.setNameFilter('*.cfg')
|
||||
dialog.setDefaultSuffix('.cfg')
|
||||
dialog.exec_()
|
||||
return dialog.selectedFiles()
|
||||
|
||||
|
||||
def get_modules():
|
||||
modules = {}
|
||||
base_path = getGeneralConfig()['basedir']
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
for dirname in listdir(base_path):
|
||||
if dirname.startswith('secop_'):
|
||||
modules[dirname] = {}
|
||||
for filename in listdir(path.join(base_path, dirname)):
|
||||
if not path.isfile(path.join(base_path, dirname, filename)) or \
|
||||
filename == '__init__.py' or filename[-3:] != '.py':
|
||||
continue
|
||||
module = '%s.%s' % (dirname, filename[:-3])
|
||||
module_in_file = False
|
||||
try:
|
||||
__import__(module)
|
||||
for name, obj in inspect.getmembers(sys.modules[module]):
|
||||
if inspect.isclass(obj) and \
|
||||
obj.__module__.startswith('secop_') and \
|
||||
issubclass(obj, Module):
|
||||
# full_name = '%s.%s' % (obj.__module__, name)
|
||||
if not module_in_file:
|
||||
modules[dirname][filename[:-3]] = []
|
||||
module_in_file = True
|
||||
modules[dirname][filename[:-3]].append(name)
|
||||
except ImportError:
|
||||
pass
|
||||
return modules
|
||||
|
||||
|
||||
def get_module_class_from_name(name):
|
||||
try:
|
||||
last_dot = name.rfind('.')
|
||||
class_name = name[last_dot+1:]
|
||||
module = name[:last_dot]
|
||||
__import__(module)
|
||||
for cls_name, obj in inspect.getmembers(sys.modules[module]):
|
||||
if inspect.isclass(obj) and obj.__module__.startswith('secop_') \
|
||||
and issubclass(obj, Module) and cls_name == class_name:
|
||||
return obj
|
||||
except ImportError:
|
||||
pass
|
||||
return -1
|
||||
|
||||
|
||||
def get_interface_class_from_name(name):
|
||||
# TODO: return the class of name and not TCPServer hard coded
|
||||
return TCPServer
|
||||
|
||||
|
||||
def get_interfaces():
|
||||
# TODO class must be found out like for modules
|
||||
interfaces = []
|
||||
interface_path = path.join(getGeneralConfig()['basedir'], 'secop',
|
||||
'protocol', 'interface')
|
||||
for filename in listdir(interface_path):
|
||||
if path.isfile(path.join(interface_path, filename)) and \
|
||||
filename != '__init__.py' and filename[-3:] == '.py':
|
||||
interfaces.append(filename[:-3])
|
||||
return interfaces
|
||||
|
||||
|
||||
def get_params(info):
|
||||
"""returns all parameter of a module with all properties of all parameter
|
||||
as dictionary: {parameter name: [Parameter object, {property name, Property object}], ...}"""
|
||||
params = {}
|
||||
try:
|
||||
conf = info.configurables
|
||||
for access in info.accessibles:
|
||||
if type(info.accessibles[access]) == Parameter:
|
||||
params[access] = [info.accessibles[access], conf[access]]
|
||||
except AttributeError:
|
||||
return {}
|
||||
return params
|
||||
|
||||
|
||||
def get_props(info):
|
||||
"""returns all properties of a module class, interface class or parameter
|
||||
as dictionary: {property name: Property object, ...}"""
|
||||
props = {}
|
||||
try:
|
||||
conf = info.configurables
|
||||
for name, value in conf.items():
|
||||
if type(value) == Property:
|
||||
props[name] = value
|
||||
except AttributeError:
|
||||
return {}
|
||||
return props
|
481
secop/gui/cfg_editor/widgets.py
Normal file
@ -0,0 +1,481 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Sandra Seger <sandra.seger@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
|
||||
from secop.gui.cfg_editor.config_file import write_config, read_config
|
||||
from secop.gui.cfg_editor.tree_widget_item import TreeWidgetItem
|
||||
from secop.gui.cfg_editor.utils import get_file_paths, get_modules, \
|
||||
get_interfaces, loadUi, set_name_edit_style, get_module_class_from_name, \
|
||||
get_all_items, get_interface_class_from_name, get_params, get_props, \
|
||||
setActionIcon
|
||||
from secop.gui.qt import QWidget, QDialog, QLabel, QTabBar, Qt, QPoint, QMenu, \
|
||||
QTreeWidget, QSize, pyqtSignal, QLineEdit, QComboBox, QDialogButtonBox, \
|
||||
QTextEdit, QTreeView, QStandardItemModel, QStandardItem
|
||||
|
||||
NODE = 'node'
|
||||
MODULE = 'module'
|
||||
INTERFACE = 'interface'
|
||||
PARAMETER = 'parameter'
|
||||
PROPERTY = 'property'
|
||||
COMMENT = 'comment'
|
||||
|
||||
|
||||
class TreeWidget(QTreeWidget):
|
||||
|
||||
save_status_changed = pyqtSignal(bool)
|
||||
add_canceled = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QTreeWidget.__init__(self, parent)
|
||||
self.file_path = None
|
||||
self.setIconSize(QSize(24, 24))
|
||||
self.setSelectionMode(QTreeWidget.SingleSelection)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.context_pos = QPoint(0, 0)
|
||||
self.menu = QMenu()
|
||||
self.context_actions = []
|
||||
self.invalid_context_actions = {}
|
||||
self.setup_context_actions()
|
||||
self.customContextMenuRequested.connect(self.on_context_menu_requested)
|
||||
|
||||
def setup_context_actions(self):
|
||||
edit = self.menu.addAction('rename')
|
||||
a_m = self.menu.addAction('add module')
|
||||
a_i = self.menu.addAction('add interface')
|
||||
a_pa = self.menu.addAction('add parameter')
|
||||
a_pr = self.menu.addAction('add property')
|
||||
a_c = self.menu.addAction('add comment')
|
||||
dup = self.menu.addAction('duplicate')
|
||||
dele = self.menu.addAction('delete')
|
||||
self.context_actions = [edit, a_m, a_i, a_pa, a_pr, a_c, dup, dele]
|
||||
setActionIcon(edit, 'edit.png')
|
||||
setActionIcon(a_m, 'add_module.png')
|
||||
setActionIcon(a_i, 'add_interface.png')
|
||||
setActionIcon(a_pa, 'add_parameter.png')
|
||||
setActionIcon(a_pr, 'add_property.png')
|
||||
setActionIcon(a_c, 'add_comment.png')
|
||||
setActionIcon(dup, 'duplicate.png')
|
||||
setActionIcon(dele, 'delete.png')
|
||||
self.invalid_context_actions = {NODE: [a_pa, dup, dele],
|
||||
MODULE: [],
|
||||
INTERFACE: [a_pa],
|
||||
PARAMETER: [edit, a_pa, dup],
|
||||
PROPERTY: [edit, a_pa, a_pr, dup],
|
||||
COMMENT: [edit, a_pa, a_pr, a_c, dup],
|
||||
None: [edit, a_pa, a_pr, a_c, dup,
|
||||
dele]}
|
||||
edit.triggered.connect(self.change_name_via_context)
|
||||
a_m.triggered.connect(self.add_module)
|
||||
a_i.triggered.connect(self.add_interface)
|
||||
a_pa.triggered.connect(self.add_parameter)
|
||||
a_pr.triggered.connect(self.add_property)
|
||||
a_c.triggered.connect(self.add_comment)
|
||||
dup.triggered.connect(self.duplicate)
|
||||
dele.triggered.connect(self.delete)
|
||||
|
||||
def emit_save_status_changed(self, status):
|
||||
self.save_status_changed.emit(status)
|
||||
|
||||
def set_file(self, file_path):
|
||||
self.file_path = file_path
|
||||
if self.file_path:
|
||||
if os.path.isfile(file_path):
|
||||
self.set_tree(read_config(self.file_path))
|
||||
self.emit_save_status_changed(True)
|
||||
return True
|
||||
else:
|
||||
self.file_path = None
|
||||
return self.new_tree()
|
||||
|
||||
def new_tree(self):
|
||||
dialog = AddDialog(NODE)
|
||||
values = dialog.get_values()
|
||||
if values:
|
||||
sec_node = self.get_tree_widget_item(NODE, values[0], values[1],
|
||||
None)
|
||||
self.addTopLevelItem(sec_node)
|
||||
sec_node.setExpanded(True)
|
||||
self.mods = self.get_tree_widget_item(name='modules')
|
||||
self.ifs = self.get_tree_widget_item(name='interfaces')
|
||||
sec_node.addChild(self.ifs)
|
||||
sec_node.addChild(self.mods)
|
||||
self.emit_save_status_changed(False)
|
||||
self.setCurrentItem(sec_node)
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_tree(self, tree_items):
|
||||
self.clear()
|
||||
self.addTopLevelItem(tree_items[0])
|
||||
self.ifs = tree_items[1]
|
||||
self.mods = tree_items[2]
|
||||
self.expandAll()
|
||||
self.setCurrentItem(tree_items[0])
|
||||
for item in get_all_items(self):
|
||||
item.widget.save_status_changed.connect(self.emit_save_status_changed)
|
||||
|
||||
def add_module(self):
|
||||
dialog = AddDialog(MODULE, get_modules(),
|
||||
self.mods.get_children_names())
|
||||
values = dialog.get_values()
|
||||
if values:
|
||||
module_class = get_module_class_from_name(values[1])
|
||||
params = get_params(module_class)
|
||||
props = get_props(module_class)
|
||||
mod = self.get_tree_widget_item(MODULE, values[0], values[1],
|
||||
module_class, params, props)
|
||||
self.mods.addChild(mod)
|
||||
|
||||
# add all mandatory properties
|
||||
for pr, pr_o in props.items():
|
||||
# TODO: mandatory to must_be_configured
|
||||
if pr_o.mandatory is True:
|
||||
pr_i = self.get_tree_widget_item(PROPERTY, pr,
|
||||
pr_o.default, pr_o)
|
||||
mod.addChild(pr_i)
|
||||
# add all mandatory properties of parameter
|
||||
for pa, pa_o in params.items():
|
||||
pa_i = None
|
||||
for pr, pr_o in pa_o[1].items():
|
||||
# TODO: mandatory to must_be_configured
|
||||
if pr_o.mandatory is True:
|
||||
pr_i = self.get_tree_widget_item(PROPERTY, pr,
|
||||
pr_o.default, pr_o)
|
||||
if not pa_i:
|
||||
pa_i = self.get_tree_widget_item(PARAMETER, pa,
|
||||
class_object=pa_o[0],
|
||||
properties=pa_o[1])
|
||||
mod.addChild(pa_i)
|
||||
pa_i.addChild(pr_i)
|
||||
|
||||
self.setCurrentItem(mod)
|
||||
self.emit_save_status_changed(False)
|
||||
|
||||
def add_interface(self):
|
||||
dialog = AddDialog(INTERFACE, get_interfaces(),
|
||||
self.ifs.get_children_names())
|
||||
values = dialog.get_values()
|
||||
if values:
|
||||
interface_class = get_interface_class_from_name(values[1])
|
||||
props = get_props(interface_class)
|
||||
interface = self.get_tree_widget_item(INTERFACE, values[0],
|
||||
values[1], interface_class,
|
||||
properties=props)
|
||||
self.ifs.addChild(interface)
|
||||
# add all mandatory properties
|
||||
for pr, pr_o in props.items():
|
||||
# TODO: mandatory to must_be_configured
|
||||
if pr_o.mandatory is True:
|
||||
pr_i = self.get_tree_widget_item(PROPERTY, pr,
|
||||
pr_o.default, pr_o)
|
||||
interface.addChild(pr_i)
|
||||
self.setCurrentItem(interface)
|
||||
self.emit_save_status_changed(False)
|
||||
|
||||
def add_parameter(self):
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item or self.is_heading(selected_item) or \
|
||||
self.is_not_extendable(selected_item) or \
|
||||
selected_item.kind in [PARAMETER, INTERFACE]:
|
||||
return
|
||||
params = selected_item.parameters
|
||||
if params:
|
||||
dialog = AddDialog(PARAMETER, params.keys())
|
||||
values = dialog.get_values()
|
||||
current_children = selected_item.get_children_names()
|
||||
if values:
|
||||
if values[0] in current_children:
|
||||
self.setCurrentItem(selected_item.child(current_children.index(
|
||||
values[0])))
|
||||
else:
|
||||
param = self.get_tree_widget_item(PARAMETER, values[0],
|
||||
class_object=params[values[0]][0],
|
||||
properties=params[values[0]][1])
|
||||
selected_item.insertChild(0, param)
|
||||
self.setCurrentItem(param)
|
||||
self.emit_save_status_changed(False)
|
||||
else:
|
||||
# TODO: warning
|
||||
pass
|
||||
|
||||
def add_property(self):
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item or self.is_heading(selected_item) or \
|
||||
self.is_not_extendable(selected_item):
|
||||
return
|
||||
props = selected_item.properties
|
||||
if props:
|
||||
dialog = AddDialog(PROPERTY, props.keys())
|
||||
values = dialog.get_values()
|
||||
current_children = selected_item.get_children_names()
|
||||
if values:
|
||||
if values[0] in current_children:
|
||||
self.setCurrentItem(selected_item.child(current_children.index(
|
||||
values[0])))
|
||||
else:
|
||||
prop = self.get_tree_widget_item(PROPERTY, values[0],
|
||||
props[values[0]].default,
|
||||
props[values[0]])
|
||||
selected_item.insertChild(0, prop)
|
||||
self.setCurrentItem(prop)
|
||||
self.emit_save_status_changed(False)
|
||||
else:
|
||||
# TODO: warning
|
||||
pass
|
||||
|
||||
def add_comment(self):
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item or self.is_heading(selected_item) or \
|
||||
selected_item.kind == COMMENT:
|
||||
return
|
||||
dialog = AddDialog(COMMENT)
|
||||
values = dialog.get_values()
|
||||
if values:
|
||||
comm = self.get_tree_widget_item(COMMENT, '#', values[0])
|
||||
selected_item.insertChild(0, comm)
|
||||
self.setCurrentItem(comm)
|
||||
self.emit_save_status_changed(False)
|
||||
|
||||
def duplicate(self):
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item or selected_item.kind not in [MODULE, INTERFACE]:
|
||||
return
|
||||
selected_item.duplicate()
|
||||
# TODO set duplicated selected
|
||||
self.emit_save_status_changed(False)
|
||||
|
||||
def delete(self):
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item or self.is_heading(selected_item) \
|
||||
or selected_item == NODE:
|
||||
return
|
||||
selected_item.parent().removeChild(selected_item)
|
||||
self.emit_save_status_changed(False)
|
||||
|
||||
def save(self, save_as=False):
|
||||
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':
|
||||
self.file_path = file_name
|
||||
write_config(self.file_path, self)
|
||||
self.emit_save_status_changed(True)
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_heading(self, item):
|
||||
return item is self.ifs or item is self.mods
|
||||
|
||||
def is_not_extendable(self, item):
|
||||
return item.kind in [PROPERTY, COMMENT]
|
||||
|
||||
def get_selected_item(self):
|
||||
selected_item = self.selectedItems()
|
||||
if not selected_item:
|
||||
return None
|
||||
return selected_item[-1]
|
||||
|
||||
def is_valid_name(self, name, kind):
|
||||
if kind == MODULE:
|
||||
if name in self.mods.get_children_names():
|
||||
return False
|
||||
elif kind == INTERFACE:
|
||||
if name in self.ifs.get_children_names():
|
||||
return False
|
||||
else:
|
||||
selected_item = self.get_selected_item()
|
||||
if not selected_item:
|
||||
return False
|
||||
if name in selected_item.get_children_names():
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_tree_widget_item(self, kind=None, name='', value=None,
|
||||
class_object=None, parameters=None, properties=None):
|
||||
item = TreeWidgetItem(kind, name, value, class_object, parameters or {}, properties or {})
|
||||
item.widget.save_status_changed.connect(self.emit_save_status_changed)
|
||||
return item
|
||||
|
||||
def change_name_via_context(self):
|
||||
self.itemAt(self.context_pos).change_name()
|
||||
|
||||
def on_context_menu_requested(self, pos):
|
||||
self.context_pos = pos
|
||||
self.menu.move(self.mapToGlobal(pos))
|
||||
self.menu.show()
|
||||
for action in self.context_actions:
|
||||
action.setEnabled(True)
|
||||
for action in self.invalid_context_actions[self.itemAt(self.context_pos).kind]:
|
||||
action.setEnabled(False)
|
||||
|
||||
|
||||
class AddDialog(QDialog):
|
||||
def __init__(self, kind, possible_values=None, invalid_names=None,
|
||||
parent=None):
|
||||
"""Notes:
|
||||
self.get_value: is mapped to the specific method for getting
|
||||
the value from self.value"""
|
||||
QWidget.__init__(self, parent)
|
||||
loadUi(self, 'add_dialog.ui')
|
||||
self.setWindowTitle('add %s' % kind)
|
||||
self.kind = kind
|
||||
self.invalid_names = invalid_names
|
||||
if self.invalid_names:
|
||||
for i, name in enumerate(self.invalid_names):
|
||||
self.invalid_names[i] = name.lower()
|
||||
if kind in [NODE, MODULE, INTERFACE]:
|
||||
self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
|
||||
self.name = QLineEdit()
|
||||
# TODO: input mask
|
||||
self.name.textChanged.connect(self.check_input)
|
||||
self.add_layout.addWidget(QLabel('name:'), 0, 0)
|
||||
self.add_layout.addWidget(self.name, 0, 1)
|
||||
if kind == NODE:
|
||||
label_text = 'description:'
|
||||
self.value = QTextEdit()
|
||||
self.get_value = self.value.toPlainText
|
||||
self.value.text = self.value.toPlainText
|
||||
else:
|
||||
label_text = 'kind:'
|
||||
self.value = QComboBox()
|
||||
self.get_value = self.value.currentText
|
||||
if type(possible_values) == dict:
|
||||
# TODO disable OK Button if TreeComboBox is empty
|
||||
self.value = TreeComboBox(possible_values)
|
||||
self.get_value = self.value.get_value
|
||||
else:
|
||||
self.value.addItems(possible_values)
|
||||
self.value.setCurrentIndex(len(possible_values)-1)
|
||||
self.add_layout.addWidget(QLabel(label_text), 1, 0)
|
||||
self.add_layout.addWidget(self.value, 1, 1)
|
||||
else:
|
||||
if kind in [PARAMETER, PROPERTY]:
|
||||
label_text = 'kind:'
|
||||
self.value = QComboBox()
|
||||
self.get_value = self.value.currentText
|
||||
self.value.addItems(possible_values)
|
||||
else:
|
||||
label_text = 'comment:'
|
||||
self.value = QTextEdit()
|
||||
self.get_value = self.value.toPlainText
|
||||
self.add_layout.addWidget(QLabel(label_text), 0, 0)
|
||||
self.add_layout.addWidget(self.value, 0, 1)
|
||||
if self.add_layout.rowCount() == 2:
|
||||
self.setTabOrder(self.name, self.value)
|
||||
self.setTabOrder(self.value, self.button_box.button(QDialogButtonBox.Ok))
|
||||
self.setTabOrder(self.button_box.button(QDialogButtonBox.Ok),
|
||||
self.button_box.button(QDialogButtonBox.Cancel))
|
||||
|
||||
def get_values(self):
|
||||
if self.exec_() == QDialog.Accepted:
|
||||
if self.kind in [NODE, MODULE, INTERFACE]:
|
||||
return [self.name.text(), self.get_value()]
|
||||
elif self.kind in [PARAMETER, PROPERTY, COMMENT]:
|
||||
return [self.get_value()]
|
||||
return None
|
||||
|
||||
def check_input(self, name):
|
||||
set_name_edit_style((self.kind in [MODULE, INTERFACE] and
|
||||
name.lower() in self.invalid_names) or name == '',
|
||||
self.name, self.button_box)
|
||||
|
||||
|
||||
class TabBar(QTabBar):
|
||||
def __init__(self, parent=None):
|
||||
QTabBar.__init__(self, parent)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.context_pos = QPoint(0, 0)
|
||||
self.menu = QMenu()
|
||||
close = self.menu.addAction('&Close')
|
||||
close_all = self.menu.addAction('&Close all')
|
||||
close.triggered.connect(self.close_tab_via_context)
|
||||
close_all.triggered.connect(self.close_all)
|
||||
self.customContextMenuRequested.connect(self.on_context_menu_requested)
|
||||
self.setMovable(True)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == Qt.MidButton:
|
||||
self.close_tab_at_pos(event.pos())
|
||||
QTabBar.mouseReleaseEvent(self, event)
|
||||
|
||||
def on_context_menu_requested(self, pos):
|
||||
self.context_pos = pos
|
||||
self.menu.move(self.mapToGlobal(pos))
|
||||
self.menu.show()
|
||||
|
||||
def close_tab_via_context(self):
|
||||
self.close_tab_at_pos(self.context_pos)
|
||||
|
||||
def close_all(self):
|
||||
for i in range(self.count()-1, -1, -1):
|
||||
self.tabCloseRequested.emit(i)
|
||||
|
||||
def close_tab_at_pos(self, pos):
|
||||
self.tabCloseRequested.emit(self.tabAt(pos))
|
||||
|
||||
|
||||
class TreeComboBox(QComboBox):
|
||||
def __init__(self, value_dict, parent=None):
|
||||
QComboBox.__init__(self, parent)
|
||||
self.tree_view = QTreeView()
|
||||
self.tree_view.setHeaderHidden(True)
|
||||
self.tree_view.expanded.connect(self.resize_length)
|
||||
self.tree_view.collapsed.connect(self.resize_length)
|
||||
self.model = QStandardItemModel()
|
||||
self.insert_dict(value_dict)
|
||||
self.setModel(self.model)
|
||||
self.setView(self.tree_view)
|
||||
self.setStyleSheet('QTreeView::item:has-children{color: black;'
|
||||
'font: bold;}')
|
||||
|
||||
def insert_dict(self, value_dict, parent=None):
|
||||
for not_selectable in value_dict:
|
||||
act_item = QStandardItem(not_selectable)
|
||||
act_item.setEnabled(False)
|
||||
font = act_item.font()
|
||||
font.setBold(True)
|
||||
act_item.setFont(font)
|
||||
if parent:
|
||||
parent.appendRow([act_item])
|
||||
else:
|
||||
self.model.appendRow([act_item])
|
||||
if type(value_dict[not_selectable]) == dict:
|
||||
self.insert_dict(value_dict[not_selectable], act_item)
|
||||
else:
|
||||
for item in value_dict[not_selectable]:
|
||||
act_item.appendRow([QStandardItem(item)])
|
||||
|
||||
def get_value(self):
|
||||
value = ''
|
||||
act_index = self.tree_view.selectedIndexes()[0]
|
||||
act_item = act_index.model().itemFromIndex(act_index)
|
||||
value += act_item.text()
|
||||
while act_item.parent():
|
||||
value = '%s.%s' % (act_item.parent().text(), value)
|
||||
act_item = act_item.parent()
|
||||
return value
|
||||
|
||||
def resize_length(self):
|
||||
self.showPopup()
|
5915
secop/gui/icon_rc_qt4.py
Normal file
5960
secop/gui/icon_rc_qt5.py
Normal file
@ -33,25 +33,32 @@ try:
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QSize, QPointF, \
|
||||
QRectF
|
||||
QRectF, QPoint
|
||||
from PyQt5.QtGui import QFont, QTextCursor, QFontMetrics, QColor, QBrush, \
|
||||
QPainter, QPolygonF, QPen
|
||||
QPainter, QPolygonF, QPen, QIcon, QStandardItemModel, QStandardItem
|
||||
from PyQt5.QtWidgets import QLabel, QWidget, QDialog, QLineEdit, QCheckBox, \
|
||||
QPushButton, QSizePolicy, QMainWindow, QMessageBox, QInputDialog, \
|
||||
QTreeWidgetItem, QApplication, QGroupBox, QSpinBox, QDoubleSpinBox, \
|
||||
QComboBox, QRadioButton, QVBoxLayout, QHBoxLayout, QGridLayout, \
|
||||
QScrollArea, QFrame
|
||||
QScrollArea, QFrame, QTreeWidget, QFileDialog, QTabBar, QAction, QMenu,\
|
||||
QDialogButtonBox, QTextEdit, QAbstractItemView, QSpacerItem, QTreeView
|
||||
|
||||
from xml.sax.saxutils import escape as toHtmlEscaped
|
||||
|
||||
import secop.gui.icon_rc_qt5
|
||||
|
||||
except ImportError:
|
||||
from PyQt4 import uic
|
||||
from PyQt4.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QSize, QPointF, QRectF
|
||||
from PyQt4.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QSize, QPointF, QRectF, QPoint
|
||||
from PyQt4.QtGui import QFont, QTextCursor, QFontMetrics, \
|
||||
QLabel, QWidget, QDialog, QLineEdit, QCheckBox, QPushButton, \
|
||||
QLabel, QWidget, QDialog, QLineEdit, QCheckBox, QPushButton, QTextEdit,\
|
||||
QSizePolicy, QMainWindow, QMessageBox, QInputDialog, QTreeWidgetItem, QApplication, \
|
||||
QGroupBox, QSpinBox, QDoubleSpinBox, QComboBox, QRadioButton, QVBoxLayout, QHBoxLayout, \
|
||||
QGridLayout, QScrollArea, QFrame, QColor, QBrush, QPainter, QPolygonF, QPen
|
||||
QGridLayout, QScrollArea, QFrame, QColor, QBrush, QPainter, QPolygonF, QPen, QIcon, \
|
||||
QTreeWidget, QFileDialog, QTabBar, QAction, QMenu, QDialogButtonBox, QAbstractItemView, \
|
||||
QSpacerItem, QTreeView, QStandardItemModel, QStandardItem
|
||||
|
||||
import secop.gui.icon_rc_qt4
|
||||
|
||||
def toHtmlEscaped(s):
|
||||
return Qt.escape(s)
|
||||
|
@ -179,7 +179,7 @@ class StructWidget(QGroupBox):
|
||||
class ArrayWidget(QGroupBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(ArrayWidget, self).__init__(parent)
|
||||
self.datatype = datatype.subtype
|
||||
self.datatype = datatype.members
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
self.subwidgets = []
|
||||
|
@ -41,10 +41,12 @@ CONFIG = {
|
||||
u'piddir': os.path.join(repodir, u'pid'),
|
||||
u'logdir': os.path.join(repodir, u'log'),
|
||||
u'confdir': os.path.join(repodir, u'etc'),
|
||||
u'basedir': repodir,
|
||||
} if os.path.exists(os.path.join(repodir, u'.git')) else {
|
||||
u'piddir': u'/var/run/secop',
|
||||
u'logdir': u'/var/log',
|
||||
u'confdir': u'/etc/secop',
|
||||
u'basedir': repodir,
|
||||
}
|
||||
|
||||
|
||||
|