diff --git a/Drone/client.py b/Drone/client.py index 1de5035..3675660 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -224,7 +224,7 @@ class Client(object): @messaging.message_callback("config_write") def _command_config_write(*args, **kwargs): options = [ConfigOption(**raw_option) for raw_option in kwargs["options"]] - logger.info("Writing config options: {}".format(options)) + logger.info("Writing config_attrs options: {}".format(options)) active_client.write_config(kwargs["reload"], *options) diff --git a/Drone/config/client.ini b/Drone/config/client.ini new file mode 100644 index 0000000..29c6d69 --- /dev/null +++ b/Drone/config/client.ini @@ -0,0 +1,19 @@ +# This is generated config_attrs with defaults +# Modify to configure +[SERVER] +port = 25000 +host = 192.168.1.101 +buffer_size = 1024 + +[BROADCAST] +use = True +port = 8181 + +[NTP] +use = False +host = ntp1.stratum2.ru +port = 123n + +[PRIVATE] +# avialiable options: /hostname ; /default ; /ip ; any string 63 characters lengh +#id = /hostname diff --git a/Drone/configs/configspec_client.ini b/Drone/config/spec/configspec_client.ini similarity index 71% rename from Drone/configs/configspec_client.ini rename to Drone/config/spec/configspec_client.ini index a6b75b6..870f205 100644 --- a/Drone/configs/configspec_client.ini +++ b/Drone/config/spec/configspec_client.ini @@ -13,4 +13,5 @@ host = string(default=ntp1.stratum2.ru) port = integer(default=123) [PRIVATE] -id = string(default=/hostname, max=63) \ No newline at end of file +# avialiable options: /hostname ; /default ; /ip ; any string 63 characters lengh +id = string(default=/hostname, max=63) diff --git a/Server/config_editor_models.py b/Server/config_editor_models.py index 102c57c..47f3a65 100644 --- a/Server/config_editor_models.py +++ b/Server/config_editor_models.py @@ -4,10 +4,12 @@ from PyQt5.QtCore import Qt as Qt class ConfigModelItem: - def __init__(self, label, value="", parent=None): - self.parentItem = parent + def __init__(self, label, value="", is_section=False, parent=None): self.itemData = [label, value] + + self.is_section=is_section self.childItems = [] + self.parentItem = parent if self.parentItem is not None: self.parentItem.appendChild(self) @@ -64,12 +66,6 @@ class ConfigModel(QtCore.QAbstractItemModel): 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) @@ -143,12 +139,11 @@ class ConfigModel(QtCore.QAbstractItemModel): def flags(self, index): if not index.isValid(): return Qt.NoItemFlags - childItem = index.internalPointer() - parentItem = childItem.parent() + item = index.internalPointer() flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable - if index.column() == 1 and parentItem != self.rootItem: + if index.column() == 1 and not item.is_section: flags |= Qt.ItemIsEditable return flags @@ -168,13 +163,18 @@ class ConfigModel(QtCore.QAbstractItemModel): 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 setup(self, data: dict, parent=None): + if parent is None: + parent = self.rootItem - def to_dict(self): + for key, value in data.items(): + if isinstance(value, dict): + item = ConfigModelItem(key, parent=parent, is_section=True) + self.setup(value, parent=item) + else: + parent.appendChild(ConfigModelItem(key, value)) + + def to_dict(self): # TODO recursive d = {} for section in self.rootItem.childItems: section_d = {} @@ -221,8 +221,8 @@ if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) Dialog = QtWidgets.QDialog() - data = {"section 1": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": ""}, - "section 2": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": ""}} + data = {"section 1": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": {'subopt': 'bal'}}, + "section 2": {"opt1": "str", "opt2": [1.1, 2.3, 34], "opt3": 1.23, "opt4": False, "...": ""}} ui = ConfigDialog(data) ui.setupUi(Dialog) @@ -230,4 +230,8 @@ if __name__ == '__main__': Dialog.show() print(app.exec_()) + + print(Dialog.result()) + print(ui.model.to_dict()) + sys.exit() diff --git a/config.py b/config.py index 910d156..f265807 100644 --- a/config.py +++ b/config.py @@ -1,64 +1,109 @@ -import collections import os -from configobj import ConfigObj + +from functools import partial +from configobj import ConfigObj, Section from validate import Validator -ConfigOption = collections.namedtuple("ConfigOption", ["section", "option", "value"]) + +def modify_filename(path, pattern): + old_path, filename = os.path.split(path) + filename = os.path.splitext(filename)[0] + newfilename = pattern.format(filename) + return os.path.join(old_path, newfilename) class ConfigManager: def __init__(self): - self.configs = {} + self.config = ConfigObj() - @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): + self.generate_default_config(path) - def load_config(self, path): # todo maybe automatic config path vdt = Validator() + config = ConfigObj(infile=path, raise_errors=True, + configspec=modify_filename(path, 'spec/configspec_{}.ini')) + test = config.validate(vdt) + print(test) + print(config) + self.config = config - 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 get(self, section, option): + return self.config[section][option] + def set(self, section, option, value, write=False): + self.config[section][option] = value + if write: + self.write() - - 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) + def write(self): + self.config.write() @classmethod - def transform(cls, section, key): - value = cls.getvalue(section, key) - print(value) - section[key] = value + def _get_defaults(cls, item, unchanged_only=False): + if isinstance(item, Section): + default_values = item.default_values.copy() + + if unchanged_only: + default_list = item.defaults.copy() + defaults = {key: default_values[key] for key in default_list if key in default_values} + else: + defaults = default_values + + for key, value in item.items(): + result = cls._get_defaults(value, unchanged_only=unchanged_only) + if result is not None: + defaults[key] = result + + return defaults if defaults else None + + @property + def default_values(self): + return self._get_defaults(self.config) or {} + + @property + def unchanged_defaults(self): + return self._get_defaults(self.config, unchanged_only=True) or {} + + @staticmethod + def generate_default_config(path): + if os.path.isfile(path): + return + + vdt = Validator() + config = ConfigObj(configspec=modify_filename(path, 'spec/configspec_{}.ini')) + config.filename = path + config.validate(vdt, copy=True) + config.initial_comment = ('This is generated config_attrs with defaults', + 'Modify to configure') + config.write() + + def __getattr__(self, item): + try: + section, option = item.split('_', 1) + return self.config[section.upper()][option.lower()] + except (ValueError, KeyError): + return self.__dict__[item] + + def __setattr__(self, key, value): + try: + section, option = key.split('_', 1) + self.config[section.upper()][option.lower()] = value + except (ValueError, KeyError): + self.__dict__[key] = value + if __name__ == '__main__': cfg = ConfigManager() - #open('Drone/default_clinet_config.ini') - cfg.load_config('Drone/configs/clinet_config.ini') + cfg.load_config('Drone/config/client.ini') + + print(cfg.server_host) + cfg.server_host = '192.168.1.103' + + print(cfg.get('SERVER', 'host')) + cfg.set('SERVER', 'host', '192.168.1.103') + + print(cfg.config) + print(cfg.default_values) + print(cfg.unchanged_defaults) + #print(cfg.con) +