Выравнивание QLabel по центру GGraphicsEllipseItem в QGraphicsScene - PullRequest
0 голосов
/ 04 мая 2020

Мне интересно, есть ли способ позиционировать Qlabel в середине «сектора» на моем графике p ie, который я разработал с помощью PyQt. Секторами являются QgraphicsEllipseItems, которые я назначил с соответствующими начальными и промежуточными углами. До сих пор я пытался поместить метку в QGraphicsProxyItem и установить 'Slice' или 'Sector' в качестве родителя. Затем я попытался расположить ярлык посередине «сектора». Однако проблема заключается в том, что boundingRect является прямоугольником, а не просто самим сектором.

Текущий круговой график

На изображении выше вы можете увидеть проблему. Ярлыки расположены неправильно. Я пытаюсь добиться чего-то подобного с метками:

Таблица моделей с метками в правильном месте

Текущий код:

    pie = QtWidgets.QGraphicsEllipseItem(60, 110, self.piechart.width, self.piechart.height)
    pie.setTransformOriginPoint(pie.boundingRect().center())
    pie.setZValue(-1)
    self.scene.addItem(pie)

    start_angle = 0


    for sector in self.piechart.sectors:
        slice = QtWidgets.QGraphicsEllipseItem(60, 110, self.piechart.width, self.piechart.height, parent=pie)
        color = QtGui.QBrush(sector.color)
        slice.setBrush(color)
        span_angle = sector.proportional_volume*360*16
        slice.setStartAngle(start_angle)
        slice.setSpanAngle(span_angle)
        start_angle += span_angle

        label = QtWidgets.QLabel(str(round(sector.proportional_volume*100,2))+'%', alignment = QtCore.Qt.AlignCenter)
        label.setAutoFillBackground(False)
        label.setStyleSheet('background-color: transparent')
        font = QtGui.QFont('Times', 8)
        font.setBold(True)
        label.setFont(font)

        proxy = QtWidgets.QGraphicsProxyWidget(slice)
        proxy.setWidget(label)
        proxy.setPos(slice.boundingRect().center()) 

        self.scene.addItem(slice)
        self.scene.addItem(proxy)

1 Ответ

0 голосов
/ 05 мая 2020

Вы не можете использовать центр ограничивающего прямоугольника из-за геометрических причин (из базового c QGraphicsItem.boundingRect () определение):

Это чисто виртуальная функция определяет внешние границы элемента как прямоугольник

Это означает, что независимо от формы элемента ограничивающий прямоугольник является прямоугольником, который включает в себя целую форму item.
На следующем изображении вы ясно видите, что « это не тот центр, который вы ищете » [цит.]:

bounding rect center not in the middle

Что вам действительно нужно, так это центр в средней оси каждого среза, так что это просто вопрос деления пополам экстента этого среза и использования половины радиуса .

Обратите внимание, что графические элементы (как и виджеты) обычно используют свой верхний левый угол в качестве ориентира для своих позиций, и если вы используете для этого "середину", вы не получите правильный результат:

label not centered

Av Кроме того, вы должны вычесть центр прямоугольника метки, чтобы он был правильно отцентрирован в нужном вам месте.

        # create a common rectangle, since we're going to use the same coordinates
        # for all ellipses
        rect = QtCore.QRectF(60, 110, self.piechart.width, self.piechart.height)
        pie = QtWidgets.QGraphicsEllipseItem(rect)
        pie.setTransformOriginPoint(pie.boundingRect().center())
        pie.setZValue(-1)
        self.scene.addItem(pie)

        # the center position is half of the radius, or a quarter of the size
        labelRadius = self.piechart.width * .25
        center = rect.center()
        start_angle = 0


        for sector in self.piechart.sectors:
            slice = QtWidgets.QGraphicsEllipseItem(rect, parent=pie)
            slice.setPen(QtGui.QPen(QtCore.Qt.NoPen))
            color = QtGui.QBrush(sector.color)
            slice.setBrush(color)
            span_angle = sector.proportional_volume*360*16
            slice.setStartAngle(start_angle)
            slice.setSpanAngle(span_angle)
            # we need the current start_angle later, let's do this later
            # start_angle += span_angle

            label = QtWidgets.QLabel(str(round(sector.proportional_volume*100,2))+'%', alignment = QtCore.Qt.AlignCenter)
            label.setAutoFillBackground(False)
            label.setStyleSheet('background-color: transparent')
            font = QtGui.QFont('Times', 8)
            font.setBold(True)
            label.setFont(font)

            proxy = QtWidgets.QGraphicsProxyWidget(slice)
            proxy.setWidget(label)

            # get the middle angle of the slice
            angle = start_angle / 16 + sector.proportional_volume * 180

            # create a line that has a length that's a quarter of the radius, and
            # has an angle that is in the middle of the current slice; finally,
            # get its end point (p2)
            line = QtCore.QLineF.fromPolar(labelRadius, angle)
            middlePoint = line.p2()

            # the above can also be achieved by using trigonometry functions; note
            # that the sine is negative, since QGraphicsScene coordinates are
            # inverted vertically (downwards coordinates are positive)
            # x = cos(radians(angle)) * labelRadius
            # y = -sin(radians(angle)) * labelRadius
            # middlePoint = QtCore.QPointF(x, y)

            # finally, translate the point to the center of the ellipse
            middlePoint += center
            r = QtCore.QRectF(-1, -1, 2, 2)
            self.scene.addEllipse(r.translated(middlePoint))

            # center the label at that point by subtracting its own center
            proxy.setPos(middlePoint - label.rect().center())

            start_angle += span_angle

            # when you create a QGraphicsItem with a parent that has already been
            # added to the scene, there's no need to add it again
            # self.scene.addItem(slice)
            # self.scene.addItem(proxy)

Учтите, что с графической точки зрения фактическая "средняя" позиция будет часто воспринимается ближе к центру из-за оптического обмана, основанного на геометрии среза и визуальном восприятии символов; следующее изображение основано на приведенном выше коде, и, как вы можете видеть, маленький светло-зеленый срез справа (4,76%) кажется смещенным, даже если он правильно отцентрирован:

final result

Итак, вы можете предпочесть использовать немного более высокое соотношение для labelRadius (например, 0,3).

Наконец, два соображения:

  • некоторые проценты могут быть очень маленькими, в результате некоторые метки могут быть частично скрыты следующим слайсом; вам, вероятно, следует избегать привязки метки к фрагменту
  • , используя QGraphicsProxyWidget для простого QLabel, нет необходимости, рассмотрите возможность использования QGraphicsSimpleTextItem или QGraphicsTextItem , но внимательно прочитайте их документация, так как их система координат не ведет себя точно так же.
...