def apply(self): if not self.__dialog: self.createDialog() self.__datasource.applied = False sourceType = unicode(self.__dialog.ui.typeComboBox.currentText()) self.__datasource.dataSourceName = unicode( self.__dialog.ui.nameLineEdit.text()) if sourceType in self.__dialog.imp.keys(): self.__dialog.imp[sourceType].fromForm(self.__datasource) self.__datasource.dataSourceType = sourceType self.__datasource.doc = unicode( self.__dialog.ui.docTextEdit.toPlainText()).strip() index = QModelIndex() if hasattr(self.__dialog, "view") and self.__dialog.view \ and self.__dialog.view.model(): if hasattr(self.__dialog.view, "currentIndex"): index = self.__dialog.view.currentIndex() finalIndex = self.__dialog.view.model().createIndex( index.row(), 2, index.parent().internalPointer()) self.__dialog.view.expand(index) row = index.row() column = index.column() parent = index.parent() if self.__dialog.root: self.updateNode(index) if index.isValid(): index = self.__dialog.view.model().index(row, column, parent) self.__dialog.view.setCurrentIndex(index) self.__dialog.view.expand(index) if hasattr(self.__dialog, "view") and self.__dialog.view \ and self.__dialog.view.model(): self.__dialog.view.model().emit( SIGNAL("dataChanged(const QModelIndex &," " const QModelIndex &)"), index.parent(), index.parent()) if index.column() != 0: index = self.__dialog.view.model().index( index.row(), 0, index.parent()) self.__dialog.view.model().emit( SIGNAL("dataChanged(const QModelIndex &," " const QModelIndex &)"), index, finalIndex) self.__dialog.view.expand(index) if not self.__datasource.tree: self.createNodes() self.__datasource.applied = True return True
def sectionSizeFromContents(self, logicalIndex: int) -> QSize: if self._pd.headerModel: curLeafIndex = QModelIndex(self._pd.leafIndex(logicalIndex)) if curLeafIndex.isValid(): styleOption = QStyleOptionHeader( self.styleOptionForCell(logicalIndex)) s = QSize(self._pd.cellSize(curLeafIndex, self, styleOption)) curLeafIndex = curLeafIndex.parent() while curLeafIndex.isValid(): if self.orientation() == Qt.Horizontal: s.setHeight(s.height() + self._pd.cellSize( curLeafIndex, self, styleOption).height()) else: s.setWidth(s.width() + self._pd.cellSize( curLeafIndex, self, styleOption).width()) curLeafIndex = curLeafIndex.parent() return s return super().sectionSizeFromContents(logicalIndex)
def sectionSizeFromContents(self, logicalIndex: int)->QSize: if self._pd.headerModel: curLeafIndex = QModelIndex(self._pd.leafIndex(logicalIndex)) if curLeafIndex.isValid(): styleOption = QStyleOptionHeader(self.styleOptionForCell(logicalIndex)) s = QSize(self._pd.cellSize(curLeafIndex, self, styleOption)) curLeafIndex=curLeafIndex.parent() while curLeafIndex.isValid(): if self.orientation() == Qt.Horizontal: s.setHeight(s.height()+self._pd.cellSize(curLeafIndex, self, styleOption).height()) else: s.setWidth(s.width()+self._pd.cellSize(curLeafIndex, self, styleOption).width()) curLeafIndex=curLeafIndex.parent() return s return super().sectionSizeFromContents(logicalIndex) def paintSection(self, painter: QPainter, rect: QRect, logicalIndex: int): if rect.isValid(): leafIndex = QModelIndex(self._pd.leafIndex(logicalIndex)) if leafIndex.isValid(): if self.orientation() == Qt.Horizontal: self._pd.paintHorizontalSection(painter, rect, logicalIndex, self, self.styleOptionForCell(logicalIndex), leafIndex) else: self._pd.paintVerticalSection(painter, rect, logicalIndex, self, self.styleOptionForCell(logicalIndex), leafIndex) return super().paintSection(painter, rect, logicalIndex) def on_sectionResized(self, logicalIndex: int): if self.isSectionHidden(logicalIndex): return leafIndex = QModelIndex(self._pd.leafIndex(logicalIndex)) if leafIndex.isValid(): leafsList = QModelIndexList(self._pd.leafs(self._pd.findRootIndex(leafIndex))) for n in range(leafsList.indexOf(leafIndex), 0, -1): logicalIndex-=1 w = self.viewport().width() h = self.viewport().height() pos = self.sectionViewportPosition(logicalIndex) r = QRect(pos, 0, w - pos, h) if self.orientation() == Qt.Horizontal: if self.isRightToLeft(): r.setRect(0, 0, pos + self.sectionSize(logicalIndex), h) else: r.setRect(0, pos, w, h - pos) self.viewport().update(r.normalized()) def setModel(self, model): super().setModel(model) model.layoutChanged.connect(self.layoutChanged) self.layoutChanged() def layoutChanged(self): if self.model(): self._pd.initFromNewModel(self.orientation(), self.model()) axis = ("column", "row")[self.orientation()!=Qt.Horizontal] cnt = getattr(self.model(), axis+"Count")(QModelIndex()) if cnt: self.initializeSections(0, cnt-1) MultiIndexHeaderView=HierarchicalHeaderView class DataFrameModel(QtCore.QAbstractTableModel): #na_values:least|greatest - for sorting options = {"striped": True, "stripesColor": "#fafafa", "na_values": "least", "tooltip_min_len": 21} def __init__(self, dataframe=None): super().__init__() self.setDataFrame(dataframe if dataframe is not None else pd.DataFrame()) def setDataFrame(self, dataframe): self.df = dataframe.copy() # self.df_full = self.df self.layoutChanged.emit() def rowCount(self, parent): return len(self.df) def columnCount(self, parent): return len(self.df.columns) def readLevel(self, y=0, xs=0, xe=None, orient=None): c = getattr(self.df, ("columns", "index")[orient!=HorizontalHeaderDataRole]) if not hasattr(c, "levels"): #not MultiIndex return [QtGui.QStandardItem(str(i)) for i in c] sibl = [] section_start, v, xe = xs, None, xe or len(c) for i in range(xs, xe): label = c.labels[y][i] if label!=v: if y+1<len(c.levels) and i>xs: children = self.readLevel(y+1, section_start, i, orient=orient) sibl[-1].appendRow(children) item = QtGui.QStandardItem(str(c.levels[y][label])) sibl.append(item) section_start = i v=label if y+1<len(c.levels): children = self.readLevel(y+1, section_start, orient=orient) sibl[-1].appendRow(children) return sibl def data(self, index, role): row, col = index.row(), index.column() if role in (Qt.DisplayRole, Qt.ToolTipRole): ret = self.df.iat[row, col] if ret is not None and ret==ret: #convert to str except for None, NaN, NaT if isinstance(ret, float): ret = "{:n}".format(ret) elif isinstance(ret, datetime.date): #FIXME: show microseconds optionally ret = ret.strftime(("%x", "%c")[isinstance(ret, datetime.datetime)]) else: ret = str(ret) if role == Qt.ToolTipRole: if len(ret)<self.options["tooltip_min_len"]: ret = "" return ret elif role == Qt.BackgroundRole: if self.options["striped"] and row%2: return QBrush(QColor(self.options["stripesColor"])) elif role in (HorizontalHeaderDataRole, VerticalHeaderDataRole): hm = QtGui.QStandardItemModel() hm.appendRow(self.readLevel(orient=role)) return hm def reorder(self, oldIndex, newIndex, orientation): "Reorder columns / rows" horizontal = orientation==Qt.Horizontal cols = list(self.df.columns if horizontal else self.df.index) cols.insert(newIndex, cols.pop(oldIndex)) self.df = self.df[cols] if horizontal else self.df.T[cols].T return True # def filter(self, filt=None): # self.df = self.df_full if filt is None else self.df[filt] # self.layoutChanged.emit() def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return label = getattr(self.df, ("columns", "index")[orientation!=Qt.Horizontal])[section] # return label if type(label) is tuple else label return ("\n", " | ")[orientation!=Qt.Horizontal].join(str(i) for i in label) if type(label) is tuple else str(label) def dataFrame(self): return self.df def sort(self, column, order): # print("sort", column, order) #FIXME: double sort after setSortingEnabled(True) if len(self.df): asc = order==Qt.AscendingOrder na_pos = 'first' if (self.options["na_values"]=="least")==asc else 'last' self.df.sort_values(self.df.columns[column], ascending=asc, inplace=True, na_position=na_pos) self.layoutChanged.emit() if __name__=="__main__": import sys, locale locale.setlocale(locale.LC_ALL, '') #system locale settings app = QtGui.QApplication(sys.argv) form = QtGui.QWidget() form.setAttribute(Qt.WA_DeleteOnClose) #http://stackoverflow.com/a/27178019/1119602 form.setMinimumSize(700, 260) view = QtGui.QTableView() QtGui.QVBoxLayout(form).addWidget(view) form.show() #Prepare data tuples=[('bar', 'one', 'q'), ('bar', 'two', 'q'), ('baz', 'one', 'q'), ('baz', 'two', 'q'), ('foo', 'one', 'q'), ('foo', 'two', 'q'), ('qux', 'one', 'q'), ('qux', 'two', 'q')] index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second', 'third']) df=pd.DataFrame(pd.np.random.randn(6, 6), index=index[:6], columns=index[:6]) print("DataFrame:\n%s"%df) #Prepare view # oldh, oldv = view.horizontalHeader(), view.verticalHeader() # oldh.setParent(form), oldv.setParent(form) #Save old headers for some reason MultiIndexHeaderView(Qt.Horizontal, view) MultiIndexHeaderView(Qt.Vertical, view) view.horizontalHeader().setMovable(True) #reorder DataFrame columns manually #Set data view.setModel(DataFrameModel(df)) view.resizeColumnsToContents() view.resizeRowsToContents() #Set sorting enabled (after setting model) view.setSortingEnabled(True) sys.exit(app.exec())
def parentIndexes(self, index: QModelIndex) -> QModelIndexList: indexes = QModelIndexList() while index.isValid(): indexes.push_front(index) index = index.parent() return indexes
def findRootIndex(self, index: QModelIndex) -> QModelIndex: while index.parent().isValid(): index = index.parent() return index