From fe38e8257334b88bad82b790cbce3a03d3bb10b0 Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Sat, 15 Feb 2020 00:41:43 +0300 Subject: [PATCH] Added ability to save and load widths of columns to config --- Server/config/spec/configspec_server.ini | 28 ++++----- Server/copter_table.py | 80 ++++++++++++++++-------- Server/copter_table_models.py | 2 - config.py | 15 ++++- update_configspec.py | 9 ++- 5 files changed, 87 insertions(+), 47 deletions(-) diff --git a/Server/config/spec/configspec_server.ini b/Server/config/spec/configspec_server.ini index fe8cd18..8c8e96a 100644 --- a/Server/config/spec/configspec_server.ini +++ b/Server/config/spec/configspec_server.ini @@ -12,21 +12,21 @@ config_version = float(default='0.0') [[PRESETS]] current = string(default="DEFAULT") [[[DEFAULT]]] - copter_id = boolean(default=True) - git_version = boolean(default=True) - animation_id = boolean(default=True) - battery = boolean(default=True) - fcu_status = boolean(default=True) - calibration_status = boolean(default=True) - mode = boolean(default=True) - selfcheck = boolean(default=True) - current_position = boolean(default=True) - start_position = boolean(default=True) - last_task = boolean(default=True) - time_delta = boolean(default=True) - config_version = boolean(default=True) + copter_id = preset_param(default=list(True, 150)) + git_version = preset_param(default=list(True, 100)) + config_version = preset_param(default=list(True, 100)) + animation_id = preset_param(default=list(True, 100)) + battery = preset_param(default=list(True, 100)) + fcu_status = preset_param(default=list(True, 100)) + calibration_status = preset_param(default=list(True, 100)) + mode = preset_param(default=list(True, 100)) + selfcheck = preset_param(default=list(True, 100)) + current_position = preset_param(default=list(True, 100)) + start_position = preset_param(default=list(True, 100)) + last_task = preset_param(default=list(True, 100)) + time_delta = preset_param(default=list(True, 100)) [[[__many__]]] - __many__ = boolean + __many__ = preset_param [CHECKS] battery_min = float(default=50.0, min=0, max=100) diff --git a/Server/copter_table.py b/Server/copter_table.py index aef2ccc..2a1df89 100644 --- a/Server/copter_table.py +++ b/Server/copter_table.py @@ -16,7 +16,7 @@ def save_preset(config, current, header_dict): presets = config.table_presets for key in presets[HeaderEditWidget.default]: - if key not in presets[current] and not header_dict[key]: + if key not in presets[current] and not header_dict[key][0]: header_dict.pop(key) presets[current] = header_dict @@ -33,7 +33,8 @@ class HeaderViewFilter(QObject): if event.type() == QEvent.Enter: # logicalIndex = self.header.logicalIndexAt(event.pos()) self.parent().cellHover.emit(QModelIndex()) - + else: + return False return True @@ -147,20 +148,36 @@ class CopterTableWidget(QTableView): if index_to != index_from: self.horizontalHeader().moveSection(index_from, index_to) - def load_columns(self, item_dict: dict=None): + def load_columns(self, item_dict: dict = None): presets = self.config.table_presets if item_dict is None: item_dict = presets[self.config.table_presets_current] - item_dict.update({key: False for key in presets[HeaderEditWidget.default] if key not in item_dict}) + item_dict.update({key: (False, presets[HeaderEditWidget.default][key][1]) + for key in presets[HeaderEditWidget.default] if key not in item_dict}) - self.set_column_order(list(item_dict.keys())) - for name, show in item_dict.items(): # for index, name in enumerate(self.columns): - self.setColumnHidden(self.columns.index(name), not show) # self.setColumnHidden(index, not item_dict.get(name, False)) + self.set_column_order(item_dict.keys()) + # self.set_column_widths({key: val[1] for key, val in item_dict.items()}) + + for name, value in item_dict.items(): # for index, name in enumerate(self.columns): + index = self.columns.index(name) + show, width = value + self.setColumnHidden(index, not show) # self.setColumnHidden(index, not item_dict.get(name, False)) + self.setColumnWidth(index, width) + + def _get_column_item(self, column): + index = self.columns.index(column) + presets = self.config.table_presets + show = not self.isColumnHidden(index) + # columnWidth is 0 when hidden, trying to get previous width from config or default + width = self.columnWidth(index) or \ + presets[self.config.table_presets_current].get(column, 0)[1] or \ + presets[HeaderEditWidget.default][column][1] + return show, width @property def item_dict(self): - return {column: not self.isColumnHidden(self.columns.index(column)) for column in self.current_columns} + return {column: self._get_column_item(column) for column in self.current_columns} def save_columns(self): current = self.config.table_presets_current @@ -180,8 +197,7 @@ class CopterTableWidget(QTableView): @pyqtSlot(QtCore.QModelIndex) def on_double_click(self, index): - col = index.column() - if col == 7: + if self.model.is_column(index, "selfcheck"): data = self.proxy_model.data(index, role=table.ModelDataRole) if data and data != "OK": self._show_info("Selfcheck info", data) @@ -254,7 +270,8 @@ class CopterTableWidget(QTableView): class HeaderListWidget(QListWidget): - ColumnKeyRole = 998 + ColumnKeyRole = Qt.UserRole + 1000 + ColumnWidthRole = Qt.UserRole + 1001 dropped = QtCore.pyqtSignal(bool) @@ -268,18 +285,21 @@ class HeaderListWidget(QListWidget): def populate_items(self, item_dict: dict): self.clear() - for name, visible in item_dict.items(): + for name, value in item_dict.items(): + visible, width = value flags = Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled state = Qt.Checked if visible else Qt.Unchecked item = QListWidgetItem(table.CopterDataModel.columns_dict.get(name, "").strip() or name, self) item.setFlags(flags) item.setCheckState(state) - item.setData(HeaderListWidget.ColumnKeyRole, name) + item.setData(self.ColumnKeyRole, name) + item.setData(self.ColumnWidthRole, width) @property def item_dict(self): - return {self.item(i).data(HeaderListWidget.ColumnKeyRole): bool(self.item(i).checkState()) + return {self.item(i).data(self.ColumnKeyRole): + (bool(self.item(i).checkState()), self.item(i).data(self.ColumnWidthRole)) for i in range(self.count())} def dropEvent(self, event: QtGui.QDropEvent): @@ -307,8 +327,7 @@ class ActiveHeaderListWidget(HeaderListWidget): key = item.data(HeaderListWidget.ColumnKeyRole) if key is None: return - self.source_widget.setColumnHidden(self.columns.index(key), - not bool(item.checkState())) + self.source_widget.setColumnHidden(self.columns.index(key), not bool(item.checkState())) def dropEvent(self, event: QtGui.QDropEvent): super().dropEvent(event) @@ -332,7 +351,7 @@ class HeaderEditWidget(QtWidgets.QWidget): self.preset_widget = QtWidgets.QComboBox() self.header_widget = ActiveHeaderListWidget(self.source) \ if self.menu_mode else HeaderListWidget() - #self.header_widget.itemChanged.connect(partial(self.saved_signal.emit, False)) + # self.header_widget.itemChanged.connect(partial(self.saved_signal.emit, False)) self.header_widget.model().dataChanged.connect(partial(self.saved_signal.emit, False)) self.header_widget.dropped.connect(partial(self.saved_signal.emit, False)) @@ -404,13 +423,17 @@ class HeaderEditWidget(QtWidgets.QWidget): self.previous = index presets = self.config.table_presets - items = {key: value for key, value in presets[index].items()} - items.update({key: False for key in presets[self.default] if key not in items}) + item_dict = {key: value for key, value in presets[index].items()} + item_dict.update({key: (False, presets[self.default][key][1]) + for key in presets[self.default] if key not in item_dict}) if self.menu_mode: - self.source.set_column_order(list(items.keys())) + self.source.set_column_order(list(item_dict.keys())) # hidden\shown is hold by header widget's itemChanged + for name, value in item_dict.items(): + self.source.setColumnWidth(self.source.columns.index(name), value[1]) + self.config.table_presets_current = index - self.header_widget.populate_items(items) + self.header_widget.populate_items(item_dict) self.saved_signal.emit(True) def add_preset(self): @@ -438,7 +461,7 @@ class HeaderEditWidget(QtWidgets.QWidget): return reply = QMessageBox.question(None, "Action can't be undone", "Remove anyway?", - QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply != QMessageBox.Yes: return @@ -486,7 +509,7 @@ class HeaderEditDialog(QtWidgets.QDialog): unsaved = not saved self.unsaved = unsaved self.setWindowTitle(f"Column preset editor - {self.widget.preset_widget.currentText()}" - + "*"*unsaved) + + "*" * unsaved) def closeEvent(self, event): if not self.unsaved: @@ -506,8 +529,11 @@ class HeaderEditDialog(QtWidgets.QDialog): if __name__ == '__main__': import sys + def except_hook(cls, exception, traceback): sys.__excepthook__(cls, exception, traceback) + + sys.excepthook = except_hook # for debugging (exceptions traceback) app = QtWidgets.QApplication(sys.argv) @@ -518,12 +544,14 @@ if __name__ == '__main__': # model.add_client(copter_table_models.StatedCopterData()) import config + c = config.ConfigManager() c.load_config_and_spec("config\server.ini") - #print(c.config) - #print(c._name_dict) + # print(c.config) + # print(c._name_dict) w1 = CopterTableWidget(model, c) w = HeaderEditWidget(w1, c) + print(w1.item_dict) # print(*w1.current_columns, sep='\n') - w.show() + # w.show() app.exec() diff --git a/Server/copter_table_models.py b/Server/copter_table_models.py index c467fbb..f643e32 100644 --- a/Server/copter_table_models.py +++ b/Server/copter_table_models.py @@ -354,8 +354,6 @@ class CopterDataModel(QtCore.QAbstractTableModel): def __init__(self, checks=ModelChecks, formatter=ModelFormatter, data_model=StatedCopterData, parent=None): super(CopterDataModel, self).__init__(parent) - # self.headers = (' copter ID ', ' version ', ' animation ID ', ' battery ', ' fcu_status ', ' sensors ', - # ' mode ', ' checks ', ' current x y z yaw frame_id ', ' start x y z ', ' task ', 'dt') self.headers = list(self.columns_dict.values()) self.data_contents = [] diff --git a/config.py b/config.py index 6b1acf6..55c911e 100644 --- a/config.py +++ b/config.py @@ -3,7 +3,7 @@ import copy import collections from configobj import ConfigObj, Section, flatten_errors -from validate import Validator +from validate import Validator, is_tuple, is_boolean, is_integer def modify_filename(path, pattern): # TODO move to core @@ -23,6 +23,11 @@ def parent_dir(path): return os.path.basename(os.path.normpath(path)) +def is_preset_param(value): + parsed = is_tuple(value, min=2, max=2) + return is_boolean(parsed[0]), is_integer(parsed[1], min=0) + + class ValidationError(ValueError): def __init__(self, message, config, errors): super(ValidationError, self).__init__(message) @@ -84,7 +89,7 @@ class ConfigManager: def validate_config(self, config=None, copy_defaults=False): config = self.config if config is None else config - vdt = Validator() + vdt = Validator({"preset_param": is_preset_param}) test = config.validate(vdt, copy=copy_defaults, preserve_errors=True) if test != True: # Important syntax, do no change @@ -306,9 +311,13 @@ class ConfigManager: if __name__ == '__main__': cfg = ConfigManager() - cfg.load_from_file('Server/config/server.ini') + cfg.load_from_file('Drone/config/client.ini') + # cfg.load_from_file('Server/config/server.ini') #cfg.load_from_file('Drone/config/spec/configspec_client.ini') print(dict(cfg.full_dict(include_defaults=True))) + cfg.config.pop("PRIVATE", None) + print(cfg.config) + # cfg.load_config_and_spec('Drone/config/client.ini') # #print(cfg.config.comments) diff --git a/update_configspec.py b/update_configspec.py index 3400119..590b32d 100644 --- a/update_configspec.py +++ b/update_configspec.py @@ -1,8 +1,13 @@ import config from Server.copter_table_models import CopterDataModel -cfg_server = config.ConfigObj('SERVER/config/spec/configspec_server.ini') -default = {key: 'boolean(default=True)' for key in CopterDataModel.columns} +cfg_server = config.ConfigObj('SERVER/config/spec/configspec_server.ini', list_values=False) +widths = {"copter_id": 150} +default_width = 100 + +default = {key: f"preset_param(default=list(True, {widths.get(key, default_width)}))" + for key in CopterDataModel.columns} + cfg_server['TABLE']['PRESETS']['DEFAULT'] = default cfg_server.write()