scatterpresenter.cpp
166 lines
| 5.6 KiB
| text/x-c
|
CppLexer
Tero Ahola
|
r194 | #include "scatterpresenter_p.h" | ||
Tero Ahola
|
r158 | #include "qscatterseries.h" | ||
Tero Ahola
|
r459 | #include "scatterseries_p.h" | ||
Tero Ahola
|
r375 | #include "chartpresenter_p.h" | ||
Tero Ahola
|
r158 | #include <QPen> | ||
#include <QPainter> | ||||
#include <QGraphicsScene> | ||||
Tero Ahola
|
r316 | #include <QGraphicsSceneMouseEvent> | ||
Tero Ahola
|
r346 | #include <QGraphicsDropShadowEffect> | ||
Tero Ahola
|
r158 | #include <QDebug> | ||
Tero Ahola
|
r278 | #include <QTime> | ||
Tero Ahola
|
r158 | |||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
ScatterPresenter::ScatterPresenter(QScatterSeries *series, QGraphicsObject *parent) : | ||||
ChartItem(parent), | ||||
Michal Klocek
|
r439 | m_minX(0), | ||
m_maxX(0), | ||||
m_minY(0), | ||||
m_maxY(0), | ||||
Tero Ahola
|
r158 | m_series(series), | ||
Tero Ahola
|
r447 | m_clippingRect() | ||
Tero Ahola
|
r158 | { | ||
Tero Ahola
|
r459 | Q_ASSERT(parent); | ||
Q_ASSERT(series); | ||||
Tero Ahola
|
r346 | |||
Tero Ahola
|
r459 | m_clippingRect = parent->boundingRect(); | ||
connect(series->d, SIGNAL(changed()), this, SLOT(handleModelChanged())); | ||||
connect(this, SIGNAL(clicked(QPointF)), series, SIGNAL(clicked(QPointF))); | ||||
Tero Ahola
|
r375 | setZValue(ChartPresenter::ScatterSeriesZValue); | ||
// TODO: how to draw a drop shadow? | ||||
// QGraphicsDropShadowEffect *dropShadow = new QGraphicsDropShadowEffect(); | ||||
// dropShadow->setOffset(2.0); | ||||
// dropShadow->setBlurRadius(2.0); | ||||
// setGraphicsEffect(dropShadow); | ||||
Tero Ahola
|
r158 | } | ||
Michal Klocek
|
r439 | void ScatterPresenter::handleDomainChanged(qreal minX, qreal maxX, qreal minY, qreal maxY) | ||
Tero Ahola
|
r158 | { | ||
Tero Ahola
|
r447 | m_minX = minX; | ||
m_maxX = maxX; | ||||
m_minY = minY; | ||||
m_maxY = maxY; | ||||
Tero Ahola
|
r158 | changeGeometry(); | ||
} | ||||
void ScatterPresenter::handleGeometryChanged(const QRectF& rect) | ||||
{ | ||||
Tero Ahola
|
r447 | m_clippingRect = rect.translated(-rect.topLeft()); | ||
Tero Ahola
|
r158 | changeGeometry(); | ||
Tero Ahola
|
r394 | setPos(rect.topLeft()); | ||
Tero Ahola
|
r158 | } | ||
void ScatterPresenter::handleModelChanged() | ||||
{ | ||||
// TODO: more fine grained modelChanged signaling | ||||
changeGeometry(); | ||||
} | ||||
void ScatterPresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/) | ||||
{ | ||||
Tero Ahola
|
r278 | painter->save(); | ||
Tero Ahola
|
r447 | painter->setClipRect(m_clippingRect); | ||
Tero Ahola
|
r278 | |||
Tero Ahola
|
r375 | // TODO: how to draw a drop shadow? | ||
// Now using a custom implementation for drop shadow instead of QGraphicsDropShadowEffect. | ||||
// It seems QGraphicsDropShadowEffect is quite heavy, at least on windows without open gl. | ||||
QPen dropShadowPen(QColor(0, 0, 0, 70)); | ||||
dropShadowPen.setWidth(3); | ||||
painter->setPen(dropShadowPen); | ||||
painter->setBrush(dropShadowPen.color()); | ||||
// painter->setRenderHint(QPainter::Antialiasing); | ||||
painter->drawPath(m_path.translated(2, 2)); | ||||
Tero Ahola
|
r278 | // Paint the shape | ||
Tero Ahola
|
r316 | // The custom settings in series override those defined by the theme | ||
Tero Ahola
|
r278 | QPen pen = m_markerPen; | ||
Tero Ahola
|
r358 | if (m_series->pen().color().isValid()) | ||
pen = m_series->pen(); | ||||
Tero Ahola
|
r375 | painter->setPen(pen); | ||
Tero Ahola
|
r358 | if (m_series->brush().color().isValid()) | ||
painter->setBrush(m_series->brush()); | ||||
Tero Ahola
|
r261 | else | ||
Tero Ahola
|
r195 | painter->setBrush(m_markerBrush); | ||
Tero Ahola
|
r375 | |||
// If either pen or brush is opaque, we need to draw the polygons one-by-one | ||||
if (painter->pen().color().alpha() < 255 || painter->brush().color().alpha() < 255) { | ||||
foreach (QPolygonF pol, m_path.toSubpathPolygons()) | ||||
painter->drawPolygon(pol); | ||||
} else { | ||||
painter->drawPath(m_path); | ||||
} | ||||
Tero Ahola
|
r316 | |||
Tero Ahola
|
r278 | painter->restore(); | ||
Tero Ahola
|
r158 | } | ||
Tero Ahola
|
r300 | void ScatterPresenter::mousePressEvent(QGraphicsSceneMouseEvent *event) | ||
{ | ||||
Tero Ahola
|
r394 | // Empty implementation to grab mouse release events for this item | ||
Q_UNUSED(event) | ||||
} | ||||
Tero Ahola
|
r316 | |||
Tero Ahola
|
r394 | void ScatterPresenter::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) | ||
{ | ||||
QPointF clickedPoint( | ||||
Tero Ahola
|
r447 | m_minX + (event->lastPos().x() / m_clippingRect.width()) * (m_maxX-m_minX), | ||
m_maxY - (event->lastPos().y() / m_clippingRect.height()) * (m_maxY-m_minY)); | ||||
Tero Ahola
|
r394 | emit clicked(clickedPoint); | ||
Tero Ahola
|
r300 | } | ||
Tero Ahola
|
r158 | void ScatterPresenter::changeGeometry() | ||
{ | ||||
Tero Ahola
|
r447 | if (m_clippingRect.isValid()) { | ||
Tero Ahola
|
r158 | prepareGeometryChange(); | ||
Tero Ahola
|
r447 | qreal scalex = m_clippingRect.width() / (m_maxX - m_minX); | ||
qreal scaley = m_clippingRect.height() / (m_maxY - m_minY); | ||||
Tero Ahola
|
r158 | |||
Tero Ahola
|
r358 | int shape = m_series->shape(); | ||
Tero Ahola
|
r316 | m_path = QPainterPath(); | ||
Tero Ahola
|
r375 | m_path.setFillRule(Qt::WindingFill); | ||
Tero Ahola
|
r397 | const qreal size = m_series->size(); | ||
Tero Ahola
|
r316 | |||
Tero Ahola
|
r158 | foreach (QPointF point, m_series->data()) { | ||
Tero Ahola
|
r316 | // Convert relative coordinates to absolute pixel coordinates that can be used for drawing | ||
Michal Klocek
|
r439 | qreal x = point.x() * scalex - m_minX * scalex - size / 2; | ||
Tero Ahola
|
r447 | qreal y = m_clippingRect.height() - point.y() * scaley + m_minY * scaley - size / 2; | ||
Tero Ahola
|
r316 | |||
Tero Ahola
|
r394 | if (x < scene()->width() && y < scene()->height()) { | ||
Tero Ahola
|
r316 | switch (shape) { | ||
case QScatterSeries::MarkerShapeDefault: | ||||
// Fallthrough, defaults to circle | ||||
case QScatterSeries::MarkerShapeCircle: | ||||
Tero Ahola
|
r394 | m_path.addEllipse(x, y, size, size); | ||
Tero Ahola
|
r316 | break; | ||
case QScatterSeries::MarkerShapeRectangle: | ||||
Tero Ahola
|
r394 | m_path.addRect(x, y, size, size); | ||
Tero Ahola
|
r316 | break; | ||
Tero Ahola
|
r459 | case QScatterSeries::MarkerShapeRoundedRectangle: | ||
m_path.addRoundedRect(x, y, size, size, size / 4.0, size / 4.0); | ||||
break; | ||||
Tero Ahola
|
r316 | case QScatterSeries::MarkerShapeTiltedRectangle: { | ||
Tero Ahola
|
r358 | // TODO: tilt the rectangle | ||
Tero Ahola
|
r394 | m_path.addRect(x, y, size, size); | ||
Tero Ahola
|
r316 | break; | ||
} | ||||
Tero Ahola
|
r459 | case QScatterSeries::MarkerShapeTriangle: { | ||
QPolygonF polygon; | ||||
polygon << QPointF(0.0, -size) << QPointF(size / 2.0, 0.0) << QPointF(-size / 2, 0.0); | ||||
// TODO: the position is not exactly right... | ||||
m_path.addPolygon(polygon.translated(x + size / 2.0, y + size)); | ||||
break; | ||||
} | ||||
Tero Ahola
|
r316 | default: | ||
// TODO: implement the rest of the shapes | ||||
Q_ASSERT(false); | ||||
break; | ||||
} | ||||
} | ||||
Tero Ahola
|
r158 | } | ||
} | ||||
} | ||||
Tero Ahola
|
r194 | #include "moc_scatterpresenter_p.cpp" | ||
Tero Ahola
|
r158 | |||
QTCOMMERCIALCHART_END_NAMESPACE | ||||