def paintEvent(self, event):
        painter = QStylePainter(self)
        rect = self._subControlRect(QStyle.SC_SliderGroove)
        is_horizontal = self.orientation() == Qt.Horizontal

        minpos, maxpos = self.minimumPosition(), self.maximumPosition()
        span = rect.width() if is_horizontal else rect.height()
        x1 = QStyle.sliderPositionFromValue(
            self.minimum(), self.maximum(), minpos, span, self.invertedAppearance())
        x2 = QStyle.sliderPositionFromValue(
            self.minimum(), self.maximum(), maxpos, span, self.invertedAppearance())

        # Background
        painter.fillRect(rect, Qt.white)

        # Highlight
        painter.setOpacity(.7)
        if is_horizontal:
            painter.fillRect(x1, rect.y(), x2 - x1, rect.height(), Qt.yellow)
        else:
            painter.fillRect(rect.x(), x1, rect.width(), x2 - x1, Qt.yellow)
        painter.setOpacity(1)

        # Histogram
        if self._pixmap:
            painter.drawPixmap(rect, self._pixmap, self._pixmap.rect())

        # Frame
        painter.setPen(QPen(QBrush(Qt.darkGray), 2))
        painter.drawRect(rect)

        # Handles
        painter.setPen(QPen(QBrush(self._HANDLE_COLOR), self._HANDLE_WIDTH))
        painter.setOpacity(9)
        if is_horizontal:
            painter.drawLine(x1, rect.y(), x1, rect.y() + rect.height())
            painter.drawLine(x2, rect.y(), x2, rect.y() + rect.height())
        else:
            painter.drawLine(rect.x(), x1, rect.x() + rect.width(), x1)
            painter.drawLine(rect.x(), x2, rect.x() + rect.width(), x2)
        painter.setOpacity(1)

        if self._show_text:
            painter.setFont(QFont('sans-serif', 7, QFont.Bold))
            strMin, strMax = self.formatValues(minpos, maxpos)
            widthMin = painter.fontMetrics().width(strMin)
            widthMax = painter.fontMetrics().width(strMax)
            height = painter.fontMetrics().height()
            is_enough_space = x2 - x1 > 2 + (max(widthMax, widthMin)
                                             if is_horizontal else
                                             (2 * height + self._HANDLE_WIDTH))
            if is_enough_space:
                if is_horizontal:
                    painter.drawText(x1 + 3, rect.y() + height - 2, strMin)
                    painter.drawText(x2 - widthMax - 1, rect.y() + rect.height() - 2, strMax)
                else:
                    painter.drawText(rect.x() + 1, x1 + height, strMin)
                    painter.drawText(rect.x() + rect.width() - widthMax - 1, x2 - 2, strMax)
        def paintHorizontalSection(self, painter: QPainter, sectionRect: QRect,
                                   logicalLeafIndex: int, hv: QHeaderView,
                                   styleOptions: QStyleOptionHeader,
                                   leafIndex: QModelIndex):
            #            print(logicalLeafIndex)
            oldBO = painter.brushOrigin()
            top = sectionRect.y()
            indexes = QModelIndexList(self.parentIndexes(leafIndex))
            for i in range(indexes.size()):
                realStyleOptions = QStyleOptionHeader(styleOptions)
                if i < indexes.size() - 1 and (
                        realStyleOptions.state & QStyle.State_Sunken
                        or realStyleOptions.state & QStyle.State_On):
                    t = QStyle.State(QStyle.State_Sunken | QStyle.State_On)
                    realStyleOptions.state = realStyleOptions.state & ~t  #FIXME: parent items are not highlighted
                if i < indexes.size(
                ) - 1:  #Use sortIndicator for inner level only
                    realStyleOptions.sortIndicator = False


#                if i==0:
#                    print(self.leafs(indexes[i]), leafIndex)
                top = self.paintHorizontalCell(painter, hv, indexes[i],
                                               leafIndex, logicalLeafIndex,
                                               realStyleOptions, sectionRect,
                                               top)
            painter.setBrushOrigin(oldBO)
 def editorEvent(self, event, model, option, index):
     """edit right aligned checkbox"""
     flags = model.flags(index)
     # make sure that the item is checkable
     if (not (flags & Qt.ItemIsUserCheckable)
             or not (flags & Qt.ItemIsEnabled)):
         return False
     # make sure that we have a check state
     value = index.data(Qt.CheckStateRole)
     if not value.isValid():
         return False
     # make sure that we have the right event type
     if event.type() == QEvent.MouseButtonRelease:
         textMargin = self.__textMargin()
         checkRect = QStyle.alignedRect(
             option.direction, Qt.AlignRight, option.decorationSize,
             QRect(option.rect.x() + (2 * textMargin), option.rect.y(),
                   option.rect.width() - (2 * textMargin),
                   option.rect.height()))
         if not checkRect.contains(event.pos()):
             return False
     elif event.type() == QEvent.KeyPress:
         if event.key() not in (Qt.Key_Space, Qt.Key_Select):
             return False
     else:
         return False
     if value.toInt()[0] == Qt.Checked:
         state = Qt.Unchecked
     else:
         state = Qt.Checked
     return model.setData(index, state, Qt.CheckStateRole)
Beispiel #4
0
 def paintEvent(self, _event):
     p = QPainter(self)
     opt = QStyleOptionToolButton()
     opt.init(self)
     opt.state |= QStyle.State_AutoRaise
     if self.isEnabled() and self.underMouse(
     ) and not self.isChecked() and not self.isDown():
         opt.state |= QStyle.State_Raised
     if self.isChecked():
         opt.state |= QStyle.State_On
     if self.isDown():
         opt.state |= QStyle.State_Sunken
     self.style().drawPrimitive(QStyle.PE_PanelButtonTool, opt, p, self)
     opt.icon = self.icon()
     opt.subControls = QStyle.SubControls()
     opt.activeSubControls = QStyle.SubControls()
     #opt.features = QStyleOptionToolButton.None
     opt.arrowType = Qt.NoArrow
     size = self.style().pixelMetric(QStyle.PM_SmallIconSize, None, self)
     opt.iconSize = QSize(size, size)
     self.style().drawComplexControl(QStyle.CC_ToolButton, opt, p, self)
 def paint(self, painter, option, index):
     """paint right aligned checkbox"""
     viewItemOption = QStyleOptionViewItemV4(option)
     if self.cellFilter(index):
         textMargin = self.__textMargin()
         newRect = QStyle.alignedRect(
             option.direction, Qt.AlignRight,
             QSize(option.decorationSize.width() + 5,
                   option.decorationSize.height()),
             QRect(option.rect.x() + textMargin, option.rect.y(),
                   option.rect.width() - (2 * textMargin),
                   option.rect.height()))
         viewItemOption.rect = newRect
     QStyledItemDelegate.paint(self, painter, viewItemOption, index)
    def _pixelPosToRangeValue(self, pos):
        groove = self._subControlRect(QStyle.SC_SliderGroove)
        handle = self._subControlRect(QStyle.SC_SliderHandle)

        if self.orientation() == Qt.Horizontal:
            slider_length = handle.width()
            slider_min = groove.x()
            slider_max = groove.right() - slider_length + 1
        else:
            slider_length = handle.height()
            slider_min = groove.y()
            slider_max = groove.bottom() - slider_length + 1

        return QStyle.sliderValueFromPosition(
            self.minimum(), self.maximum(), pos - slider_min,
            slider_max - slider_min, self.invertedAppearance())
Beispiel #7
0
    def pixelPosToRangeValue(self, pos):
        opt = QStyleOptionSlider()
        self.initStyleOption(opt)

        gr = self.style().subControlRect(QStyle.CC_Slider, opt,
                                         QStyle.SC_SliderGroove, self)
        sr = self.style().subControlRect(QStyle.CC_Slider, opt,
                                         QStyle.SC_SliderHandle, self)
        if self.orientation() == QtCore.Qt.Horizontal:
            slider_length = sr.width()
            slider_min = gr.x()
            slider_max = gr.right() - slider_length + 1
        else:
            slider_length = sr.height()
            slider_min = gr.y()
            slider_max = gr.bottom() - slider_length + 1

        return QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), pos - slider_min,
                                              slider_max - slider_min, opt.upsideDown)
 def paintVerticalSection(self, painter: QPainter, sectionRect: QRect,
                          logicalLeafIndex: int, hv: QHeaderView,
                          styleOptions: QStyleOptionHeader,
                          leafIndex: QModelIndex):
     oldBO = painter.brushOrigin()
     left = sectionRect.x()
     indexes = QModelIndexList(self.parentIndexes(leafIndex))
     for i in range(indexes.size()):
         realStyleOptions = QStyleOptionHeader(styleOptions)
         if i < indexes.size() - 1 and (
                 realStyleOptions.state & QStyle.State_Sunken
                 or realStyleOptions.state & QStyle.State_On):
             t = QStyle.State(QStyle.State_Sunken | QStyle.State_On)
             realStyleOptions.state = realStyleOptions.state & ~t  #FIXME: parent items are not highlighted
         left = self.paintVerticalCell(painter, hv, indexes[i],
                                       leafIndex, logicalLeafIndex,
                                       realStyleOptions, sectionRect,
                                       left)
     painter.setBrushOrigin(oldBO)
Beispiel #9
0
    def start(self):
        """
        Starts the main window
        """
        QTApp = QtGui.QApplication(sys.argv)

        app_dir = os.path.dirname(os.path.realpath(__file__))
        icon_dir = app_dir + os.path.sep + "icons" + os.path.sep

        mainWindow = UiMainWindow(self.VERSION, icon_path=icon_dir)
        mainWindow.setGeometry(
            QStyle.alignedRect(
                QtCore.Qt.LeftToRight,
                QtCore.Qt.AlignCenter,
                mainWindow.size(),
                QTApp.desktop().availableGeometry()))

        mainWindow.show()

        sys.exit(QTApp.exec_())
Beispiel #10
0
    def pixelPosToRangeValue(self, pos):
        opt = QStyleOptionSlider()
        self.initStyleOption(opt)

        gr = self.style().subControlRect(QStyle.CC_Slider, opt,
                                         QStyle.SC_SliderGroove, self)
        sr = self.style().subControlRect(QStyle.CC_Slider, opt,
                                         QStyle.SC_SliderHandle, self)
        if self.orientation() == QtCore.Qt.Horizontal:
            slider_length = sr.width()
            slider_min = gr.x()
            slider_max = gr.right() - slider_length + 1
        else:
            slider_length = sr.height()
            slider_min = gr.y()
            slider_max = gr.bottom() - slider_length + 1

        return QStyle.sliderValueFromPosition(self.minimum(), self.maximum(),
                                              pos - slider_min,
                                              slider_max - slider_min,
                                              opt.upsideDown)
def paintVerticalSection(self, painter: QPainter, sectionRect: QRect, logicalLeafIndex: int, hv: QHeaderView, styleOptions: QStyleOptionHeader, leafIndex: QModelIndex):
oldBO = painter.brushOrigin()
left = sectionRect.x()
indexes = QModelIndexList(self.parentIndexes(leafIndex))
for i in range(indexes.size()):
    realStyleOptions = QStyleOptionHeader(styleOptions)
    if i<indexes.size()-1 and (realStyleOptions.state&QStyle.State_Sunken or realStyleOptions.state&QStyle.State_On):
        t = QStyle.State(QStyle.State_Sunken | QStyle.State_On)
        realStyleOptions.state = realStyleOptions.state&~t #FIXME: parent items are not highlighted
    left=self.paintVerticalCell(painter, hv, indexes[i], leafIndex, logicalLeafIndex, realStyleOptions, sectionRect, left)
painter.setBrushOrigin(oldBO)

def __init__(self, orientation: Qt.Orientation, parent: QWidget):
super().__init__(orientation, parent)
self._pd = self.private_data()
self.sectionResized.connect(self.on_sectionResized)
self.setHighlightSections(self.options.get("highlightSections"))
self.setClickable(self.options.get("clickable"))
self.show() #force to be visible
getattr(parent, "set%sHeader"%("Horizontal", "Vertical")[orientation!=Qt.Horizontal])(self)
self.sectionMoved.connect(self.on_sectionMoved)

def on_sectionMoved(self, logicalIndex, oldVisualIndex, newVisualIndex):
    view, model = self.parent(), self.parent().model()
    if not hasattr(model, "reorder"):
        return #reorder underlying data of models with /reorder/ def only
    if getattr(self, "manual_move", False):
        self.manual_move=False
        return
    self.manual_move=True
    self.moveSection(newVisualIndex, oldVisualIndex) #cancel move
    if model.reorder(oldVisualIndex, newVisualIndex, self.orientation()):
        #Reorder column widths / row heights
        horizontal = self.orientation()==Qt.Horizontal
        itemSize = (view.rowHeight, view.columnWidth)[horizontal]
        setItemSize = (view.setRowHeight, view.setColumnWidth)[horizontal]
        rng = sorted((oldVisualIndex, newVisualIndex))
        options = [(itemSize(i), i) for i in range(rng[0], rng[1]+1)]
        options.insert(newVisualIndex-rng[0], options.pop(oldVisualIndex-rng[0]))
        for i, col in enumerate(range(rng[0], rng[1]+1)):
            setItemSize(col, options[i][0])
        getattr(view, "select"+("Row", "Column")[horizontal])(newVisualIndex) #FIXME: don't select if sorting is enable?
        if self.isSortIndicatorShown():
            sortIndIndex = next((i for i, o in enumerate(options) if o[1]==self.sortIndicatorSection()), None)
            if sortIndIndex is not None: #sort indicator is among sections being reordered
                self.setSortIndicator(sortIndIndex+rng[0], self.sortIndicatorOrder()) #FIXME: does unnecessary sorting
        model.layoutChanged.emit() #update view

def styleOptionForCell(self, logicalInd: int)->QStyleOptionHeader:
opt = QStyleOptionHeader()
self.initStyleOption(opt)
if self.isSortIndicatorShown() and self.sortIndicatorSection()==logicalInd:
    opt.sortIndicator = (QStyleOptionHeader.SortUp, QStyleOptionHeader.SortDown)[self.sortIndicatorOrder()==Qt.AscendingOrder]
if self.window().isActiveWindow():
    opt.state = opt.state|QStyle.State_Active
opt.textAlignment = Qt.AlignCenter
opt.iconAlignment = Qt.AlignVCenter
opt.section = logicalInd
visual = self.visualIndex(logicalInd)
if self.count() == 1:
    opt.position = QStyleOptionHeader.OnlyOneSection
else:
    if visual == 0:
        opt.position = QStyleOptionHeader.Beginning
    else:
        opt.position = QStyleOptionHeader.End if visual==self.count()-1 else QStyleOptionHeader.Middle
if self.isClickable():
    #            if logicalIndex == d.hover:
    #            ...
    if self.highlightSections() and self.selectionModel():
        if self.orientation()==Qt.Horizontal:
            if self.selectionModel().columnIntersectsSelection(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_On
            if self.selectionModel().isColumnSelected(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_Sunken
        else:
            if self.selectionModel().rowIntersectsSelection(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_On
            if self.selectionModel().isRowSelected(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_Sunken
if self.selectionModel():
    previousSelected=False
    if self.orientation()==Qt.Horizontal:
        previousSelected = self.selectionModel().isColumnSelected(self.logicalIndex(visual - 1), self.rootIndex())
    else:
        previousSelected = self.selectionModel().isRowSelected(self.logicalIndex(visual - 1), self.rootIndex())
    nextSelected=False
    if self.orientation()==Qt.Horizontal:
        nextSelected = self.selectionModel().isColumnSelected(self.logicalIndex(visual + 1), self.rootIndex())
    else:
        nextSelected = self.selectionModel().isRowSelected(self.logicalIndex(visual + 1), self.rootIndex())
    if previousSelected and nextSelected:
        opt.selectedPosition = QStyleOptionHeader.NextAndPreviousAreSelected
    else:
        if previousSelected:
            opt.selectedPosition = QStyleOptionHeader.PreviousIsSelected
        else:
            if nextSelected:
                opt.selectedPosition = QStyleOptionHeader.NextIsSelected
            else:
                opt.selectedPosition = QStyleOptionHeader.NotAdjacent
return opt

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())
Beispiel #12
0
def center_widget_on_desktop(widget):
    widget.setGeometry(QStyle.alignedRect(Qt.LeftToRight,
                                          Qt.AlignCenter,
                                          widget.size(),
                                          qApp.desktop().availableGeometry()))