From 67b86d303bdc46d4df38344ca47f3e28977d66ae Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Tue, 4 Feb 2020 15:52:58 +0300 Subject: [PATCH] Added dialog for existing configs and server config dialog, improved config.py --- Server/config_editor_models.py | 87 +++++++++++++++++++++------------- Server/server_qt.py | 14 ++++++ config.py | 38 +++++++++------ 3 files changed, 92 insertions(+), 47 deletions(-) diff --git a/Server/config_editor_models.py b/Server/config_editor_models.py index 8471878..45699af 100644 --- a/Server/config_editor_models.py +++ b/Server/config_editor_models.py @@ -158,14 +158,14 @@ class ConfigModelItem: if self.spec_default is not None and self.data(1) == self.spec_default \ and self.data(0) == self.default_values[0] and self.type != 'section': self.set_state('default') - print('def', self.data(1), self.data(0), self.spec_default) + # print('def', self.data(1), self.data(0), self.spec_default) child_states = [child.state for child in self.childItems] if any(state in child_states for state in ['edited', 'added', 'deleted']): self.state = 'edited' if len(set(child_states)) == 1: # if all states equal self.set_state(child_states[0], set_children=False) - print(child_states) + # print(child_states) if self.parentItem is not None: self.parentItem.check_state() @@ -285,7 +285,7 @@ class ConfigModel(QtCore.QAbstractItemModel): childItem = index.internalPointer() if not isinstance(childItem, ConfigModelItem): - print(childItem, index.column()), # index.row(), index.parent().internalPointer()) + # print(childItem, index.column()), # index.row(), index.parent().internalPointer()) return QtCore.QModelIndex() parentItem = childItem.parent() @@ -744,10 +744,15 @@ class ConfigDialog(QtWidgets.QDialog): super(ConfigDialog, self).__init__(parent) self.ui = config_editor.Ui_config_dialog() self.model = ConfigModel(widget=self) + self._filename = None self.unsaved = False self.setupUi() self.copter_editor_signal.connect(self._call_copter_dialog) + @property + def filename(self): + return self._filename or 'Untitled.ini' + def setupModel(self, data, pure_dict=False, convert_types=False): if pure_dict: self.model.dict_setup(data, convert_types=convert_types) @@ -774,10 +779,12 @@ class ConfigDialog(QtWidgets.QDialog): # self.ui.delete_button.pressed.connect(self.remove_selected) + def update_title(self): + self.setWindowTitle(f"Config editor - {self.filename}" + "*"*self.unsaved) + def unsaved_call(self): - name = self.windowTitle()+'*' - self.setWindowTitle(name) self.unsaved = True + self.update_title() self.model.dataChanged.disconnect(self.unsaved_call) def closeEvent(self, event): @@ -803,13 +810,14 @@ class ConfigDialog(QtWidgets.QDialog): return reply == QMessageBox.Yes def save_as(self): - cfg = config.ConfigManager() - cfg.load_from_dict(self.model.to_config_dict()) save_path = QFileDialog.getSaveFileName(self, "Save as configuration file", + directory=self.filename, filter="Config files (*.ini)")[0] if not save_path: return + cfg = config.ConfigManager() + cfg.load_from_dict(self.model.to_config_dict()) cfg.config.filename = save_path cfg.write() @@ -841,21 +849,36 @@ class ConfigDialog(QtWidgets.QDialog): @pyqtSlot(object, object) def _call_copter_dialog(self, client, value): - logging.info("Opening dialog") + logging.info("Opening copter config dialog") config_dict, spec_dict = value["config"], value["configspec"] cfg = config.ConfigManager() cfg.load_from_dict(config_dict, spec_dict) - self.setupModel(cfg.full_dict(include_defaults=True)) - if not self.validation_loop(cfg, spec_dict): - return False + def save_callback(): + edited_dict = cfg.full_dict(include_defaults=False) + client.send_message("config", {"config": edited_dict, "mode": "rewrite"}) - edited_dict = self.model.to_config_dict() - client.send_message("config", {"config": edited_dict, "mode": "rewrite"}) - - if self.ui.do_restart.isChecked(): + def restart_callback(): client.send_message("service_restart", {"name": "clever-show"}) + if not self.call_config_dialog(cfg, save_callback, restart_callback, f"{client.copter_id}"): + return False + return True + + def call_config_dialog(self, cfg: config.ConfigManager, on_save=None, on_restart=None, name="Untitled.ini"): + self.setupModel(cfg.full_dict(include_defaults=True), convert_types=(not cfg.validated)) + self.ui.do_restart.setEnabled(on_restart is not None) + self._filename = name + self.update_title() + + if not self.validation_loop(cfg, cfg.config.configspec): + return False + + if on_save is not None: + on_save() + + if on_restart is not None and self.ui.do_restart.isChecked(): + on_restart() return True def call_standalone_dialog(self): @@ -872,26 +895,26 @@ class ConfigDialog(QtWidgets.QDialog): "Config cannot be opened or validated: {}".format(error)) return False - self.setupModel(cfg.full_dict(include_defaults=True), convert_types=(not cfg.validated)) - self.ui.do_restart.setDisabled(True) + def save_callback(): + if cfg.config.filename is None: + save_path = QFileDialog.getSaveFileName(self, "Save configuration file", + directory=self.filename, + filter="Config files (*.ini)")[0] + if not save_path: + return False + else: + save_path = cfg.config.filename - filename = cfg.config.filename - validation_path = path if cfg.config.filename is None else cfg.config.filename - validation_path = validation_path if cfg.validated else None + cfg.config.filename = save_path + cfg.write() - if not self.validation_loop(cfg, validation_path): + if cfg.config.filename is not None: + name = os.path.split(cfg.config.filename)[1] + else: # when editing only configspec-based file + name = os.path.split(path)[1] + + if not self.call_config_dialog(cfg, on_save=save_callback, name=name): return False - - if filename is None: - save_path = QFileDialog.getSaveFileName(self, "Save configuration file", - filter="Config files (*.ini)")[0] - if not save_path: - return False - else: - save_path = filename - - cfg.config.filename = save_path - cfg.write() return True diff --git a/Server/server_qt.py b/Server/server_qt.py index 5c41637..1e47482 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -41,6 +41,7 @@ def b_partial(func, *args, **kwargs): # call argument blocker partial def restart(): # move to core + # ANY prints will break restarting or opening new windows after restart os.execl(sys.executable, os.path.abspath(__file__), *sys.argv) @@ -135,6 +136,10 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.action_play_music.triggered.connect(self.play_music) self.ui.action_stop_music.triggered.connect(self.stop_music) + self.ui.action_edit_any_config.triggered.connect(ConfigDialog.call_standalone_dialog) + self.ui.action_edit_server_config.triggered.connect(self.edit_server_config) + + self.ui.action_restart_server.triggered.connect(restart) self.ui.action_update_server_git.triggered.connect(update_server) @@ -589,6 +594,15 @@ class MainWindow(QtWidgets.QMainWindow): def configure_columns(self): HeaderEditDialog(self.ui.copter_table, self.server.config).exec() + @pyqtSlot() + def edit_server_config(self): + config = self.server.config + + def save_callback(): + config.write() + + ConfigDialog().call_config_dialog(config, save_callback, restart, name="Server config") + def register_callbacks(self): @messaging.message_callback("telemetry") def get_telem_data(client, value, **kwargs): diff --git a/config.py b/config.py index d1c878f..047ed39 100644 --- a/config.py +++ b/config.py @@ -48,7 +48,6 @@ class ValidationError(ValueError): class ConfigManager: def __init__(self, config=None): self.config = ConfigObj() if config is None else config - self.validated = False self._name_dict = {} @@ -75,13 +74,16 @@ class ConfigManager: def write(self): self.config.write() + @property + def validated(self): + return self.config.configspec is not None + def set_config(self, config): self.config = config self._name_dict = self.flatten_keys(config) - self.validated = False def validate_config(self, config=None, copy_defaults=False): - config = config or self.config + config = self.config if config is None else config vdt = Validator() test = config.validate(vdt, copy=copy_defaults, preserve_errors=True) @@ -89,7 +91,6 @@ class ConfigManager: raise ValidationError('Some config values are wrong', config, test) self.set_config(config) - self.validated = True @classmethod def _full_dict(cls, item, include_defaults=False): @@ -263,21 +264,27 @@ class ConfigManager: final_comment = d.pop('final_comment', ['']) kwargs = {'infile': self._extract_values(d), 'indent_type': ''} + filename = None if isinstance(configspec, dict): kwargs.update({'configspec': configspec}) elif isinstance(configspec, str): - spec_path = self._get_spec_path(configspec) # check for /spec, then for config - if not self._config_exists(spec_path): - spec_path = configspec - if self._config_exists(spec_path): + spec_path = self._get_spec_path(configspec) + if self._config_exists(spec_path): # when 'configspec' points to configuration file and configspec exists kwargs.update({'configspec': spec_path}) + filename = configspec + elif self._config_exists(configspec): # when 'configspec' points to configspec file + kwargs.update({'configspec': configspec}) + if parent_dir(configspec) == 'spec': + filename = self._get_config_path(configspec) + else: + raise ValueError("Configspec does not exist") config = ConfigObj(**kwargs) - config.filename = configspec if isinstance(configspec, str) else None + config.filename = filename config.initial_comment = initial_comment config.final_comment = final_comment - if configspec is not None: + if config.configspec is not None: self.validate_config(config) else: self.set_config(config) @@ -318,13 +325,14 @@ if __name__ == '__main__': import pprint #pprint.pprint(cfg.full_dict) cfg2 = ConfigManager() - cfg2.load_from_dict({"PRIVATE": {"offset": [1, 2, 3]}}, configspec='Drone/config/spec/configspec_client.ini') - #cfg2.load_from_dict({"PRIVATE": {"id": 123132}}) + #cfg2.load_from_dict({"PRIVATE": {"offset": [1, 2, 3]}}, configspec='Drone/config/spec/configspec_client.ini') + cfg2.load_from_dict({"PRIVATE": {"id": "heh"}}) #pprint.pprint(cfg2.full_dict) - cfg.merge(cfg2) + #cfg.merge(cfg2) #pprint.pprint(cfg.full_dict) - print(cfg.full_dict(include_defaults=True)) - print(dict(cfg.config.configspec)) + print(cfg2.full_dict(include_defaults=True)) + #print(dict(cfg2.config.configspec)) + print(cfg2.config.PRIVATE) #print(dict(ConfigManager(cfg.config.configspec).config)) # #print(cfg.full_dict)