亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Python (PyQt5) 版本的 Qt Callout 示例

Python (PyQt5) 版本的 Qt Callout 示例

MMMHUHU 2022-07-26 20:44:49
標注示例來自 QtCharts 包:https ://doc.qt.io/qt-5/qtcharts-callout-example.html這個(優秀的)示例展示了如何在圖表頂部繪制附加元素(標注)。問題是這個例子是否可以在 python 實現的某個地方找到?
查看完整描述

1 回答

?
蠱毒傳說

TA貢獻1895條經驗 獲得超3個贊

     我一直在尋找移植到 PyQt5 (python) 的示例,但沒有找到。所以我自己寫的,想和大家分享一下(只是為了節省你的時間)。它是為 Python 3.8 編寫的,但也適用于舊版本,PyQt 版本是 5.14.1。


與原始的 C++ 實現只有一點點不同。QGraphicsScene必須顯式設置 - 在View構造函數類中,并且每個新Callout對象都必須顯式添加到場景中。坦率地說,我不知道為什么,但我不在乎。C++ 中完全相同的 Qt 庫版本不需要。


import sys

from typing import List


from PyQt5.QtChart import QChart, QLineSeries, QSplineSeries

from PyQt5.QtCore import QPointF, QRect, QRectF, QSizeF, Qt

from PyQt5.QtGui import QColor, QFont, QFontMetrics, QMouseEvent, QPainter, QPainterPath, QResizeEvent

from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsScene, QGraphicsSceneMouseEvent, \

    QGraphicsSimpleTextItem, QGraphicsView, QStyleOptionGraphicsItem, QWidget



class Callout(QGraphicsItem):


    def __init__(self, parent: QChart):

        super().__init__()

        self.m_chart: QChart = parent

        self.m_text: str = ''

        self.m_anchor: QPointF = QPointF()

        self.m_font: QFont = QFont()

        self.m_textRect: QRectF = QRectF()

        self.m_rect: QRectF = QRectF()


    def setText(self, text: str):

        self.m_text = text

        metrics = QFontMetrics(self.m_font)

        self.m_textRect = QRectF(metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self.m_text))

        self.m_textRect.translate(5, 5)

        self.prepareGeometryChange()

        self.m_rect = QRectF(self.m_textRect.adjusted(-5, -5, 5, 5))

        self.updateGeometry()


    def updateGeometry(self):

        self.prepareGeometryChange()

        self.setPos(self.m_chart.mapToPosition(self.m_anchor) + QPointF(10, -50))


    def boundingRect(self) -> QRectF:

        from_parent = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor))

        anchor = QPointF(from_parent)

        rect = QRectF()

        rect.setLeft(min(self.m_rect.left(), anchor.x()))

        rect.setRight(max(self.m_rect.right(), anchor.x()))

        rect.setTop(min(self.m_rect.top(), anchor.y()))

        rect.setBottom(max(self.m_rect.bottom(), anchor.y()))

        return rect


    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget):

        path = QPainterPath()

        mr = self.m_rect

        path.addRoundedRect(mr, 5, 5)


        anchor = QPointF(self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)))

        if not mr.contains(anchor):

            point1 = QPointF()

            point2 = QPointF()


            # establish the position of the anchor point in relation to self.m_rect

            above = anchor.y() <= mr.top()

            above_center = mr.top() < anchor.y() <= mr.center().y()

            below_center = mr.center().y() < anchor.y() <= mr.bottom()

            below = anchor.y() > mr.bottom()


            on_left = anchor.x() <= mr.left()

            left_of_center = mr.left() < anchor.x() <= mr.center().x()

            right_of_center = mr.center().x() < anchor.x() <= mr.right()

            on_right = anchor.x() > mr.right()


            # get the nearest self.m_rect corner.

            x = (on_right + right_of_center) * mr.width()

            y = (below + below_center) * mr.height()

            corner_case = (above and on_left) or (above and on_right) or (below and on_left) or (below and on_right)

            vertical = abs(anchor.x() - x) > abs(anchor.y() - y)

            horizontal = bool(not vertical)


            x1 = x + left_of_center * 10 - right_of_center * 20 + corner_case * horizontal * (

                    on_left * 10 - on_right * 20)

            y1 = y + above_center * 10 - below_center * 20 + corner_case * vertical * (above * 10 - below * 20)

            point1.setX(x1)

            point1.setY(y1)


            x2 = x + left_of_center * 20 - right_of_center * 10 + corner_case * horizontal * (

                    on_left * 20 - on_right * 10)

            y2 = y + above_center * 20 - below_center * 10 + corner_case * vertical * (above * 20 - below * 10)

            point2.setX(x2)

            point2.setY(y2)


            path.moveTo(point1)

            path.lineTo(anchor)

            path.lineTo(point2)

            path = path.simplified()


        painter.setPen(QColor(30, 30, 30))

        painter.setBrush(QColor(255, 255, 255))

        painter.drawPath(path)

        painter.drawText(self.m_textRect, self.m_text)


    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):

        event.setAccepted(True)


    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):

        if event.buttons() & Qt.LeftButton:

            self.setPos(self.mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton)))

            event.setAccepted(True)

        else:

            event.setAccepted(False)



class View(QGraphicsView):


    def __init__(self, parent=None):

        super().__init__(parent)

        self.m_callouts: List[Callout] = []

        self.setDragMode(QGraphicsView.NoDrag)

        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)


        # chart

        self.m_chart = QChart(parent)

        self.m_chart.setMinimumSize(640, 480)

        self.m_chart.setTitle("Hover the line to show callout. Click the line to make it stay")

        self.m_chart.legend().hide()

        series = QLineSeries()

        series.append(1, 3)

        series.append(4, 5)

        series.append(5, 4.5)

        series.append(7, 1)

        series.append(11, 2)

        self.m_chart.addSeries(series)


        series2 = QSplineSeries()

        series2.append(1.6, 1.4)

        series2.append(2.4, 3.5)

        series2.append(3.7, 2.5)

        series2.append(7, 4)

        series2.append(10, 2)

        self.m_chart.addSeries(series2)


        self.m_chart.createDefaultAxes()

        self.m_chart.setAcceptHoverEvents(True)


        self.setRenderHint(QPainter.Antialiasing)


        self.setScene(QGraphicsScene())

        self.scene().addItem(self.m_chart)


        self.m_coordX = QGraphicsSimpleTextItem(self.m_chart)

        self.m_coordX.setPos(self.m_chart.size().width() / 2 - 50, self.m_chart.size().height() - 20)

        self.m_coordX.setText("X: ")

        self.m_coordY = QGraphicsSimpleTextItem(self.m_chart)

        self.m_coordY.setPos(self.m_chart.size().width() / 2 + 50, self.m_chart.size().height() - 20)

        self.m_coordY.setText("Y: ")


        self.m_tooltip = Callout(self.m_chart)

        self.scene().addItem(self.m_tooltip)


        series.clicked.connect(self.keep_callout)

        series.hovered.connect(self.tooltip)

        series2.clicked.connect(self.keep_callout)

        series2.hovered.connect(self.tooltip)


        self.setMouseTracking(True)


    def resizeEvent(self, event: QResizeEvent):

        if scene := self.scene():

            scene.setSceneRect(QRectF(QPointF(0, 0), QSizeF(event.size())))

            self.m_chart.resize(QSizeF(event.size()))

            self.m_coordX.setPos(self.m_chart.size().width() / 2 - 50, self.m_chart.size().height() - 20)

            self.m_coordY.setPos(self.m_chart.size().width() / 2 + 50, self.m_chart.size().height() - 20)


            for callout in self.m_callouts:

                callout.updateGeometry()


        super().resizeEvent(event)


    def mouseMoveEvent(self, event: QMouseEvent):

        from_chart = self.m_chart.mapToValue(event.pos())

        self.m_coordX.setText(f"X: {from_chart.x()}")

        self.m_coordX.setText(f"Y: {from_chart.y()}")

        super().mouseMoveEvent(event)


    def keep_callout(self):

        self.m_callouts.append(self.m_tooltip)

        self.m_tooltip = Callout(self.m_chart)

        self.scene().addItem(self.m_tooltip)


    def tooltip(self, point: QPointF, state: bool):

        if not self.m_tooltip:

            self.m_tooltip = Callout(self.m_chart)


        if state:

            self.m_tooltip.setText(f"X: {point.x()} \nY: {point.x()} ")

            self.m_tooltip.m_anchor = point

            self.m_tooltip.setZValue(11)

            self.m_tooltip.updateGeometry()

            self.m_tooltip.show()

        else:

            self.m_tooltip.hide()



if __name__ == '__main__':

    app = QApplication(sys.argv)

    window = View()

    window.resize(640, 480)

    window.show()

    sys.exit(app.exec_())


查看完整回答
反對 回復 2022-07-26
  • 1 回答
  • 0 關注
  • 121 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號