//QChart 类提供了两种在场景坐标和系列域(由轴范围定义)之间映射的方法。
//QPointF QChart::mapToPosition(const QPointF &value, QAbstractSeries *series)
//QPointF QChart::mapToValue(const QPointF &position, QAbstractSeries *series)
#include
#include "view.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
View w;
w.show();
return a.exec();
}
view.h
#ifndef VIEW_H
#define VIEW_H
#include
#include
QT_BEGIN_NAMESPACE
//该类用作 QGraphicsItems 的容器。
//它与 QGraphicsView 一起用于在 2D 表面上可视化图形项目
//例如线条、矩形、文本,甚至自定义项目。
//QGraphicsScene 是图形视图框架的一部分。
//QGraphicsScene 还提供了一些功能,
//可以让您有效地确定项目的位置,
//并确定哪些项目在场景的任意区域内可见。
//使用 QGraphicsView 小部件,
//您可以可视化整个场景,或者放大并仅查看场景的一部分。
/*
QGraphicsScene scene;
scene.addText("Hello,world!");
QGraphicsView view(&scene);
view.show();
*/
//请注意,QGraphicsScene 没有自己的视觉外观;
//它只管理项目。
//您需要创建一个 QGraphicsView 小部件来可视化场景。
//要将项目添加到场景中,您首先要构造一个 QGraphicsScene 对象。
//然后,您有两个选择:
//通过调用 addItem() 添加现有的 QGraphicsItem 对象
//或者您可以调用便利函数之一 addEllipse()、addLine()、addPath()、
//addPixmap()、addPolygon()、addRect( ) 或 addText(),
//它们都返回一个指向新添加项的指针。
//添加这些函数的物品的尺寸是相对于物品的坐标系而言的
//物品的位置在场景中被初始化为(0, 0)。
//然后您可以使用 QGraphicsView 可视化场景。
//当场景发生变化时,(例如,当一个项目移动或变形时)
//QGraphicsScene 会发出 changed() 信号。
//要删除项目,请调用 removeItem()。
//QGraphicsScene 使用索引算法来有效地管理项目的位置。
//默认情况下,使用 BSP(二进制空间分区)树;
//适用于大多数项目保持静态(即不移动)的大型场景的算法。
//您可以通过调用 setItemIndexMethod() 选择禁用此索引。
//有关可用索引算法的更多信息,请参阅 itemIndexMethod 属性。
//场景的边界矩形是通过调用 setSceneRect() 设置的。
//物品可以放置在场景的任意位置,场景的大小默认没有限制。
//场景 rect 仅用于内部簿记,维护场景的项目索引。
//如果场景矩形未设置,QGraphicsScene 将使用 itemsBoundingRect()
//返回的所有项目的边界区域作为场景矩形。
//然而,itemsBoundingRect() 是一个相对耗时的函数
//因为它通过收集场景中每个项目的位置信息来运行。
//因此,在大型场景上操作时,您应该始终设置场景矩形。
//QGraphicsScene 的最大优势之一是它能够有效地确定项目的位置。
//即使场景中有数百万个项目,items() 函数也可以在几毫秒内确定项目的位置。
//items() 有几种重载:一种是在特定位置查找项目,
//另一种是在多边形或矩形内或与多边形或矩形相交的项目中查找项目
//等等。 返回的项目列表按堆叠顺序排序,最上面的项目是列表中的第一个项目。
//为方便起见,还有一个 itemAt() 函数返回给定位置的最上面的项目。
//QGraphicsScene 维护场景的选择信息。
//。 要选择项目,请调用 setSelectionArea(),要清除当前选择,请调用 clearSelection()。
//调用 selectedItems() 以获取所有选定项目的列表。
//事件处理和传播
//QGraphicsScene 的另一个职责是从 QGraphicsView 传播事件。
//要将事件发送到场景,请构造一个继承 QEvent 的事件,
//然后使用例如 QCoreApplication::sendEvent() 发送它。
//event() 负责将事件分派到各个项目。
//一些常见事件由便利事件处理程序处理。
//例如,按键事件由 keyPressEvent() 处理,鼠标按下事件由 mousePressEvent() 处理。
//关键事件被传递到焦点项目。
//要设置焦点项目,您可以调用 setFocusItem(),传递接受焦点的项目,
//或者项目本身可以调用 QGraphicsItem::setFocus()。
//调用 focusItem() 以获取当前焦点项。
//为了与小部件兼容,场景还维护自己的焦点信息。
//默认情况下,场景没有焦点,所有关键事件都被丢弃。
// /如果 setFocus() 被调用,或者场景上的一个项目获得焦点,
//场景会自动获得焦点。
//如果场景有焦点,hasFocus() 将返回 true,并且关键事件将被转发到焦点项(如果有)。
//如果场景失去焦点,(即有人调用 clearFocus())而项目有焦点,
//场景将保持其项目焦点信息
//一旦场景重新获得焦点,它将确保最后一个焦点项目重新获得焦点。
//对于鼠标悬停效果,QGraphicsScene 调度悬停事件。
//如果一个项目接受悬停事件(参见 QGraphicsItem::acceptHoverEvents())
//当鼠标进入它的区域时它会收到一个 GraphicsSceneHoverEnter 事件。
//当鼠标继续在项目区域内移动时,
//QGraphicsScene 将向它发送 GraphicsSceneHoverMove 事件。
//当鼠标离开该项目的区域时,该项目将收到一个 GraphicsSceneHoverLeave 事件。
//所有鼠标事件都传递给当前的鼠标抓取器项目。
//如果一个项目接受鼠标事件(参见 QGraphicsItem::acceptedMouseButtons())
//并且它接收鼠标按下,它就会成为场景的鼠标抓取器。
//当没有按下其他鼠标按钮时,它会保持鼠标抓取器,直到它收到鼠标释放。
//您可以调用 mouseGrabberItem() 来确定当前正在抓取鼠标的项目。
class QGraphicsScene;
//当在小部件内按下或释放鼠标按钮或移动鼠标光标时,会发生鼠标事件。
//只有在按下鼠标按钮时才会发生鼠标移动事件
//除非使用 QWidget::setMouseTracking() 启用鼠标跟踪。
//当在小部件内按下鼠标按钮时,Qt 会自动抓取鼠标;
//小部件将继续接收鼠标事件,直到释放最后一个鼠标按钮。
//鼠标事件包含一个特殊的接受标志,指示接收者是否需要该事件。
//如果您的小部件未处理鼠标事件,您应该调用 ignore()。
//鼠标事件沿父小部件链向上传播,直到小部件通过 accept() 接受它,
//,或者事件过滤器使用它。
//注意:如果鼠标事件被传播到一个已经设置了
//Qt::WA_NoMousePropagation 的小部件,该鼠标事件将不会在父小部件链上进一步传播。
//可以通过调用从 QInputEvent 继承的 modifiers() 函数找到键盘修饰键的状态。
//position() 函数给出相对于接收鼠标事件的小部件或项目的光标位置。
//如果由于鼠标事件而移动小部件,
//请使用 globalPosition() 返回的全局位置来避免晃动。
//QWidget::setEnabled() 函数可用于启用或禁用小部件的鼠标和键盘事件。
//重新实现 QWidget 事件处理程序、
//QWidget::mousePressEvent()、
//QWidget::mouseReleaseEvent()、
//QWidget::mouseDoubleClickEvent() 和
//QWidget::mouseMoveEvent() 以在您自己的小部件中接收鼠标事件。
class QMouseEvent;
//调整大小事件被发送到已调整大小的小部件。
//事件处理程序 QWidget::resizeEvent() 接收调整大小事件。
class QResizeEvent;
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
//QChart 是一个 QGraphicsWidget,您可以在 QGraphicsScene 中显示它。
//它管理不同类型系列和其他图表相关对象(如图例和轴)的图形表示。
//为了简单地在布局中显示图表,可以使用便利类 QChartView 代替 QChart。
//此外,可以使用 QPolarChart 类将折线、样条曲线、面积和散点序列显示为极坐标图。
class QChart;
QT_END_NAMESPACE
class Callout;
QT_USE_NAMESPACE
class View: public QGraphicsView
{
Q_OBJECT
public:
View(QWidget *parent = 0);
protected:
void resizeEvent(QResizeEvent *event);
void mouseMoveEvent(QMouseEvent *event);
public slots:
void keepCallout();
void tooltip(QPointF point, bool state);
private:
//要设置项目的文本,您可以将 QString 传递给
//QGraphicsSimpleTextItem 的构造函数
//或者稍后调用 setText() 更改文本。
//要设置文本填充颜色,请调用 setBrush()。
//简单的文本项可以同时具有填充和轮廓;
//setBrush() 将设置文本填充(即文本颜色)
//setPen() 设置将用于绘制文本轮廓的笔。
//(后者可能会很慢,特别是对于复杂的钢笔和具有较长文本内容的项目。)
//如果您只想绘制一条简单的文本行
//应该只调用 setBrush(),并且不设置钢笔;
//QGraphicsSimpleTextItem 的笔默认为 Qt::NoPen。
//QGraphicsSimpleTextItem 使用文本的格式化大小和关联的字
//体来提供 boundingRect()、shape() 和 contains() 的合理实现。
//您可以通过调用 setFont() 来设置字体。
//QGraphicsSimpleText 不显示富文本;
//相反,您可以使用 QGraphicsTextItem,它提供全文控制功能。
QGraphicsSimpleTextItem *m_coordX;
QGraphicsSimpleTextItem *m_coordY;
QChart *m_chart;
Callout *m_tooltip;
QList<Callout *> m_callouts;
};
#endif
view.cpp
#include "view.h"
#include
#include
#include
#include
#include
#include
#include "callout.h"
#include
//QGraphicsView 在可滚动视口中可视化 QGraphicsScene 的内容。
//要创建具有几何项的场景,请参阅 QGraphicsScene 的文档。
//QGraphicsView 是图形视图框架的一部分。
//要可视化场景,首先要构造一个 QGraphicsView 对象
//将要可视化的场景的地址传递给 QGraphicsView 的构造函数。
//或者,您可以稍后调用 setScene() 来设置场景。
//调用 show() 后,视图将默认滚动到场景中心并显示此时可见的所有项目
//例如:
/*
QGraphicsScene scene;
scene.addText("Hello,world");
QGraphicsView view(&scene);
view.show();
*/
//您可以使用滚动条或通过调用 centerOn() 显式滚动到场景中的任何位置。
//通过将一个点传递给 centerOn(),
//QGraphicsView 将滚动其视口以确保该点在视图中居中。
//为滚动到 QGraphicsItem 提供了重载,
//在这种情况下,QGraphicsView 将看到项目的中心在视图中居中。
//如果您只想确保某个区域可见(但不一定居中)
//则可以调用 ensureVisible() 代替。
//QGraphicsView 可用于可视化整个场景,或仅可视化其中的一部分
//可视化区域默认在第一次显示视图时自动检测(通过调用 QGraphicsScene::itemsBoundingRect())
//要自己设置可视化区域矩形,可以调用 setSceneRect()。
//这将适当地调整滚动条的范围。
//请注意,尽管场景支持几乎无限大小,
//但滚动条的范围永远不会超过整数范围(INT_MIN、INT_MAX)
//QGraphicsView 通过调用 render() 来可视化场景。
//默认情况下,通过使用常规 QPainter 和默认渲染提示将项目绘制到视口上。
//要更改在绘制项目时 QGraphicsView 传递给 QPainter 的默认渲染提示
//您可以调用 setRenderHints()。
//默认情况下,QGraphicsView 为视口小部件提供了一个常规的 QWidget。
// /您可以通过调用 viewport() 来访问这个小部件
//也可以通过调用 setViewport() 来替换它。
//要使用 OpenGL 渲染,只需调用 setViewport(new QOpenGLWidget)。
//QGraphicsView 拥有视口小部件的所有权。
//QGraphicsView 支持仿射变换,使用 QTransform。
//您可以将矩阵传递给 setTransform(),也可以调用便利函数之一rotate()、scale()、translate() 或shear()。
//最常见的两种变换是缩放,用于实现缩放和旋转。
//QGraphicsView 在转换过程中保持视图的中心固定。
//由于场景对齐(setAligment()),平移视图不会有视觉影响。
//您可以使用鼠标和键盘与场景中的项目进行交互。
//QGraphicsView 将鼠标和按键事件转化为场景事件,
//(继承 QGraphicsSceneEvent 的事件)
//并转发到可视化的场景中
//最后,它是处理事件并对其做出反应的单个项目
//例如,如果您单击一个可选项目
//该项目通常会让场景知道它已被选中
//并且还会重绘自身以显示一个选择矩形
//同样,如果你点击并拖动鼠标来移动一个可移动的项目
//它是处理鼠标移动并移动自己的项目
//默认情况下启用项目交互,您可以通过调用 setInteractive() 来切换它。
//您还可以通过创建 QGraphicsView 的子类并重新实现鼠标和
//键事件处理程序来提供您自己的自定义场景交互。
//为了简化与视图中的项目进行编程交互的方式
//QGraphicsView 提供了映射函数 mapToScene() 和 mapFromScene(),
//以及项目访问器 items() 和 itemAt()。
//这些函数允许您在视图坐标和场景坐标之间映射点、矩形、多边形和路径
//并使用视图坐标在场景中查找项目。
//QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
//构造一个 QGraphicsView 并将可视化场景设置为场景。
//parent 传递给 QWidget 的构造函数。
View::View(QWidget *parent)
: QGraphicsView(new QGraphicsScene, parent),
m_coordX(0),
m_coordY(0),
m_chart(0),
m_tooltip(0)
{
//dragMode : DragMode
//此属性保存在按下鼠标左键时在场景上拖动鼠标的行为。
//此属性定义了当用户单击场景背景并拖动鼠标
//例如,使用指向手形光标滚动视口内容,或使用橡皮筋选择多个项目)
//时应发生的情况。 默认值 NoDrag 不执行任何操作。
//此行为仅影响未由任何项目处理的鼠标单击。
//您可以通过创建 QGraphicsView 的子类并重新实现 mouseMoveEvent() 来定义自定义行为。
setDragMode(QGraphicsView::NoDrag);
//verticalScrollBarPolicy : Qt::ScrollBarPolicy
//此属性保存垂直滚动条的策略
//Qt::ScrollBarAlwaysOff QAbstractScrollArea 从不显示滚动条。
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//QChart 是一个 QGraphicsWidget,您可以在 QGraphicsScene 中显示它。
//它管理不同类型系列和其他图表相关对象(如图例和轴)的图形表示。
//为了简单地在布局中显示图表,可以使用便利类 QChartView 代替 QChart。
//此外,可以使用 QPolarChart 类将折线、样条曲线、面积和散点序列显示为极坐标图。
// chart
m_chart = new QChart;
m_chart->setMinimumSize(640, 480);
m_chart->setTitle("Hover the line to show callout. Click the line to make it stay");
m_chart->legend()->hide();
//折线图用于将信息显示为由直线连接的一系列数据点。
//创建基本折线图很简单:
/*
QLineSeries* series = new QLineSeries();
series->append(0,6);
series->append(2,4);
...
chart->addSeries(series);
*/
QLineSeries *series = new QLineSeries;
series->append(1, 3);
series->append(4, 5);
series->append(5, 4.5);
series->append(7, 1);
series->append(11, 2);
m_chart->addSeries(series);
QSplineSeries *series2 = new 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);
m_chart->addSeries(series2);
//void QChart::createDefaultAxes()
//根据已添加到图表的系列为图表创建轴。
//之前添加到图表中的任何轴都将被删除。
//注意:必须在所有系列都添加到图表后调用此函数。
//调用此函数后,此函数创建的轴不会自动附加到添加到图表中的任何系列。
//此函数创建的轴不会自动附加到添加到图表中的任何系列。
//默认情况下,没有附加轴的系列将缩放以利用图表的整个绘图区域,
//如果还存在其他具有正确附加轴的系列,则可能会造成混淆。
//如果在图表中添加了多个 QXYSeries 派生系列
//并且没有添加其他类型的系列,则仅创建一对轴。
//如果图表中添加了多个不同类型的系列,则每个系列都有自己的轴对。
//特定于系列的轴稍后可以通过提供系列作为轴()函数调用的参数从图表中获得。
//QPieSeries 不创建任何轴。
//如果 enabled 为 true,则此项将接受悬停事件
//否则,它将忽略它们。 默认情况下,项目不接受悬停事件。
m_chart->createDefaultAxes();
//void QGraphicsItem::setAcceptHoverEvents(bool enabled)
//当当前没有鼠标抓取器项目时
//会传递悬停事件
//当鼠标光标进入一个项目时,当它在项目内移动时
//以及当光标离开一个项目时,它们会被发送。
//悬停事件通常用于在输入项目时突出显示项目
//以及在鼠标光标悬停在项目上时跟踪鼠标光标(相当于 QWidget::mouseTracking)。
//父项在其子项之前接收悬停输入事件,
//并在其子项之后接收离开事件。
//但是,如果光标进入子级,父级不会收到悬停离开事件;
//父级保持“悬停”,直到光标离开其区域,包括其子级区域。
//如果父项处理子事件,当光标经过其子项时
//它会收到悬停移动、拖动移动和放置事件,
//但不会代表其收到悬停进入和悬停离开事件
//也不会收到拖动进入和拖动离开事件 孩子们。
m_chart->setAcceptHoverEvents(true);
setRenderHint(QPainter::Antialiasing);
scene()->addItem(m_chart);
//要设置项目的文本,您可以将 QString 传递给 QGraphicsSimpleTextItem 的构造函数,
//或者稍后调用 setText() 更改文本。
//要设置文本填充颜色,请调用 setBrush()。
//简单的文本项可以同时具有填充和轮廓;
//setBrush() 将设置文本填充(即文本颜色)
//setPen() 设置将用于绘制文本轮廓的笔。
//后者可能会很慢,特别是对于复杂的钢笔和具有较长文本内容的项目。)
//如果您只想绘制一条简单的文本行,您应该只调用 setBrush(),
//并且不设置钢笔; QGraphicsSimpleTextItem 的笔默认为 Qt::NoPen。
//QGraphicsSimpleTextItem 使用文本的格式化大小和关联的字
//体来提供 boundingRect()、shape() 和 contains() 的合理实现。
//您可以通过调用 setFont() 来设置字体。
//QGraphicsSimpleText 不显示富文本;
//相反,您可以使用 QGraphicsTextItem,它提供全文控制功能。
//QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(QGraphicsItem *parent = nullptr)
//构造一个 QGraphicsSimpleTextItem。
//parent 传递给 QGraphicsItem 的构造函数。
m_coordX = new QGraphicsSimpleTextItem(m_chart);
m_coordX->setPos(m_chart->size().width()/2 - 50, m_chart->size().height());
m_coordX->setText("X: ");
m_coordY = new QGraphicsSimpleTextItem(m_chart);
m_coordY->setPos(m_chart->size().width()/2 + 50, m_chart->size().height());
m_coordY->setText("Y: ");
connect(series, &QLineSeries::clicked, this, &View::keepCallout);
connect(series, &QLineSeries::hovered, this, &View::tooltip);
connect(series2, &QSplineSeries::clicked, this, &View::keepCallout);
connect(series2, &QSplineSeries::hovered, this, &View::tooltip);
this->setMouseTracking(true);
}
void View::resizeEvent(QResizeEvent *event)
{
if (scene()) {
scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
m_chart->resize(event->size());
m_coordX->setPos(m_chart->size().width()/2 - 50, m_chart->size().height() - 20);
m_coordY->setPos(m_chart->size().width()/2 + 50, m_chart->size().height() - 20);
const auto callouts = m_callouts;
for (Callout *callout : callouts)
callout->updateGeometry();
}
QGraphicsView::resizeEvent(event);
}
void View::mouseMoveEvent(QMouseEvent *event)
{
m_coordX->setText(QString("X: %1").arg(m_chart->mapToValue(event->pos()).x()));
m_coordY->setText(QString("Y: %1").arg(m_chart->mapToValue(event->pos()).y()));
QGraphicsView::mouseMoveEvent(event);
}
void View::keepCallout()
{
m_callouts.append(m_tooltip);
m_tooltip = new Callout(m_chart);
}
void View::tooltip(QPointF point, bool state)
{
if (m_tooltip == 0)
m_tooltip = new Callout(m_chart);
if (state) {
m_tooltip->setText(QString("X: %1 \nY: %2 ").arg(point.x()).arg(point.y()));
m_tooltip->setAnchor(point);
m_tooltip->setZValue(11);
m_tooltip->updateGeometry();
m_tooltip->show();
} else {
m_tooltip->hide();
}
}
callout.h
#ifndef CALLOUT_H
#define CALLOUT_H
#include
#include
#include
//当 QGraphicsView 收到 QMouseEvent 时,
//它会将其转换为 QGraphicsSceneMouseEvent。
//然后将事件转发到与视图关联的 QGraphicsScene。
//如果事件不是由场景处理的,视图可以使用它,例如,用于 DragMode。
//除了包含事件的项目、场景和屏幕坐标(如 pos()、scenePos() 和 screenPos())
//鼠标事件还包含视图接收到的前一个鼠标事件的坐标。
//这些可以使用 lastPos()、lastScreenPos() 和 lastScenePos() 检索。
QT_BEGIN_NAMESPACE
class QGraphicsSceneMouseEvent;
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
class QChart;
QT_END_NAMESPACE
QT_USE_NAMESPACE
//它为编写您自己的自定义项目提供了一个轻量级的基础。
//这包括通过其事件处理程序定义项目的几何形状、
//、碰撞检测、其绘制实现和项目交互。
//QGraphicsItem 是图形视图框架的一部分
//为方便起见,Qt 为最常见的形状提供了一组标准图形项。 这些是:
//QGraphicsEllipseItem 提供椭圆项
//QGraphicsLineItem 提供一个行项目
//QGraphicsPathItem 提供任意路径项
//QGraphicsPixmapItem 提供了一个像素图项
//QGraphicsPolygonItem 提供多边形项
//QGraphicsRectItem 提供一个矩形项
//QGraphicsSimpleTextItem 提供了一个简单的文本标签项
//QGraphicsTextItem 提供高级文本浏览器项
//项目的所有几何信息都基于其局部坐标系。
//项目的位置 pos() 是唯一不在本地坐标中操作的函数,
//因为它返回父坐标中的位置。
//图形视图坐标系详细描述了坐标系。
//您可以通过调用 setVisible() 设置项目是否应该可见(即,绘制和接受事件)
//隐藏项目也会隐藏其子项。
//同样,您可以通过调用 setEnabled() 启用或禁用项目。
//如果您禁用一个项目,它的所有子项也将被禁用。
//默认情况下,项目既可见又启用
//要切换项目是否被选中,首先通过设置 ItemIsSelectable 标志启用选择,然后调用 setSelected()。
//通常,作为用户交互的结果,选择由场景切换。
//要编写自己的图形项,首先要创建 QGraphicsItem 的子类
//然后从实现它的两个纯虚公共函数开始:
//boundingRect(),它返回对该项绘制的区域的估计
//以及paint(),它实现 实际的绘画。 例如:
/*
class SimpleItem : public QGraphicsItem
{
public:
QRectF boundingRect() const override
{
qreal penWidth = 1;
return QRectF(-19 - penWidth / 2, -10 - penWidth / 2,
20 + penWidth,20 + penWidth);
}
void paint(QPainter *parenter,const QStyleOptionGraphicsItem *option,
QWidget *widget) override
{
painter->drawRoundedRect(-10,-10,20,20,5,5);
}
};
*/
//boundingRect() 函数有许多不同的用途。
//QGraphicsScene 的项索引基于 boundingRect(),
//而 QGraphicsView 使用它来剔除不可见项
//以及确定绘制重叠项时需要重新组合的区域
//此外,QGraphicsItem 的碰撞检测机制使用 boundingRect() 来提供有效的截止。
//collidesWithItem() 中的细粒度碰撞算法基于调用 shape()
//它将项目形状的准确轮廓作为 QPainterPath 返回。
//QGraphicsScene 期望所有项目 boundingRect() 和 shape() 保持不变,除非得到通知。
//如果您想以任何方式更改项目的几何形状
//您必须首先调用 prepareGeometryChange() 以允许 QGraphicsScene 更新其簿记。
//碰撞检测可以通过两种方式完成:
//重新实现 shape() 为您的项目返回准确的形状,并依赖 collidesWithItem() 的默认实现来进行形状-形状相交。
//如果形状复杂,这可能会相当昂贵。
//重新实现 collidesWithItem() 以提供您自己的自定义项目和形状碰撞算法。
//可以调用 contains() 函数来确定项目是否包含点。
//此功能也可以由项目重新实现。
//contains() 的默认行为基于调用 shape()。
//项目可以包含其他项目
//也可以被其他项目包含。 所有项目都可以有一个父项目和一个子项列表。
//除非该项目没有父项,否则其位置在父坐标中(即,父项的本地坐标)。
//父项将它们的位置和变换传播给所有子项。
//转型
//除了基础位置 pos() 之外,QGraphicsItem 还支持投影变换。
//有多种方法可以更改项目的转换。
//对于简单的转换,您可以调用方便的函数 setRotation() 或 setScale(),
//或者您可以将任何转换矩阵传递给 setTransform()。
//于高级转换控制,您还可以通过调用 setTransformations() 选择设置多个组合转换。
//项转换从父项到子项累积
//,因此如果父项和子项都旋转 90 度,
//则子项的总转换将为 180 度。
//同样,如果项目的父项缩放到原始大小的 2 倍,其子项也将是原来大小的两倍
//项目的变换不影响其自身的局部几何;
//所有几何函数(例如 contains()、update() 和所有映射函数)仍然在局部坐标下运行。
//为方便起见,QGraphicsItem 提供了函数sceneTransform(),
//它返回项的总变换矩阵(包括它的位置和所有父项的位置和变换)和scenePos(),它返回它在场景坐标中的位置。
//要重置项目的矩阵,请调用 resetTransform()
//某些转换操作会根据它们的应用顺序产生不同的结果
//例如,如果您缩放一个变换,然后旋转它
//您得到的结果可能与先旋转变换时得到的结果不同。
//是,您在 QGraphicsItem 上设置转换属性的顺序不会影响结果转换;
// /QGraphicsItem 总是以固定的、定义的顺序应用属性:
//应用项目的基本变换 (transform())
//项目的转换列表按顺序应用 (transformations())
//项目相对于其变换原点旋转(rotation()、transformOriginPoint())
//QGraphicsView 调用paint() 函数来绘制项目的内容
//该项目没有自己的背景或默认填充; 项
//项目后面的任何内容都将穿透此功能中未明确绘制的所有区域
//您可以调用 update() 来安排重绘,可选择传递需要重绘的矩形。
//根据项目在视图中是否可见,项目可能会或可能不会重新绘制
//QGraphicsItem 中没有等效于 QWidget::repaint() 的内容。
//项目由视图绘制,从父项开始,然后以升序堆叠顺序绘制子项。
//可以通过调用 setZValue() 设置项目的堆叠顺序
//并通过调用 zValue() 对其进行测试,
//其中低 z 值的项目在具有高 z 值的项目之前绘制
//堆叠顺序适用于同级项目; 父母总是在孩子面前被吸引。
//此示例显示了拖放机器人示例中机器人所有肢体的堆叠顺序。
//躯干是根项目(所有其他项目都是躯干的孩子或后代)
//,所以它首先被绘制
//接下来,绘制头部,因为它是躯干儿童列表中的第一项。
//然后绘制左上臂。
//由于下臂是上臂的子级,因此接下来绘制下臂,
//然后是上臂的下一个兄弟,即右上臂,依此类推。
//对于高级用户,有多种方法可以更改项目的排序方式:。
//您可以在项目上调用 setZValue() 以将其显式堆叠在其他同级项目的顶部或下方。
//项目的默认 Z 值为 0。具有相同 Z 值的项目按插入顺序堆叠。
//您可以设置 ItemStacksBehindParent 标志以将子项堆叠在其父项后面。
//两个同级项的堆叠顺序也计入每个项的子项和后代项。
//因此,如果一项位于另一项之上,则其所有子项也将位于所有其他项的所有子项之上。
//Events
//QGraphicsItem 通过虚函数sceneEvent() 接收来自QGraphicsScene 的事件。
//此函数将最常见的事件分发给一组便捷的事件处理程序:
// /contextMenuEvent() 处理上下文菜单事件
//focusInEvent() 和 focusOutEvent() 处理焦点进出事件
//hoverEnterEvent()、hoverMoveEvent() 和 hoverLeaveEvent()
//处理悬停进入、移动和离开事件
//inputMethodEvent() 处理输入事件,用于辅助功能支持
//keyPressEvent() 和 keyReleaseEvent() 处理按键按下和释放事件
//mousePressEvent()、mouseMoveEvent()、mouseReleaseEvent() 和 mouseDoubleClickEvent()
// 处理鼠标按下、移动、释放、单击和双击事件
//您可以通过安装事件过滤器来过滤任何其他项目的事件。
//此功能与 Qt 的常规事件过滤器(参见 QObject::installEventFilter())不同,
//后者仅适用于 QObject 的子类。
//通过调用 installSceneEventFilter() 将您的项目安装为另一个项目的事件过滤器后
//过滤的事件将由虚拟函数 sceneEventFilter() 接收。
//您可以通过调用 removeSceneEventFilter() 来移除项目事件过滤器。
//Custom Data
//有时,将自定义数据注册到项目(无论是自定义项目还是标准项目)很有用。
//您可以在任何项目上调用 setData() 以使用键值对(键是整数,值是 QVariant)在其中存储数据。
//从项目中获取自定义数据,请调用 data()。
//这个功能完全不受 Qt 本身的影响; 它是为了用户的方便而提供的。
class Callout : public QGraphicsItem
{
public:
Callout(QChart *parent);
void setText(const QString &text);
void setAnchor(QPointF point);
void updateGeometry();
//[pure virtual] QRectF QGraphicsItem::boundingRect() const
//这个纯虚函数将项的外边界定义为一个矩形
//所有绘画都必须限制在项目的边界矩形内
//QGraphicsView 使用它来确定项目是否需要重绘。
//尽管 item 的形状可以是任意的,
//但是 bounding rect 始终是矩形的,并且不受 item 变换的影响。
//如果要更改项目的边界矩形,则必须首先调用 prepareGeometryChange()。
//这会通知场景即将发生变化,以便它可以更新其项目几何索引;
//否则,场景将不知道项目的新几何体
//,并且结果未定义(通常,渲染工件会留在视图中)。
//重新实现这个函数,让 QGraphicsView 确定小部件的哪些部分(如果有)需要重绘。
//注意:对于绘制轮廓/描边的形状,重要的是在边界矩形中包含笔宽度的一半。
//但是,没有必要补偿抗锯齿。
/*
QRectF CircleItem::boundingRect() const
{
qreal penWidth = 1;
return QRect(-radius - penWidth/2,-radius - penWidget / 2,
diameter + penWidth / 2,diameter + penWidget);
}
*/
QRectF boundingRect() const;
//[pure virtual] void QGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr)
//该函数通常由 QGraphicsView 调用,以局部坐标绘制项目的内容。
//在 QGraphicsItem 子类中重新实现此函数以使用 Painter 提供项目的绘制实现。
//option 参数为项目提供样式选项,例如其状态、暴露区域及其详细级别提示。
//小部件参数是可选的。
//如果提供,它指向正在绘制的小部件;
//否则为 0。对于缓存绘画,widget 始终为 0。
/*
void RoundRectItem::paint(QPainter *painter,
const QStyleOptionGraphicsImtem *option,
QWidget *widget)
{
painter->drawRoundedRect(-10,-10,20,20,5,5);
}
*/
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
//画家的画笔默认为 0 宽度,并且它的画笔从绘画设备的调色板
//初始化为 QPalette::Text 画笔。
//画笔被初始化为 QPalette::Window。
//确保将所有绘画限制在 boundingRect() 的边界内以避免渲染伪影(因为 QGraphicsView 不会为您剪辑画家)。
//特别是,当 QPainter 使用指定的 QPen 绘制形状的轮廓时,
//轮廓的一半将绘制在您正在绘制的形状的外部
//一半绘制在内部(例如,笔宽度为 2 个单位时
//您必须绘制轮廓 boundingRect() 内的 1 个单元)。
//QGraphicsItem 不支持使用非零宽度的化妆笔。
//所有绘画都是在本地坐标中完成的。
//注意:除非调用 update(),否则项目必须始终以完全相同的方式重绘自身;
//否则可能会出现视觉伪影。
//换句话说,对paint() 的两次后续调用必须始终产生相同的输出,除非在它们之间调用了update()。
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
private:
QString m_text;
QRectF m_textRect;
QRectF m_rect;
QPointF m_anchor;
QFont m_font;
QChart *m_chart;
};
#endif // CALLOUT_H
#include "callout.h"
#include
#include
#include
#include
#include
Callout::Callout(QChart *chart):
QGraphicsItem(chart),
m_chart(chart)
{
}
//矩形通常表示为左上角和大小。
//QRectF 的大小(宽度和高度)始终等同于构成其渲染基础的数学矩形。
//QRectF 可以用一组左、上、宽和高坐标构造,或者从 QPointF 和 QSizeF 构造。
//以下代码创建两个相同的矩形。
/*
QRectF r1(100.0, 200.1, 11.2, 16.3);
QRectF r2(QPointF(100.0, 200.1), QSizeF(11.2, 16.3));
*/
//还有第三个构造函数从 QRect 创建 QRectF
//以及相应的 toRect() 函数
//该函数根据此矩形的值返回 QRect 对象
//注意,返回的矩形中的坐标四舍五入为最接近的整数)。
//QRectF 类提供了一组函数,这些函数返回各种矩形坐标,并启用对这些坐标的操作。
//QRectF 还提供了相对于各种坐标移动矩形的功能。
//此外,还有一个 moveTo() 函数可以移动矩形,将其左上角留在给定的坐标处。
//或者, translate() 函数将矩形相对于当前位置移动给定的偏移量,
//而 translate() 函数返回此矩形的平移副本。
//size() 函数将矩形的尺寸作为 QSizeF 返回。
//也可以使用 width() 和 height() 函数分别检索尺寸。
//要操作尺寸,请使用 setSize()、setWidth() 或 setHeight() 函数。
//或者,可以通过应用设置矩形坐标的函数之一来更改大小,例如 setBottom() 或 setRight()。
//contains() 函数告诉给定的点是否在矩形内
//如果该矩形与给定的矩形相交,
//则 intersects() 函数返回真(否则为假)。
//QRectF 类还提供了返回相交矩形的 intersected() 函数
//以及返回包围给定矩形的矩形的 United() 函数和这个:
//如果矩形的宽度或高度小于或等于 0,
//则 isEmpty() 函数返回 true。
//请注意,空矩形无效:如果宽度和高度都大于 0,则 isValid() 函数返回 true
//另一方面,空矩形 (isNull() == true) 的宽度和高度都设置为 0。
//Rendering
//使用抗锯齿画家时,QRectF 的边界线将在数学矩形边界线的两侧对称呈现。
//但是当使用别名画家(默认)时,其他规则适用。
//然后,当用一像素宽的笔进行渲染时,
//,QRectF 的边界线将被渲染到数学矩形边界线的右侧和下方。
//当使用两个像素宽的笔进行渲染时,
//边界线将被数学矩形在中间分开。
//每当将笔设置为偶数像素时都会出现这种情况
//而使用具有奇数像素的笔进行渲染时,
//备用像素将渲染到数学矩形的右侧和下方,就像在一个像素的情况下一样。
//Coordinates
//QRectF 类提供了一组函数,这些函数返回各种矩形坐标,并启用对这些坐标的操作。
//QRectF 还提供了相对于各种坐标移动矩形的功能。
例如:bottom()、setBottom() 和 moveBottom() 函数:
//bottom() 返回矩形底边的 y 坐标,
//setBottom() 将矩形的底边设置为给定的 y 坐标
//它可能会改变 高度,但永远不会改变矩形的顶边
//并且 moveBottom() 垂直移动整个矩形
//使矩形的底边保持在给定的 y 坐标处,其大小保持不变。
//还可以使用 adjust() 函数为这个矩形的坐标添加偏移量
//以及使用 adjust() 函数根据原始矩形的调整检索一个新矩形。
//如果宽度和高度中的任何一个为负,则使用 normalized() 函数来检索角交换的矩形。
//此外,QRectF 提供了 getCoords() 函数,用于提取矩形的左上
//角和右下角的位置,以及 getRect() 函数,用于提取矩形的左上角、宽度和高度。
//使用 setCoords() 和 setRect() 函数一次性操作矩形的坐标和尺寸。
//一个点由 x 坐标和 y 坐标指定,可以使用 x() 和 y() 函数访问它们。
//为了精确起见,使用有限浮点数指定点的坐标。
//如果 x 和 y 都设置为 0.0,则 isNull() 函数返回 true。
//可以使用 setX() 和 setY() 函数设置(或更改)坐标,
//或者使用返回坐标引用的 rx() 和 ry() 函数(允许直接操作)。
//给定一个点 p,以下语句都是等价的:
/*
QPointF p;
p.setX(p.x() + 1.0);
p += QPointF(1.0,0.0);
p.rx()++;
*/
//QPointF 对象也可以用作向量:
//加法和减法的定义与向量相同(每个分量单独相加)。
//QPointF 对象也可以除以或乘以 int 或 qreal。
//此外,QPointF 类提供了一个将 QPoint 对象转换为 QPointF 对象的构造函数,
//以及相应的 toPoint() 函数,该函数返回该点的 QPoint 副本。
//最后,QPointF 对象可以进行流式传输和比较。
QRectF Callout::boundingRect() const
{
//QPointF QGraphicsItem::mapFromParent(const QPointF &point) const
//将位于此项的父项坐标系中的点点映射到该项的坐标系,并返回映射的坐标
//QPointF QChart::mapToPosition(const QPointF &value, QAbstractSeries *series = nullptr)
//返回与 series 指定的系列中的值对应的图表上的位置。
QPointF anchor = mapFromParent(m_chart->mapToPosition(m_anchor));
QRectF rect;
rect.setLeft(qMin(m_rect.left(), anchor.x()));
rect.setRight(qMax(m_rect.right(), anchor.x()));
rect.setTop(qMin(m_rect.top(), anchor.y()));
rect.setBottom(qMax(m_rect.bottom(), anchor.y()));
return rect;
}
//QStyleOptionGraphicsItem 类用于描述绘制 QGraphicsItem 所需的参数。
//画家路径是由许多图形构建块组成的对象
//例如矩形、椭圆、直线和曲线。
//构建块可以连接到封闭的子路径中,例如作为矩形或椭圆形。
//封闭路径具有重合的起点和终点。
//或者它们可以作为未封闭的子路径独立存在,例如直线和曲线。
//QPainterPath 对象可用于填充、勾勒轮廓和剪切。
//要为给定的画家路径生成可填充的轮廓,
//请使用 QPainterPathStroker 类。
//绘制路径相对于普通绘制操作的主要优点是复杂的形状只需要创建一次;
//那么它们可以通过调用 QPainter::drawPath() 函数多次绘制。
//QPainterPath 提供了一组函数,可用于获取有关路径及其元素的信息。
//此外,可以使用 toReversed() 函数反转元素的顺序。
//还有几个函数可以将此画家路径对象转换为多边形表示。
//Composing a QPainterPath
//QPainterPath 对象可以构造为空路径,
//具有给定的起点,或作为另一个 QPainterPath 对象的副本
//创建后,可以使用 lineTo()、arcTo()、cubicTo() 和 quadTo() 函数将直线和曲线添加到路径中。
//直线和曲线从 currentPosition() 延伸到作为参数传递的位置。
//QPainterPath 对象的 currentPosition() 始终是添加的最后一个子路径的结束位置(或初始起点)。
//使用 moveTo() 函数移动 currentPosition() 而不添加组件。
//moveTo() 函数隐式地启动一个新的子路径,并关闭前一个。
//启动新子路径的另一种方法是调用 closeSubpath() 函数
//该函数通过从 currentPosition() 向路径的起始位置添加一行来关闭当前路径
//请注意,新路径将 (0, 0) 作为其初始 currentPosition()。
//QPainterPath 类还提供了几个方便的函数来向画家路径添加封闭的子路径:
//addEllipse()、addPath()、addRect()、addRegion() 和 addText()。
//addPolygon() 函数添加一个未闭合的子路径。
//其实这些函数都是moveTo()、lineTo()和cubicTo()操作的集合。
//此外,可以使用 connectPath() 函数将路径添加到当前路径。
//但请注意,此函数将通过添加一行将当前路径的最后一个元素连接到给定路径的第一个元素。
//下面的代码片段展示了如何使用 QPainterPath 对象:
/*
QPainterPath path;
path.addRect(20,20,60,60);
path.moveTo(0,0);
path.cubicTo(99,0,50,50,99,99);
path.cubicTo(0,99,50,50,0,0);
QPainter painter(this);
painter.fillRect(0,0,100,100,Qt::white);
painter.setPen(QPen(QColor(79,106,25),1,Qt::SolidLine,
Qt::FlatCap,Qt::MiterJoin));
painter.setBrush(QColor(122,163,39));
painter.drawPath(path);
*/
//绘制路径在构建时最初是空的。
//我们首先添加一个矩形
//它是一个封闭的子路径。
//然后我们添加两条贝塞尔曲线,
//它们一起形成一个封闭的子路径,即使它们不是单独封闭的。
//最后我们画出整个路径。
//使用默认填充规则 Qt::OddEvenFill 填充路径。 Qt 提供了两种填充路径的方法:
//有关规则的定义,请参阅 Qt::FillRule 文档。
//可以使用 fillRule() 函数检索画家路径当前设置的填充规则,
//并使用 setFillRule() 函数进行更改。
//指定应使用哪种方法来填充路径和多边形。
//Qt::OddEvenFill
//指定使用奇偶填充规则填充区域。
//有了这个规则,我们使用以下方法确定一个点是否在形状内部。
//从点到形状外的位置画一条水平线,并计算交叉点的数量。
//如果交点数为奇数,则该点在形状内。 此模式为默认模式。
//Qt::WindingFill
//指定使用非零缠绕规则填充区域。
//有了这个规则,我们使用以下方法确定一个点是否在形状内部。
//绘制一条从该点到形状外部位置的水平线。
//确定每个交点处的线的方向是向上还是向下。
//通过对每个交叉点的方向求和来确定绕组数。
//如果数字不为零,则该点在形状内。
//这种填充模式在大多数情况下也可以被视为闭合形状的交集。
//void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
//使用由 c1 和 c2 指定的控制点在当前位置和给定端点之间添加三次贝塞尔曲线。
//添加曲线后,更新当前位置为曲线终点
/*
QLinearGradient myGradient;
QPen myPen;
QPainterPath myPath;
myPath.cubicTo(c1,c2,endPoint);
QPainter painter(this);
painter.setBrush(myGradient);
painter.setPen(myPen);
painter.drawPath(myPath);
*/
//void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X, qreal c2Y, qreal endPointX, qreal endPointY)
//在当前位置和端点 (endPoint, endPoint) 之间添加三次贝塞尔曲线,
//控制点由 (c1X, c1Y) 和 (c2X, c2Y) 指定。
//QPainterPath 信息
//QPainterPath 类提供了一组函数,这些函数返回有关路径及其元素的信息。
//currentPosition() 函数返回添加的最后一个子路径的终点(或初始起点)。
//elementAt() 函数可以用来检索各种子路径元素,
//元素的数量可以使用 elementCount() 函数来检索,
//isEmpty() 函数会告诉这个 QPainterPath 对象是否包含任何元素。
//controlPointRect() 函数返回包含此路径中所有点和控制点的矩形
//此函数的计算速度明显快于精确 boundingRect(),后者以浮点精度返回此画家路径的边界矩形。
//最后,QPainterPath 提供了 contains() 函数可用于确定给定点或矩形是否在路径内,
//以及 intersects() 函数确定给定矩形内的任何点是否也在此路径内。
//QPainterPath Conversion
//出于兼容性原因,可能需要简化画家路径的表示:
//QPainterPath 提供了 toFillPolygon()、toFillPolygons() 和 toSubpathPolygons() 函数,
//,可将画家路径转换为多边形。
//toFillPolygon() 将画家路径作为单个多边形返回
//而后两个函数返回一个多边形列表。
//提供 toFillPolygons() 和 toSubpathPolygons() 函数是因为绘制
//多个小多边形通常比绘制一个大多边形更快
//即使绘制的点总数相同。
//两者之间的区别在于它们返回的多边形数量:
//toSubpathPolygons() 为每个子路径创建一个多边形,
//而不管相交的子路径(即重叠的边界矩形)如何
//而 toFillPolygons() 函数只为重叠的子路径创建一个多边形。
//toFillPolygon() 和 toFillPolygons() 函数首先将所有子路径转换为多边形
//然后使用倒带技术确保可以使用正确的填充规则填充重叠的子路径。
//请注意,倒带会在多边形中插入额外的线,因此填充多边形的轮廓与路径的轮廓不匹配。
//出于性能原因,成员函数很少
//并且对成员变量的访问是直接的(即,使用 . 或 -> 运算符)
//这使得结构易于使用,并强调这些只是样式函数使用的参数。
//有关演示如何使用样式选项的示例,请参阅样式示例。
void Callout::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QPainterPath path;
//void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize)
//将给定的带有圆角的矩形 rect 添加到路径中。
//xRadius 和 yRadius 参数指定定义圆角矩形角的椭圆的半径。
//当 mode 为 Qt::RelativeSize 时,xRadius 和 yRadius 分别指定
//为矩形宽度和高度的一半的百分比,应在 0.0 到 100.0 的范围内。
path.addRoundedRect(m_rect, 5, 5);
//将位于此项的父项坐标系中的点点映射到该项的坐标系,并返回映射的坐标。
//QPointF QChart::mapToPosition(const QPointF &value, QAbstractSeries *series = nullptr)
//返回与 series 指定的系列中的值对应的图表上的位置。
QPointF anchor = mapFromParent(m_chart->mapToPosition(m_anchor));
if (!m_rect.contains(anchor) && !m_anchor.isNull()) {
QPointF point1, point2;
// 建立锚点相对于 m_rect 的位置
bool above = anchor.y() <= m_rect.top();
bool aboveCenter = anchor.y() > m_rect.top() && anchor.y() <= m_rect.center().y();
bool belowCenter = anchor.y() > m_rect.center().y() && anchor.y() <= m_rect.bottom();
bool below = anchor.y() > m_rect.bottom();
bool onLeft = anchor.x() <= m_rect.left();
bool leftOfCenter = anchor.x() > m_rect.left() && anchor.x() <= m_rect.center().x();
bool rightOfCenter = anchor.x() > m_rect.center().x() && anchor.x() <= m_rect.right();
bool onRight = anchor.x() > m_rect.right();
// get the nearest m_rect corner.
qreal x = (onRight + rightOfCenter) * m_rect.width();
qreal y = (below + belowCenter) * m_rect.height();
bool cornerCase = (above && onLeft) || (above && onRight) || (below && onLeft) || (below && onRight);
bool vertical = qAbs(anchor.x() - x) > qAbs(anchor.y() - y);
qreal x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * !vertical * (onLeft * 10 - onRight * 20);
qreal y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20);;
point1.setX(x1);
point1.setY(y1);
qreal x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * !vertical * (onLeft * 20 - onRight * 10);;
qreal 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(m_textRect, m_text);
}
void Callout::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(true);
}
void Callout::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton){
setPos(mapToParent(event->pos() - event->buttonDownPos(Qt::LeftButton)));
event->setAccepted(true);
} else {
event->setAccepted(false);
}
}
void Callout::setText(const QString &text)
{
m_text = text;
QFontMetrics metrics(m_font);
m_textRect = metrics.boundingRect(QRect(0, 0, 150, 150), Qt::AlignLeft, m_text);
m_textRect.translate(5, 5);
prepareGeometryChange();
m_rect = m_textRect.adjusted(-5, -5, 5, 5);
}
void Callout::setAnchor(QPointF point)
{
m_anchor = point;
}
void Callout::updateGeometry()
{
prepareGeometryChange();
setPos(m_chart->mapToPosition(m_anchor) + QPoint(10, -50));
}