赞
踩
在数据可视化应用中,对图表里某些点,我们期望将其特别标注出来,比如对曲线上的某些点的坐标值数据标注出来。在这种情况下,我们可以自定义一个绘制标注的类来实现。在本文的示例中,定义类Callout, 用于实现标注的绘制。
QChart继承自QGraphicsWidget, 标注对象可以看做是QGraphicsWidget中的条目,因此Callout自然而然继承自QGraphicsItem。并重载函数boundingRect()以计算Callout的外框大小,paint()函数实现对图标的绘制。
根据Qt演示代码,在图标中添加一条折线和一条曲线,当鼠标移动到这些线上的某个点时,则会显示该点的坐标标注,如果此时按下鼠标左键,则可以将标注添加在图标中。完整代码如下:
- import sys
- from PyQt5.QtCore import Qt, QPoint, QPointF, QRect, QRectF
- from PyQt5.QtGui import QFont, QPainter, QPainterPath, QColor, QFontMetrics
- from PyQt5.QtWidgets import (QApplication, QWidget, QGraphicsItem, QGraphicsSimpleTextItem,
- QStyleOptionGraphicsItem, QGraphicsView, QGraphicsScene)
- from PyQt5.QtChart import QChart, QChartView, QLineSeries, QSplineSeries
-
- #实现标注绘制
- class Callout(QGraphicsItem):
- def __init__(self, chart):
- super(Callout, self).__init__(chart)
- self.chart = chart
- self.text=''
- self.anchor = QPointF()
- self.rect = QRectF()
- self.textRect = QRectF()
- self.font = QFont()
-
- def boundingRect(self):
- anchor = self.mapFromParent(self.chart.mapToPosition(self.anchor))
- rect = QRectF()
- rect.setLeft(min(self.rect.left(), anchor.x()))
- rect.setRight(max(self.rect.right(), anchor.x()))
- rect.setTop(min(self.rect.top(), anchor.y()))
- rect.setBottom(max(self.rect.bottom(), anchor.y()))
-
- return rect
-
- def paint(self, painter, option, widget):
- path = QPainterPath()
- path.addRoundedRect(self.rect, 5, 5)
-
- anchor = self.mapFromParent(self.chart.mapToPosition(self.anchor))
- if not (self.rect.contains(anchor) and self.anchor.isNull()):
- point1 = QPointF()
- point2 = QPointF()
-
- #相对于self.rect的位置建立锚点
- above = anchor.y() <= self.rect.top()
- aboveCenter = anchor.y() > self.rect.top() and anchor.y() <= self.rect.center().y()
- belowCenter = anchor.y() > self.rect.center().y() and anchor.y() <= self.rect.bottom()
- below = anchor.y() > self.rect.bottom()
-
- left = anchor.x() <= self.rect.left()
- leftCenter = anchor.x() > self.rect.left() and anchor.x() <= self.rect.center().x()
- rightCenter = anchor.x() > self.rect.center().x() and anchor.x() <= self.rect.right()
- right = anchor.x() > self.rect.right()
-
- #得到self.rect最近的角落位置
- x = (right + rightCenter) * self.rect.width()
- y = (below + belowCenter) * self.rect.height()
- cornerCase = (above and left) or (above and right) or (below and left) or (below and right)
- vertical = abs(anchor.x() - x) > abs(anchor.y() - y)
-
- x1 = x + leftCenter * 10 - rightCenter * 20 + cornerCase * (not vertical) * (left * 10 - right * 20)
- y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20)
- point1.setX(x1)
- point1.setY(y1)
-
- x2 = x + leftCenter * 20 - rightCenter * 10 + cornerCase * (not vertical) * (left * 20 - right * 10)
- y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 -below * 10)
- point2.setX(x2)
- point2.setY(y2)
-
- path.moveTo(point1)
- path.lineTo(anchor)
- path.lineTo(point2)
- path = path.simplified()
-
- painter.setBrush(QColor(255, 255, 255))
- painter.drawPath(path)
- painter.drawText(self.textRect, self.text)
-
-
- def mousePressEvent(self, event):
- event.setAccepted(True)
-
- def mouseMoveEvent(self, event):
- if event.buttons() & Qt.LeftButton:
- self.setPos(self.mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton)))
- event.setAccepted(True)
- else:
- event.setAccepted(False)
-
- def setText(self, text):
- self.text = text
- metrics = QFontMetrics(self.font)
- self.textRect = QRectF(metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self.text))
- self.textRect.translate(5, 5)
- self.prepareGeometryChange()
- self.rect = self.textRect.adjusted(-5, -5, 5, 5)
-
- def setAnchor(self, point):
- self.anchor = point
-
- def updateGeometry(self):
- self.prepareGeometryChange()
- self.setPos(self.chart.mapToPosition(self.anchor) + QPoint(10, 50))
-
- class DemoCallout(QGraphicsView):
- def __init__(self, parent=None):
- super(DemoCallout, self).__init__(QGraphicsScene(), parent)
-
- # 设置窗口标题
- self.setWindowTitle('实战 Qt for Python: 给图表添加标记')
- # 设置窗口大小
- self.setMinimumSize(640, 480)
-
- self.tooltip = None
- self.callouts = []
-
- self.setDragMode(QGraphicsView.NoDrag)
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
-
- #图表
- self.chart = QChart()
- self.chart.setMinimumSize(640, 480)
- self.chart.setTitle('悬停在线上显示标注, 点击在该点添加标注')
- self.chart.legend().hide()
- lineSeries = QLineSeries()
- lineSeries.append(1, 3)
- lineSeries.append(4, 5)
- lineSeries.append(5, 4.5)
- lineSeries.append(7, 1)
- lineSeries.append(11, 2)
- self.chart.addSeries(lineSeries)
-
- splineSeries = QSplineSeries()
- splineSeries.append(1.6, 1.4)
- splineSeries.append(2.4, 3.5)
- splineSeries.append(3.7, 2.5)
- splineSeries.append(7, 4)
- splineSeries.append(10, 2)
- self.chart.addSeries(splineSeries)
-
- self.chart.createDefaultAxes()
- self.chart.setAcceptHoverEvents(True)
-
- self.setRenderHint(QPainter.Antialiasing)
- self.scene().addItem(self.chart)
-
- self.coordX = QGraphicsSimpleTextItem(self.chart)
- self.coordX.setPos(self.chart.size().width()/2 - 50, self.chart.size().height())
- self.coordX.setText('X: ')
- self.coordY = QGraphicsSimpleTextItem(self.chart)
- self.coordY.setPos(self.chart.size().width()/2 + 50, self.chart.size().height())
- self.coordY.setText('Y: ')
-
- lineSeries.clicked.connect(self.keepCallout)
- lineSeries.hovered.connect(self.showTooltip)
-
- splineSeries.clicked.connect(self.keepCallout)
- splineSeries.hovered.connect(self.showTooltip)
-
- def resizeEvent(self, event):
- if self.scene():
- event_w = event.size().width()
- event_h = event.size().height()
- self.scene().setSceneRect(0, 0, event_w, event_h)
- self.chart.resize(event_w, event_h)
- chart_w = self.chart.size().width()
- chart_h = self.chart.size().height()
- self.coordX.setPos(chart_w/2 - 50, chart_h - 20)
- self.coordY.setPos(chart_w/2 + 50, chart_h - 20)
- for callout in self.callouts:
- callout.updateGeometry()
- QGraphicsView.resizeEvent(self, event)
-
- def mouseMoveEvent(self, event):
- self.coordX.setText('X: %.5f' % self.chart.mapToValue(event.pos()).x())
- self.coordY.setText('Y: %.5f' % self.chart.mapToValue(event.pos()).y())
- QGraphicsView.mouseMoveEvent(self, event)
-
- def keepCallout(self):
- self.callouts.append(self.tooltip)
- self.tooltip = Callout(self.chart)
-
- def showTooltip(self, point, state):
- if self.tooltip is None:
- self.tooltip = Callout(self.chart)
- if state:
- self.tooltip.setText('X: %.5f\n Y: %.5f' % (point.x(), point.y()))
- self.tooltip.setAnchor(point)
- self.tooltip.setZValue(11)
- self.tooltip.updateGeometry()
- self.tooltip.show()
- else:
- self.tooltip.hide()
-
- if __name__ == '__main__':
- app = QApplication(sys.argv)
- window = DemoCallout()
- window.show()
- sys.exit(app.exec())
运行结果如下图:
为图表添加标注
请多多关注,评论,收藏,点赞,和转发。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。