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>
This commit is contained in:
Seger Sandra 2019-03-19 10:39:41 +01:00 committed by Enrico Faulhaber
parent 0bf43d67ba
commit e1f017d678
35 changed files with 14621 additions and 7 deletions

46
bin/cfg-editor Executable file
View 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))

View File

View 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:])

View 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)

View 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())

View 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)

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View 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>&amp;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>&amp;Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menu_Edit">
<property name="title">
<string>&amp;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>&amp;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>&amp;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>&amp;Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionSave_as">
<property name="text">
<string>Save &amp;as ...</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+S</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>&amp;About</string>
</property>
<property name="shortcut">
<string>Ctrl+A</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>&amp;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>&amp;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>

View 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>

View 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

View 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

File diff suppressed because it is too large Load Diff

5960
secop/gui/icon_rc_qt5.py Normal file

File diff suppressed because it is too large Load Diff

View 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)

View File

@ -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 = []

View File

@ -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,
}