mirror of
https://github.com/CopterExpress/clever-show.git
synced 2026-05-26 15:13:26 +00:00
WIP1 column state saving
This commit is contained in:
@@ -9,6 +9,23 @@ buffer_size = integer(default=1024)
|
||||
# True -> clients are removed on disconnection
|
||||
# False -> disconnected clients indicated
|
||||
remove_disconnected = boolean(default=False)
|
||||
[[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)
|
||||
[[[__many__]]]
|
||||
__many__ = boolean
|
||||
|
||||
[CHECKS]
|
||||
battery_min = float(default=50.0, min=0, max=100)
|
||||
|
||||
@@ -7,16 +7,17 @@ from PyQt5.QtCore import Qt as Qt
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import QTableView, QMessageBox, QMenu, QAction, QWidgetAction, QListWidget, \
|
||||
QAbstractItemView, QListWidgetItem
|
||||
QAbstractItemView, QListWidgetItem, QVBoxLayout, QHBoxLayout, QPushButton, QInputDialog, QLineEdit
|
||||
|
||||
from config_editor_models import ConfigDialog
|
||||
import copter_table_models as table
|
||||
|
||||
|
||||
class CopterTableWidget(QTableView):
|
||||
def __init__(self, model, data_model=table.StatedCopterData):
|
||||
def __init__(self, model, config, data_model=table.StatedCopterData):
|
||||
QTableView.__init__(self)
|
||||
|
||||
self.config = config
|
||||
self.model = model
|
||||
self._data_model = data_model
|
||||
|
||||
@@ -29,7 +30,7 @@ class CopterTableWidget(QTableView):
|
||||
# Initiate table and table self.model
|
||||
self.setModel(self.proxy_model)
|
||||
|
||||
self.columns = [header.strip() for header in self.model.headers] # header keys
|
||||
self.columns = list(table.columns_names.keys()) #[header.strip() for header in self.model.headers] # header keys
|
||||
self.current_columns = self.columns[:]
|
||||
|
||||
header = self.horizontalHeader()
|
||||
@@ -44,6 +45,9 @@ class CopterTableWidget(QTableView):
|
||||
self.customContextMenuRequested.connect(self.open_menu)
|
||||
|
||||
# Adjust properties
|
||||
self.setTextElideMode(QtCore.Qt.ElideMiddle)
|
||||
self.setWordWrap(True)
|
||||
|
||||
self.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
self.resizeColumnsToContents()
|
||||
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
@@ -62,6 +66,14 @@ class CopterTableWidget(QTableView):
|
||||
if index_to != index_from:
|
||||
self.horizontalHeader().moveSection(index_from, index_to)
|
||||
|
||||
def load_columns(self, item_dict: dict=None):
|
||||
if item_dict is None:
|
||||
item_dict = self.config.table_presets[self.config.table_presets_current]
|
||||
|
||||
self.set_column_order(list(item_dict.keys()))
|
||||
for index, show in enumerate(item_dict.values()):
|
||||
self.horizontalHeader().setColumnHidden(index, not show)
|
||||
|
||||
# Some fancy wrappers to simplify syntax
|
||||
def add_client(self, **kwargs):
|
||||
self.signals.add_client_signal.emit(self._data_model(**kwargs))
|
||||
@@ -91,13 +103,14 @@ class CopterTableWidget(QTableView):
|
||||
|
||||
def showHeaderMenu(self, event):
|
||||
menu = QMenu(self)
|
||||
header_view = HeaderListWidget(menu, self)
|
||||
header_view.setFixedHeight((header_view.geometry().height()-2) * len(header_view.columns))
|
||||
#box.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
header_view = HeaderEditWidget(self, self.config, menu_mode=True, parent=menu)
|
||||
#header_view.setFixedHeight((header_view.geometry().height()-2) * len(header_view.columns))
|
||||
# box.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
action = QWidgetAction(menu)
|
||||
action.setDefaultWidget(header_view)
|
||||
menu.addAction(action)
|
||||
menu.exec_(QCursor.pos())
|
||||
# todo header_view.
|
||||
|
||||
@pyqtSlot(QtCore.QPoint)
|
||||
def open_menu(self, point):
|
||||
@@ -107,7 +120,7 @@ class CopterTableWidget(QTableView):
|
||||
# print(item, index.row(), index.column())
|
||||
|
||||
edit_config = QAction("Edit config")
|
||||
edit_config.triggered.connect(partial(self.edit_config, item))
|
||||
edit_config.triggered.connect(partial(self.edit_copter_config, item))
|
||||
menu.addAction(edit_config)
|
||||
|
||||
if item is None:
|
||||
@@ -116,7 +129,7 @@ class CopterTableWidget(QTableView):
|
||||
menu.exec_(QCursor.pos())
|
||||
|
||||
@pyqtSlot()
|
||||
def edit_config(self, copter):
|
||||
def edit_copter_config(self, copter):
|
||||
dialog = ConfigDialog()
|
||||
copter.client.get_response("config", dialog.call_copter_dialog)
|
||||
|
||||
@@ -129,34 +142,241 @@ class CopterTableWidget(QTableView):
|
||||
|
||||
|
||||
class HeaderListWidget(QListWidget):
|
||||
def __init__(self, parent, source: CopterTableWidget):
|
||||
super().__init__(parent)
|
||||
self.source_widget = source
|
||||
self.source_model = source.proxy_model
|
||||
ColumnKeyRole = 998
|
||||
|
||||
def __init__(self, config, parent=None, default_items=None):
|
||||
super().__init__(parent)
|
||||
self.populated_items = {}
|
||||
if default_items is not None:
|
||||
self.populate_items(default_items)
|
||||
|
||||
self.config = config
|
||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
self.setDefaultDropAction(Qt.MoveAction)
|
||||
|
||||
self.current_columns = source.current_columns
|
||||
self.columns = source.columns
|
||||
self.populate_items()
|
||||
self.itemChanged.connect(self.on_itemChanged)
|
||||
|
||||
def populate_items(self):
|
||||
for column, name in enumerate(self.current_columns):
|
||||
hidden = self.source_widget.isColumnHidden(column)
|
||||
def populate_items(self, item_dict: dict):
|
||||
self.populated_items = item_dict
|
||||
self.clear()
|
||||
for name, visible in item_dict.items():
|
||||
flags = Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled
|
||||
state = Qt.Unchecked if hidden else Qt.Checked
|
||||
state = Qt.Checked if visible else Qt.Unchecked
|
||||
|
||||
item = QListWidgetItem(name, self)
|
||||
item = QListWidgetItem(table.columns_names.get(name, "").strip() or name, self)
|
||||
item.setFlags(flags)
|
||||
item.setCheckState(state)
|
||||
item.setData(HeaderListWidget.ColumnKeyRole, name)
|
||||
|
||||
def dropEvent(self, event: QtGui.QDropEvent):
|
||||
super().dropEvent(event)
|
||||
column_order = [self.item(i).text() for i in range(self.count())]
|
||||
self.source_widget.set_column_order(column_order)
|
||||
@property
|
||||
def item_dict(self):
|
||||
return {self.item(i).data(HeaderListWidget.ColumnKeyRole): bool(self.item(i).checkState())
|
||||
for i in range(self.count())}
|
||||
|
||||
def save_to_config(self, current=None, write=False):
|
||||
if self.count() != len(self.populated_items): # Do not save when populating
|
||||
print("nosave")
|
||||
return
|
||||
|
||||
if current is None:
|
||||
current = self.config.table_presets_current
|
||||
print("sAVe", current)
|
||||
presets = self.config.table_presets
|
||||
header_dict = self.item_dict
|
||||
|
||||
for key in presets[HeaderEditWidget.default]:
|
||||
print(key, current, header_dict, presets)
|
||||
if key not in presets[current] and not header_dict[key]:
|
||||
header_dict.pop(key)
|
||||
|
||||
presets[current] = header_dict
|
||||
if write:
|
||||
self.config.write()
|
||||
|
||||
|
||||
class ActiveHeaderListWidget(HeaderListWidget):
|
||||
def __init__(self, source: CopterTableWidget, config, parent=None):
|
||||
super().__init__(config, parent=parent)
|
||||
self.source_widget = source
|
||||
|
||||
self.config = config
|
||||
self.current_columns = source.current_columns
|
||||
self.columns = source.columns
|
||||
|
||||
self._populate_from_widget()
|
||||
|
||||
self.itemChanged.connect(self.on_itemChanged)
|
||||
|
||||
def _populate_from_widget(self):
|
||||
item_dict = {}
|
||||
for column, name in enumerate(self.current_columns):
|
||||
visible = not self.source_widget.isColumnHidden(column)
|
||||
item_dict[name] = visible
|
||||
|
||||
self.populate_items(item_dict)
|
||||
|
||||
@pyqtSlot(QListWidgetItem)
|
||||
def on_itemChanged(self, item):
|
||||
self.source_widget.setColumnHidden(self.columns.index(item.text()), not bool(item.checkState()))
|
||||
key = item.data(HeaderListWidget.ColumnKeyRole)
|
||||
if key is None:
|
||||
return
|
||||
self.source_widget.setColumnHidden(self.columns.index(key),
|
||||
not bool(item.checkState()))
|
||||
self.save_to_config(write=True)
|
||||
|
||||
def dropEvent(self, event: QtGui.QDropEvent):
|
||||
super().dropEvent(event)
|
||||
column_order = [self.item(i).data(HeaderListWidget.ColumnKeyRole) for i in range(self.count())]
|
||||
self.source_widget.set_column_order(column_order)
|
||||
self.save_to_config(write=True)
|
||||
|
||||
|
||||
class HeaderEditWidget(QtWidgets.QWidget):
|
||||
add_new_text = "< add new >"
|
||||
default = "DEFAULT"
|
||||
|
||||
def __init__(self, source, config_data, menu_mode=False, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# self.auto_apply = auto_apply
|
||||
self.source = source # source = copter table
|
||||
self.config = config_data
|
||||
self.menu_mode = menu_mode
|
||||
|
||||
self.preset_widget = QtWidgets.QComboBox()
|
||||
self.header_widget = ActiveHeaderListWidget(self.source, self.config) \
|
||||
if self.menu_mode else HeaderListWidget(self.config)
|
||||
|
||||
self.previous = self.config.table_presets_current
|
||||
|
||||
self.setupUi()
|
||||
|
||||
def setupUi(self):
|
||||
self.header_widget.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
self.update_preset_list()
|
||||
self.preset_widget.currentTextChanged.connect(self.on_preset_changed)
|
||||
self.on_preset_changed(self.previous) # to init
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(self.header_widget)
|
||||
vbox.addWidget(self.preset_widget)
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
if not self.menu_mode:
|
||||
add_button = QPushButton("Add")
|
||||
add_button.clicked.connect(self.add_preset)
|
||||
remove_button = QPushButton("Remove")
|
||||
remove_button.setToolTip("Permanently remove preset from config")
|
||||
remove_button.clicked.connect(self.remove_preset)
|
||||
save_button = QPushButton("Save")
|
||||
save_button.clicked.connect(self.save_preset)
|
||||
apply_button = QPushButton("Apply")
|
||||
apply_button.clicked.connect(self.apply_preset)
|
||||
|
||||
hbox.addWidget(add_button)
|
||||
hbox.addWidget(remove_button)
|
||||
hbox.addStretch()
|
||||
hbox.addWidget(save_button)
|
||||
hbox.addWidget(apply_button)
|
||||
else:
|
||||
dialog_button = QPushButton("Manage presets")
|
||||
hbox.addWidget(dialog_button)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
self.setLayout(vbox)
|
||||
|
||||
def update_preset_list(self):
|
||||
self.preset_widget.clear()
|
||||
for name, preset in self.config.table_presets.items():
|
||||
if isinstance(preset, dict): # looking only for preset sections
|
||||
self.preset_widget.addItem(name)
|
||||
|
||||
self.preset_widget.addItem(self.add_new_text)
|
||||
self.preset_widget.setCurrentText(self.previous)
|
||||
|
||||
def on_preset_changed(self, index):
|
||||
if not index:
|
||||
return
|
||||
|
||||
if index == self.add_new_text:
|
||||
self.add_preset()
|
||||
return
|
||||
|
||||
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})
|
||||
|
||||
if self.menu_mode:
|
||||
self.source.set_column_order(list(items.keys()))
|
||||
self.config.table_presets_current = index
|
||||
print(index)
|
||||
self.config.write()
|
||||
self.header_widget.populate_items(items)
|
||||
|
||||
def add_preset(self):
|
||||
name, ok = QInputDialog.getText(None, "Enter new preset name", "Name:",
|
||||
QLineEdit.Normal, "")
|
||||
if not ok or not name:
|
||||
self.preset_widget.setCurrentText(self.previous)
|
||||
return
|
||||
|
||||
if name in self.config.table_presets or name == self.default or name == self.add_new_text:
|
||||
QMessageBox.warning(None, "Preset already exists!", "Preset already exists!")
|
||||
self.preset_widget.setCurrentText(self.previous)
|
||||
return
|
||||
|
||||
self.config.table_presets[name] = deepcopy(dict(self.config.table_presets[self.default]))
|
||||
self.config.write()
|
||||
|
||||
self.update_preset_list()
|
||||
self.preset_widget.setCurrentText(name)
|
||||
|
||||
def remove_preset(self):
|
||||
if self.preset_widget.currentText() == self.default:
|
||||
QMessageBox.warning(None, "Can't delete default preset!", "Can't delete default preset!")
|
||||
return
|
||||
|
||||
reply = QMessageBox.question(None, "Action can't be undone", "Remove anyway?",
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
|
||||
if reply != QMessageBox.Yes:
|
||||
return
|
||||
|
||||
self.config.table_presets.pop(self.preset_widget.currentText())
|
||||
self.config.write()
|
||||
|
||||
self.previous = self.default
|
||||
self.update_preset_list()
|
||||
|
||||
def save_preset(self):
|
||||
self.header_widget.save_to_config(current=self.preset_widget.currentText(), write=True)
|
||||
|
||||
def apply_preset(self):
|
||||
self.config.table_presets_current = self.preset_widget.currentText()
|
||||
self.save_preset()
|
||||
self.source.load_columns()
|
||||
|
||||
|
||||
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)
|
||||
import copter_table_models
|
||||
|
||||
model = copter_table_models.CopterDataModel()
|
||||
# for i in range(10):
|
||||
# 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)
|
||||
w1 = CopterTableWidget(model, c)
|
||||
w = HeaderEditWidget(w1, c)
|
||||
# print(*w1.current_columns, sep='\n')
|
||||
w.show()
|
||||
app.exec()
|
||||
|
||||
@@ -106,11 +106,11 @@ def check_time_delta(item):
|
||||
|
||||
|
||||
columns_names = {'copter_id': 'copter ID',
|
||||
'git_ver': 'version',
|
||||
'git_version': 'version',
|
||||
'animation_id': ' animation ID ',
|
||||
'battery': ' battery ',
|
||||
'fcu_status': 'FCU status',
|
||||
'cal_status': 'sensors',
|
||||
'calibration_status': 'sensors',
|
||||
'mode': ' mode ',
|
||||
'selfcheck': ' checks ',
|
||||
'current_position': 'current x y z yaw frame_id',
|
||||
@@ -311,9 +311,9 @@ class CopterDataModel(QtCore.QAbstractTableModel):
|
||||
|
||||
def __init__(self, checks=ModelChecks, formatter=ModelFormatter, parent=None):
|
||||
super(CopterDataModel, self).__init__(parent)
|
||||
# self.headers = list(columns_names.values()) # todo
|
||||
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 = (' 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(columns_names.values())
|
||||
self.data_contents = []
|
||||
|
||||
self.checks = checks
|
||||
|
||||
@@ -154,7 +154,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.horizontalLayout.removeWidget(self.ui.tableView)
|
||||
self.ui.tableView.close()
|
||||
# Init our custom widget
|
||||
self.ui.copter_table = CopterTableWidget(self.model)
|
||||
self.ui.copter_table = CopterTableWidget(self.model, self.server.config)
|
||||
self.ui.copter_table.setObjectName("copter_table")
|
||||
# Insert to layout at right
|
||||
self.ui.horizontalLayout.insertWidget(0, self.ui.copter_table, 0)
|
||||
|
||||
Reference in New Issue
Block a user