From 3dc665bd006df1348103b964a14185d9e7b86d2c Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Mon, 25 Nov 2019 23:19:07 +0300 Subject: [PATCH] WIP --- Drone/client.py | 1 + Drone/configs/configspec_client.ini | 16 +++ Drone/configs/default_clinet_config.ini | 19 +++ Server/config_editor.py | 56 ++++----- Server/config_editor.ui | 14 +-- Server/config_editor_models.py | 152 +++++++++++++++++++++--- config.py | 64 ++++++++++ 7 files changed, 271 insertions(+), 51 deletions(-) create mode 100644 Drone/configs/configspec_client.ini create mode 100644 Drone/configs/default_clinet_config.ini create mode 100644 config.py diff --git a/Drone/client.py b/Drone/client.py index a52e704..1de5035 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -20,6 +20,7 @@ sys.path.insert(0, parent_dir) logger = logging.getLogger(__name__) import messaging_lib as messaging +import config ConfigOption = collections.namedtuple("ConfigOption", ["section", "option", "value"]) diff --git a/Drone/configs/configspec_client.ini b/Drone/configs/configspec_client.ini new file mode 100644 index 0000000..a6b75b6 --- /dev/null +++ b/Drone/configs/configspec_client.ini @@ -0,0 +1,16 @@ +[SERVER] +port = integer(default=25000) +host = ip_addr(default=192.168.1.101) #string? +buffer_size = integer(default=1024) + +[BROADCAST] +use = boolean(default=True) +port = integer(default=8181) + +[NTP] +use = boolean(default=False) +host = string(default=ntp1.stratum2.ru) +port = integer(default=123) + +[PRIVATE] +id = string(default=/hostname, max=63) \ No newline at end of file diff --git a/Drone/configs/default_clinet_config.ini b/Drone/configs/default_clinet_config.ini new file mode 100644 index 0000000..fbf49e2 --- /dev/null +++ b/Drone/configs/default_clinet_config.ini @@ -0,0 +1,19 @@ +# DO NOT EDIT this file in production +# Use non-default config files for configuration purposes + +[SERVER] +port = 25000 +host = 192.168.1.101 +buffer_size = 1024 + +[BROADCAST] +use = True +port = 8181 + +[NTP] +use = False +host = ntp1.stratum2.ru +port = 123 + +[PRIVATE] +id = /hostname \ No newline at end of file diff --git a/Server/config_editor.py b/Server/config_editor.py index ecad213..2ace49f 100644 --- a/Server/config_editor.py +++ b/Server/config_editor.py @@ -10,25 +10,25 @@ from PyQt5 import QtCore, QtGui, QtWidgets -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(268, 247) - Dialog.setModal(False) - self.gridLayout = QtWidgets.QGridLayout(Dialog) +class Ui_config_dialog(object): + def setupUi(self, config_dialog): + config_dialog.setObjectName("config_dialog") + config_dialog.resize(317, 247) + config_dialog.setModal(False) + self.gridLayout = QtWidgets.QGridLayout(config_dialog) self.gridLayout.setObjectName("gridLayout") - self.treeView = QtWidgets.QTreeView(Dialog) - self.treeView.setObjectName("treeView") - self.gridLayout.addWidget(self.treeView, 0, 0, 1, 1) + self.config_view = QtWidgets.QTreeView(config_dialog) + self.config_view.setObjectName("config_view") + self.gridLayout.addWidget(self.config_view, 0, 0, 1, 1) self.gridLayout_2 = QtWidgets.QGridLayout() self.gridLayout_2.setObjectName("gridLayout_2") - self.pushButton = QtWidgets.QPushButton(Dialog) - self.pushButton.setObjectName("pushButton") - self.gridLayout_2.addWidget(self.pushButton, 0, 0, 1, 1) - self.checkBox = QtWidgets.QCheckBox(Dialog) + self.delete_button = QtWidgets.QPushButton(config_dialog) + self.delete_button.setObjectName("delete_button") + self.gridLayout_2.addWidget(self.delete_button, 0, 0, 1, 1) + self.checkBox = QtWidgets.QCheckBox(config_dialog) self.checkBox.setObjectName("checkBox") self.gridLayout_2.addWidget(self.checkBox, 1, 0, 1, 1) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + self.buttonBox = QtWidgets.QDialogButtonBox(config_dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save) self.buttonBox.setCenterButtons(False) @@ -36,25 +36,25 @@ class Ui_Dialog(object): self.gridLayout_2.addWidget(self.buttonBox, 1, 1, 1, 1) self.gridLayout.addLayout(self.gridLayout_2, 1, 0, 1, 1) - self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) + self.retranslateUi(config_dialog) + self.buttonBox.accepted.connect(config_dialog.accept) + self.buttonBox.rejected.connect(config_dialog.reject) + QtCore.QMetaObject.connectSlotsByName(config_dialog) - def retranslateUi(self, Dialog): + def retranslateUi(self, config_dialog): _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Config Editor")) - self.pushButton.setText(_translate("Dialog", "Delete")) - self.pushButton.setShortcut(_translate("Dialog", "Del")) - self.checkBox.setText(_translate("Dialog", "Restart")) - self.checkBox.setShortcut(_translate("Dialog", "R")) + config_dialog.setWindowTitle(_translate("config_dialog", "Config Editor")) + self.delete_button.setText(_translate("config_dialog", "Delete")) + self.delete_button.setShortcut(_translate("config_dialog", "Del")) + self.checkBox.setText(_translate("config_dialog", "Restart")) + self.checkBox.setShortcut(_translate("config_dialog", "R")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - Dialog = QtWidgets.QDialog() - ui = Ui_Dialog() - ui.setupUi(Dialog) - Dialog.show() + config_dialog = QtWidgets.QDialog() + ui = Ui_config_dialog() + ui.setupUi(config_dialog) + config_dialog.show() sys.exit(app.exec_()) diff --git a/Server/config_editor.ui b/Server/config_editor.ui index dc3b4f5..a65b2b1 100644 --- a/Server/config_editor.ui +++ b/Server/config_editor.ui @@ -1,12 +1,12 @@ - Dialog - + config_dialog + 0 0 - 268 + 317 247 @@ -18,12 +18,12 @@ - + - + Delete @@ -64,7 +64,7 @@ buttonBox accepted() - Dialog + config_dialog accept() @@ -80,7 +80,7 @@ buttonBox rejected() - Dialog + config_dialog reject() diff --git a/Server/config_editor_models.py b/Server/config_editor_models.py index 1a82ea3..102c57c 100644 --- a/Server/config_editor_models.py +++ b/Server/config_editor_models.py @@ -4,13 +4,17 @@ from PyQt5.QtCore import Qt as Qt class ConfigModelItem: - def __init__(self, data: list, parent=None): + def __init__(self, label, value="", parent=None): self.parentItem = parent - self.itemData = data + self.itemData = [label, value] self.childItems = [] + if self.parentItem is not None: + self.parentItem.appendChild(self) + def appendChild(self, item): self.childItems.append(item) + item.parentItem = self def child(self, row): return self.childItems[row] @@ -19,7 +23,7 @@ class ConfigModelItem: return len(self.childItems) def columnCount(self): - return len(self.itemData) + return 2 def data(self, column): try: @@ -27,32 +31,51 @@ class ConfigModelItem: except IndexError: return None + def set_data(self, data, column): + try: + self.itemData[column] = data + except IndexError: + return False + + return True + def parent(self): return self.parentItem def row(self): - if self.parentItem: + if self.parentItem is not None: return self.parentItem.childItems.index(self) return 0 + def removeChild(self, position): + if position < 0 or position > len(self.childItems): + return False + + child = self.childItems.pop(position) + child.parentItem = None + return True + + class ConfigModel(QtCore.QAbstractItemModel): def __init__(self, data, parent=None): super(ConfigModel, self).__init__(parent) - self.rootItem = ConfigModelItem(("Option", "Value")) - #self.setupModelData(data.split('\n'), self.rootItem) - self.rootItem.appendChild(ConfigModelItem(("1314", "345"))) + self.rootItem = ConfigModelItem("Option", "Value") + self.setup(data) + + #i = ConfigModelItem("1314", "") + #self.rootItem.appendChild(i) + #i.appendChild(ConfigModelItem("36hhj", "34566")) + #i.appendChild(ConfigModelItem("36hhj", "34566")) + def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.rootItem.data(section) def columnCount(self, parent): - if parent.isValid(): - return parent.internalPointer().columnCount() - else: - return self.rootItem.columnCount() + return 2 def rowCount(self, parent): if parent.column() > 0: @@ -93,6 +116,99 @@ class ConfigModel(QtCore.QAbstractItemModel): return self.createIndex(parentItem.row(), 0, parentItem) + def data(self, index, role): + if not index.isValid(): + return None + + item = index.internalPointer() + + if role == Qt.DisplayRole or role == Qt.EditRole: + return item.data(index.column()) + + return None + + @QtCore.pyqtSlot() + def setData(self, index, value, role=Qt.EditRole): + if not index.isValid(): + return False + + item = index.internalPointer() + if role == Qt.EditRole: + item.set_data(value, index.column()) + + self.dataChanged.emit(index, index, (role,)) + + return True + + def flags(self, index): + if not index.isValid(): + return Qt.NoItemFlags + childItem = index.internalPointer() + parentItem = childItem.parent() + + flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable + + if index.column() == 1 and parentItem != self.rootItem: + flags |= Qt.ItemIsEditable + + return flags + + @QtCore.pyqtSlot() + def removeRow(self, index): + parent = index.parent() + self.beginRemoveRows(parent, index.row(), index.row()) + + if not parent.isValid(): + parentNode = self.rootItem + else: + parentNode = parent.internalPointer() + + parentNode.removeChild(index.row()) + + self.endRemoveRows() + return True + + def setup(self, d: dict): + for section, options in d.items(): + section_item = ConfigModelItem(section, parent=self.rootItem) + for option, value in options.items(): + section_item.appendChild(ConfigModelItem(option, value)) + + def to_dict(self): + d = {} + for section in self.rootItem.childItems: + section_d = {} + section_name, _ = section.itemData + + for item in section.childItems: + option, value = item.itemData + section_d[option] = value + + d[section_name] = section_d + + return d + + +class ConfigDialog(config_editor.Ui_config_dialog): + def __init__(self, data): + super(ConfigDialog, self).__init__() + self.model = ConfigModel(data) + + def setupUi(self, config_dialog): + super(ConfigDialog, self).setupUi(config_dialog) + + self.config_view.setModel(self.model) + self.config_view.expandAll() + + self.delete_button.pressed.connect(self.remove_selected) + + def remove_selected(self): + index = self.config_view.selectedIndexes()[0] + self.model.removeRow(index) + + #print(self.model.to_dict()) + + if __name__ == '__main__': import sys @@ -102,12 +218,16 @@ if __name__ == '__main__': sys.excepthook = except_hook - m = ConfigModel([12313, 123]) - app = QtWidgets.QApplication(sys.argv) Dialog = QtWidgets.QDialog() - ui = config_editor.Ui_Dialog() + + data = {"section 1": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": ""}, + "section 2": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": ""}} + + ui = ConfigDialog(data) ui.setupUi(Dialog) - ui.treeView.setModel(m) + + Dialog.show() - sys.exit(app.exec_()) + print(app.exec_()) + sys.exit() diff --git a/config.py b/config.py new file mode 100644 index 0000000..910d156 --- /dev/null +++ b/config.py @@ -0,0 +1,64 @@ +import collections +import os +from configobj import ConfigObj +from validate import Validator + +ConfigOption = collections.namedtuple("ConfigOption", ["section", "option", "value"]) + + +class ConfigManager: + def __init__(self): + self.configs = {} + + @staticmethod + def _get_default_path(path): + old_path, filename = os.path.split(path) + filename = os.path.splitext(filename)[0] + newfilename = "default_{}.ini".format(filename) + print(os.path.join(old_path, newfilename)) + return os.path.join(old_path, newfilename) + + def load_config(self, path): # todo maybe automatic config path + vdt = Validator() + + default_config = ConfigObj( + infile=self._get_default_path(path), configspec="Drone/configs/configspec_client.ini") + default_config.validate(vdt) + print(default_config) + default_config.walk(self.transform) + print(default_config.dict()) + #default_config = configparser.ConfigParser(co) + #default_config.read(default_path) + + + + def create_empty_config(self, path): + with open(path, 'w') as f: + f.write("# Write here any configurations to replace default values \n\n") + + @staticmethod + def getvalue(section, key): + try: + return section.as_int(key) + except ValueError: + pass + try: + return section.as_float(key) + except ValueError: + pass + try: + return section.as_bool(key) + except ValueError: + pass + return section.get(key) + + @classmethod + def transform(cls, section, key): + value = cls.getvalue(section, key) + print(value) + section[key] = value + +if __name__ == '__main__': + cfg = ConfigManager() + #open('Drone/default_clinet_config.ini') + cfg.load_config('Drone/configs/clinet_config.ini')