def refreshPropertiesDock(self): # TODO separate the toolbar with one bottom widget holding some informations and a upper part holding a layout specific to each node self.print('Dock properties') # get DataSubWindow if hasattr(self.mdiArea.activeSubWindow(), 'widget'): actSubWnd = self.mdiArea.activeSubWindow().widget() try: # reset layout itemsSelected = actSubWnd.scene.getSelectedItems() if len(itemsSelected) > 1: self.propDock.setWidget(self.propDockLbl) self.propDockLbl.setText('{} items selected'.format(len(itemsSelected))) elif len(itemsSelected) == 1: itemSelected = itemsSelected[0] if hasattr(itemSelected, 'node') and itemSelected.node.hasPropertiesWidget(): self.propDock.setWidget(itemSelected.node.propertiesWidget) else: self.propDockLbl.setText('{} items selected'.format(len(itemsSelected))) self.propDock.setWidget(self.propDockLbl) else: self.propDock.setWidget(self.propDockLbl) self.propDockLbl.setText('No selection') except Exception as e: dumpException(e)
def onDrop(self, event): if event.mimeData().hasFormat(LISTBOX_MIMETYPE): eventData = event.mimeData().data(LISTBOX_MIMETYPE) dataStream = QDataStream(eventData, QIODevice.ReadOnly) pixmap = QPixmap() dataStream >> pixmap op_code = dataStream.readInt() text = dataStream.readQString() mouse_position = event.pos() scene_position = self.scene.grScene.views()[0].mapToScene( mouse_position) if DEBUG: print("GOT DROP: [%d] '%s'" % (op_code, text), "mouse:", mouse_position, "scene:", scene_position) try: node = get_class_from_opcode(op_code)(self.scene) node.setPos(scene_position.x(), scene_position.y()) self.scene.history.storeHistory("Created node %s" % node.__class__.__name__) except Exception as e: dumpException(e) event.setDropAction(Qt.MoveAction) event.accept() else: # print(" ... drop ignored, not requested format '%s'" % LISTBOX_MIMETYPE) event.ignore()
def __init__(self, parent: 'DataTableView', dataframe: Union[pd.DataFrame, pd.Series] = None): """Model handling values taken either from a DataFrame or a Series Parameters ---------- parent Instance attributes ------------------- _source_dataframe : pd.DataFrame Data source _dataframe : pd.DataFrame Data sorted or filtered filters : List[Filter] list of `Filter` to apply """ try: super().__init__(parent=parent) # init attributes self._source_dataframe: Union[pd.DataFrame, pd.Series, None] = None self._dataframe: Union[pd.DataFrame, pd.Series, None] = None self.dataframe = dataframe except Exception as e: dumpException(e)
def startDrag(self, *args, **kwargs): try: item = self.currentItem() op_code = item.data(Qt.UserRole + 1) pixmap = QPixmap(item.data(Qt.UserRole)) itemData = QByteArray() dataStream = QDataStream(itemData, QIODevice.WriteOnly) dataStream << pixmap dataStream.writeInt(op_code) dataStream.writeQString(item.text()) mimeData = QMimeData() mimeData.setData(LISTBOX_MIMETYPE, itemData) drag = QDrag(self) drag.setMimeData(mimeData) drag.setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2)) drag.setPixmap(pixmap) drag.exec_(Qt.MoveAction) except Exception as e: dumpException(e)
def setEditorData(self, editor, index): try: value = index.data(Qt.DisplayRole) num = self.options.index(value) editor.setCurrentIndex(num) except Exception as e: dumpException(e)
def startDrag(self, supportedActions: Union[Qt.DropActions, Qt.DropAction]) -> None: try: item: SourceListWidgetItem = self.currentItem() drag = getDragObject(self, item) drag.exec_(Qt.MoveAction) except Exception as e: dumpException(e)
def onFileOpen(self): """Open OpenFileDialog""" # OpenFile dialog fnames, filter = QFileDialog.getOpenFileNames( self, 'Open graph from file', self.getFileDialogDirectory(), self.getFileDialogFilter()) try: for fname in fnames: if fname: existing = self.findMdiChild(fname) if existing: self.mdiArea.setActiveSubWindow(existing) else: # do not use createMdiChild as a new node editor to call the fileLoad method # Create new subwindow and open file nodeeditor = CalculatorSubWindow() if nodeeditor.fileLoad(fname): self.statusBar().showMessage( f'File {fname} loaded', 5000) nodeeditor.setTitle() subwnd = self.createMdiChild(nodeeditor) subwnd.show() else: nodeeditor.close() except Exception as e: dumpException(e)
def onFileNew(self): try: subwnd = self.createMdiChild() subwnd.widget().fileNew() subwnd.show() except Exception as e: dumpException(e)
def onDrop(self, event: QDropEvent): if DEBUG: print('CalcSubWnd : onDrop') print(event.mimeData().text()) if event.mimeData().hasFormat(LISTBOX_MIMETYPE): eventData = event.mimeData().data(LISTBOX_MIMETYPE) dataStream = QDataStream(eventData, QIODevice.ReadOnly) pixmap = QPixmap() dataStream >> pixmap op_code = dataStream.readQString() text = dataStream.readQString() mouse_pos = event.pos() scene_pos = self.scene.grScene.views()[0].mapToScene(mouse_pos) if DEBUG: print(f'DROP: {op_code} {text} at {scene_pos}') try: node = NodeFactory.from_op_code(op_code)(self.scene) node.setPos(scene_pos.x(), scene_pos.y()) self.scene.history.storeHistory('Create node {}'.format( node.__class__.__name__)) event.setDropAction(Qt.MoveAction) event.accept() except Exception as e: dumpException(e) else: if DEBUG: print(' ... drop ignored, not requested format ', LISTBOX_MIMETYPE) event.ignore()
def filter(self): """Filter the underlying dataframe Parameters ---------- header : QHeaderView reference to header holding the text for filtering """ try: header = self.view.horizontalHeader() mask = None self.layoutAboutToBeChanged.emit() # Filter according to filter text in header # TODO handle in function of datatype for i in range(self.columnCount()): text = header.getFilterTextAtIndex(i) column = self._source_data.columns[i] if text != '': if mask is not None: mask &= self._source_data[column].str.contains(text, na=False) else: mask = self._source_data[column].str.contains(text, na=False) if mask is None: self._data = self._source_data else: self._data = self._source_data[mask] self.layoutChanged.emit() except Exception as e: dumpException(e)
def startDrag(self, *args, **kwargs): try: # Retrieve the operational code item = self.currentItem() op_code = item.data(Qt.UserRole + 1) # name = item.text() # Define Mime data # Retrieve pixmap pixmap = QPixmap(item.data(Qt.UserRole)) # Populate a QByteArray with datastream itemData = QByteArray() dataStream = QDataStream(itemData, QIODevice.WriteOnly) dataStream << pixmap dataStream.writeQString(op_code) dataStream.writeQString(item.text()) mimeData = QMimeData() mimeData.setData(LISTBOX_MIMETYPE, itemData) drag = QDrag(self) drag.setMimeData(mimeData) drag.setPixmap(pixmap) # set pixmap to drag drag.setHotSpot(QPoint(pixmap.width() // 2, pixmap.height() // 2)) # Hotspot in the middle of the drag drag.exec_(Qt.MoveAction) except Exception as e: dumpException(e)
def data(self, index, role=Qt.DisplayRole): try: if index.isValid(): if role == Qt.DisplayRole: return self._data.iloc[index.row(), index.column()] # elif role == Qt.TextAlignmentRole: # return _align(value) except Exception as e: dumpException(e)
def loadFromFile(self, filename): with open(filename, "r") as file: raw_data = file.read() try: data = json.loads(raw_data, encoding='utf-8') self.deserialize(data) except json.JSONDecodeError: raise InvalidFile("%s is not a valid JSON file" % os.path.basename(filename)) except Exception as e: dumpException(e)
def deserialize(self, data: dict, hashmap: dict = {}, restore_id: bool = True): res = super().deserialize(data, hashmap) try: value = data['value'] self.edit.setText(value) return True & res except Exception as e: dumpException(e) return res
def set_spans(self): """Adjust spans of the table to display multiheader like""" self.clearSpans() try: # Find spans for horizontal HeaderView if self.isHorizontal(): self._adjust_spans(self.dataframe.columns) else: self._adjust_spans(self.dataframe.index) except Exception as e: dumpException(e)
def changeHorizontalHeader(self, index): try: oldHeader = self.model().headerData(index, Qt.Horizontal, Qt.DisplayRole) value, valid = QInputDialog.getText(self, 'Change header label for column %d' % index, 'Header:', QLineEdit.Normal, str(oldHeader)) if valid: self.model().setHeaderValue(index, Qt.Horizontal, value) except Exception as e: dumpException(e)
def closeEvent(self, event: QCloseEvent) -> None: try: self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() # In case of fixing the application closing # import sys # sys.exit(0) except Exception as e: dumpException(e)
def debug(self): print('dropped') try: settings = dict() root = self.outputTree.invisibleRootItem() for n in range(root.childCount()): child = root.child(n) settings[child.text(0)] = list() if child.childCount() > 0: for i in range(child.childCount()): print(child.child(i).value) except Exception as e: dumpException(e)
def onDoubleClicked(self, event): """Event handling double click on Graphics Node in `Scene`""" try: flags = Qt.WindowMaximizeButtonHint flags |= Qt.WindowCloseButtonHint # flags |= Qt.WindowMinimizeButtonHint wnd = QDialog(flags=flags) layout = QVBoxLayout() layout.addWidget(self.content.view.copy()) wnd.setLayout(layout) wnd.exec_() except Exception as e: dumpException(e)
def updateEditMenu(self): try: # print("update Edit Menu") active = self.getCurrentNodeEditorWidget() hasMdiChild = (active is not None) self.actPaste.setEnabled(hasMdiChild) self.actCut.setEnabled(hasMdiChild and active.hasSelectedItems()) self.actCopy.setEnabled(hasMdiChild and active.hasSelectedItems()) self.actDelete.setEnabled(hasMdiChild and active.hasSelectedItems()) self.actUndo.setEnabled(hasMdiChild and active.canUndo()) self.actRedo.setEnabled(hasMdiChild and active.canRedo()) except Exception as e: dumpException(e)
def sort(self, column: int, order: Qt.SortOrder = ...) -> None: """Sort the undelying dataframe Parameters ---------- column : int Column along which to sort order : Qt.SortOrder Ascending or descending """ try: self.layoutAboutToBeChanged.emit() self._data = self._data.sort_values(self._data.columns[column], ascending=not order) self.layoutChanged.emit() except Exception as e: dumpException(e)
def eval(self): if not self.isDirty() and not self.isInvalid(): print( f" _> return cached {self.__class__.__name__} value {self.value}" ) return self.value try: val = self.evalImplementation() return val except ValueError as e: self.markInvalid() self.grNode.setToolTip(str(e)) self.markDescendantDirty() except Exception as e: self.markInvalid() self.grNode.setToolTip(str(e)) dumpException(e)
def setDataFrame(self, dataframe: pd.DataFrame): """Define the dataframe through a new `DataTableModel` Parameters ---------- dataframe : pd.DataFrame dataframe holding the data editable : bool flag setting the `DataTableModel` as editable """ try: # dataframe_model_bak = DataTableModel(view=self, dataframe=dataframe, editable=editable) # self.header.setFilterBoxes(dataframe_model_bak.columnCount()) self.model().dataframe = dataframe self.header.setFilterBoxes(dataframe.shape[1]) # Connect filter from model to event in FilterHeader # super().setModel(dataframe_model_bak) except Exception as e: dumpException(e)
def updateEditMenu(self): """Gray the properties of the editMenu in function of the active subwindow """ if DEBUG: self.print('updateEditMenu') try: active = self.getCurrentNodeEditorWidget() hasMdiChild = (active is not None) hasSelectedItems = hasMdiChild and active.hasSelectedItems() self.actPaste.setEnabled(hasMdiChild) self.actCut.setEnabled(hasSelectedItems) self.actCopy.setEnabled(hasSelectedItems) self.actDelete.setEnabled(hasSelectedItems) self.actUndo.setEnabled(hasMdiChild and active.canUndo()) self.actRedo.setEnabled(hasMdiChild and active.canRedo()) except Exception as e: dumpException(e)
def deserialize(self, data, hashmap={}, restore_id=True): try: if restore_id: self.id = data['id'] hashmap[data['id']] = self self.setPos(data['pos_x'], data['pos_y']) self.title = data['title'] data['inputs'].sort(key=lambda socket: socket['index'] + socket[ 'position'] * 10000) data['outputs'].sort(key=lambda socket: socket['index'] + socket[ 'position'] * 10000) num_inputs = len(data['inputs']) num_outputs = len(data['outputs']) self.inputs = [] for socket_data in data['inputs']: new_socket = Socket(node=self, index=socket_data['index'], position=socket_data['position'], socket_type=socket_data['socket_type'], count_on_this_node_side=num_inputs, is_input=True) new_socket.deserialize(socket_data, hashmap, restore_id) self.inputs.append(new_socket) self.outputs = [] for socket_data in data['outputs']: new_socket = Socket(node=self, index=socket_data['index'], position=socket_data['position'], socket_type=socket_data['socket_type'], count_on_this_node_side=num_outputs, is_input=False) new_socket.deserialize(socket_data, hashmap, restore_id) self.outputs.append(new_socket) except Exception as e: dumpException(e) # also deseralize the content of the node # res = self.content.deserialize(data['content'], hashmap) return True
def dropEvent(self, event: QDropEvent) -> None: try: item = self.itemAt(event.pos()) source = event.source() mime = event.mimeData() if mime.hasFormat(LISTBOX_W_VALUE_MIMETYPE): # Retrieve value from mime data value = getValueFromMime(mime) new_item = DraggableListItem(value) # find existing items with the same name and remove them # source is either self or the instance of DraggableListWidget source.removeItems(new_item.text()) # Add new_item currentRow = self.row(item) self.insertItem(currentRow, new_item) self.itemDropped.emit() event.acceptProposedAction() except Exception as e: dumpException(e)
def eval(self, force: bool = False) -> Any: """Evaluate this `Node`. Return cached value in case no change was detected. Evaluation can be forced by setting Force to ``True``. Otherwise, evaluate this `Node` by calling :py:meth:`~data_node_base.DataNode.evalImplementation`. In case of ValueError, set the node and their children as dirty. Any other error will set the node and their children as invalid Parameters ---------- force: bool ``True`` Force evaluation of this `Node`. Returns ------- Any """ if not self.isDirty() and not self.isInvalid(): self.print('Dirty : ', self.isDirty(), 'Invalid : ', self.isInvalid()) self.print( f" _> return cached {self.__class__.__name__} value {self.value}" ) return self.value try: # By default, undirty and valid the node # self.markDirty(False) # self.markInvalid(False) val = self.evalImplementation(force=force) return val except ValueError as e: self.markDirty() self.setToolTip(str(e)) self.markDescendantDirty() except Exception as e: self.markInvalid() self.setToolTip(str(e)) dumpException(e)
def contextMenuEvent(self, event: QContextMenuEvent) -> None: try: item = self.scene.getItemAt(event.pos()) if DEBUG_CONTEXT: print(item) if type(item) == QGraphicsProxyWidget: item = item.widget() if hasattr(item, 'node') or hasattr(item, 'socket'): self.handleNodeContextMenu(event) elif hasattr(item, 'edge'): self.handleEdgeContextMenu(event) else: self.handleNewNodeContextMenu(event) return super().contextMenuEvent(event) except Exception as e: dumpException(e)
def restoreHistoryStamp(self, history_stamp): if DEBUG: print("RHS: ", history_stamp['desc']) try: self.scene.deserialize(history_stamp['snapshot']) # restore selection for edge_id in history_stamp['selection']['edges']: for edge in self.scene.edges: if edge.id == edge_id: edge.grEdge.setSelected(True) break for node_id in history_stamp['selection']['nodes']: for node in self.scene.nodes: if node.id == node_id: node.grNode.setSelected(True) break except Exception as e: dumpException(e)
def restoreStatus(self, data: dict) -> bool: """Restore the status of the widget. Restore the status of the widget using a dictionary with keys 'input', 'output' as returned by getStatus method. Parameters ---------- data: dict dictionary as returned by the method getStatus Returns ------- ``bool`` True if successful """ try: # restore inputs self.inputList.clear() if data['inputs'] is not None: for value in data['inputs']: item = SourceListWidgetItem(value) self.inputList.addItem(item) # restore outputs root = self.outputTree.invisibleRootItem() for n in range(root.childCount()): child = root.child(n) # remove current child items in case of any if child.childCount() > 0: for i in range(child.childCount()): child.removeChild(child.child(i)) # add child items to the corresponding leaf key = child.text(0) for value in data['outputs'][key]: item = DestTreeWidgetItem(value) child.addChild(item) return True except Exception as e: dumpException(e) return False