from functools import partial from copy import deepcopy from PyQt5 import QtWidgets, QtCore, QtGui 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 from config_editor_models import ConfigDialog import copter_table_models as table class CopterTableWidget(QTableView): config_dialog_signal = QtCore.pyqtSignal(object, object) def __init__(self, model, window, data_model=table.StatedCopterData): QTableView.__init__(self) self._window = window self.model = model self._data_model = data_model self.proxy_model = table.CopterProxyModel() self.signals = table.SignalManager(self.model) self.proxy_model.setSourceModel(self.model) self.proxy_model.setDynamicSortFilter(True) # Initiate table and table self.model self.setModel(self.proxy_model) self.columns = [header.strip() for header in self.model.headers] self.current_columns = deepcopy(self.columns) header = self.horizontalHeader() header.sectionMoved.connect(self.moved) header.setSectionsMovable(True) header.setContextMenuPolicy(Qt.CustomContextMenu) header.customContextMenuRequested.connect(self.showHeaderMenu) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.open_menu) self.signal_connection = None # self.horizontalHeader().contextMenuEvent = self.headercontextMenuEvent # Adjust properties self.resizeColumnsToContents() self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.doubleClicked.connect(self.on_double_click) def moved(self, logical_index, old_index, new_index): # print(logical_index, old_index, new_index) name = self.current_columns.pop(old_index) self.current_columns.insert(new_index, name) # Some fancy wrappers to simplify syntax def add_client(self, **kwargs): self.signals.add_client_signal.emit(self._data_model(**kwargs)) def remove_client_data(self, row_data): self.signals.remove_client_signal.emit(row_data) def update_data(self, row, col, data, role=table.ModelDataRole): self.signals.update_data_signal.emit(row, col, data, role) @pyqtSlot(QtCore.QModelIndex) def on_double_click(self, index): col = index.column() if col == 7: data = self.proxy_model.data(index, role=table.ModelDataRole) if data and data != "OK": self._show_info("Selfcheck info", data) def _show_info(self, title, data): dialog = QMessageBox() dialog.setIcon(QMessageBox.NoIcon) dialog.setStandardButtons(QMessageBox.Ok) dialog.setWindowTitle(title) dialog.setText("\n".join(data[:10])) dialog.setDetailedText("\n".join(data)) dialog.exec() def showHeaderMenu(self, event): menu = QMenu(self) header_view = HeaderListWidget(menu, self) header_view.setFixedHeight((header_view.geometry().height()-2) * len(header_view.original_names)) #box.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) action = QWidgetAction(menu) action.setDefaultWidget(header_view) menu.addAction(action) menu.exec_(QCursor.pos()) @pyqtSlot(QtCore.QPoint) def open_menu(self, point): menu = QMenu(self) index = self.indexAt(point) item = self.model.get_row_data(index) # print(item, index.row(), index.column()) edit_config = QAction("Edit config") edit_config.triggered.connect(partial(self.edit_config, item)) menu.addAction(edit_config) if item is None: edit_config.setDisabled(True) menu.exec_(QCursor.pos()) @pyqtSlot() def edit_config(self, copter): if self.signal_connection is not None: self.config_dialog_signal.disconnect(self.signal_connection) call = ConfigDialog(self._window).call_copter_dialog self.signal_connection = self.config_dialog_signal.connect(call) copter.client.get_response("config", self.config_dialog_signal.emit) # def _selfcheck_shortener(self, data): # TODO!!! # shortened = [] # for line in data: # if len(line) > 89: # pass # return shortened class HeaderListWidget(QListWidget): def __init__(self, parent, source: CopterTableWidget): super().__init__(parent) self.source_widget = source self.source_model = source.proxy_model self.setDragDropMode(QAbstractItemView.InternalMove) self.setDefaultDropAction(Qt.MoveAction) self.names = source.current_columns self.original_names = source.columns # list(deepcopy(parent.names))#list(self.get_names()) self.populate_items() self.itemChanged.connect(self.on_itemChanged) # def get_names(self): # for column in range(self.source_model.columnCount()): # yield self.source_model.headerData(column, Qt.Horizontal).strip() def populate_items(self): for column, name in enumerate(self.names): hidden = self.source_widget.isColumnHidden(column) flags = Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled state = Qt.Unchecked if hidden else Qt.Checked item = QListWidgetItem(name, self) item.setFlags(flags) item.setCheckState(state) def dropEvent(self, event: QtGui.QDropEvent): super().dropEvent(event) old_names = self.names[:] print(old_names) names = [self.item(i).text() for i in range(self.count())] #print(names) self.names[:] = names # don't breaking the link print(self.source_widget.current_columns) # print(self.indexAt(event.pos()).row()) #event.accept() @pyqtSlot(QListWidgetItem) def on_itemChanged(self, item): self.source_widget.setColumnHidden(self.original_names.index(item.text()), not bool(item.checkState()))