PDA

View Full Version : QChart + QDateTiemAxis, mapTo and mapFrom



mustermann.klaus@gmx.de
22nd November 2017, 19:37
Hello Everybody,

I'd like to add some describing text to a given QPointF on a chart. I really spent a lot of time trying all of the mapToX's and mapFromX's to map coordinates.
Here's a short wrap up about what I'm doing:




// create Chart
chart = new QChart();
chart->createDefaultAxes();

// costumize chart
dateTimeAxis = new QDateTimeAxis;

categoryAxis = new QBarCategoryAxis();
categoryAxis->append(categoriesList);

chart->addAxis(dateTimeAxis,Qt::AlignBottom);
chart->addAxis(categoryAxis, Qt::AlignLeft);

// hide casual axes
chart->axisX()->hide();
chart->axisY()->hide();

// use QChartView
myChartView = new QChartView(chart);

// promote to QMainWindow
layout->addWidget(myChartView);

myWidget = new QWidget();
myWidget->setLayout(layout);
setCentralWidget(myWidget);

///////////////////

// generate some life
lowerSeries = new QLineSeries();
upperSeries = new QLineSeries();
area = new QAreaSeries(upperSeries, lowerSeries);

area->attachAxis(dateTimeAxis);
area->attachAxis(chart->axisY());

p1 = QPointF(QDateTime::currentTime.toMSecsSinceEpoch() , 1);
p3 = QPointF(QDateTime::currentTime.toMSecsSinceEpoch() , 5);
p2 = QPointF(QDateTime::currentTime.addSecs(rand_N_ofSe c).toMSecsSinceEpoch(), 1);
p4 = QPointF(QDateTime::currentTime.addSecs(rand_N_ofSe c).toMSecsSinceEpoch(), 5);

lowerSeries << p1 << p2;
upperSeries << p3 << p4;


// now the problem:
tag = new QGraphicsSimpleTextItem(chart);
tag->setText("any text to describe the point");

// how toset the location of the tag properly (near the point)?




i.e. the point is at (30,60) I'd like to set the tag on (31,61)
a typical QPointF on the chart would look like



p1 = QPointF(QDateTime::currentTime.toMSecsSinceEpoch() , 3);
qDebug() << "p:" << p;

p: QPointF(1.51138e+12, 3) // strange numbers due to toMSecSinceEpoche()


Thanks in advance, Lars

d_stranz
23rd November 2017, 01:56
What's wrong with "tag->setPos(1.51138e+12, 3);" ? Those are the world coordinates of your axes. And if that doesn't work, then use the QPointF returned by mapToValue(), using the world coordinates and the series as arguments to the method.

mustermann.klaus@gmx.de
23rd November 2017, 09:42
Hey d_stranz,
very glad to hearing from you. One of your replies brought me here.

That is exactely my question. No, unfortunately it doesn't work like this. To me it seems that p=(1.51138e+12, 3) are the "inside-chart-coordinates". I can obtain them using mapFromScene(). I tried a little bit around and I approach the point doing tag->setPos(400, 300); Now: how to map from (1.51138e+12, 3) to (400, 300). I didn't find a way after all.

The "why" is more than clear due to http://www.qtcentre.org/threads/61459-MapToItem-MapToScene?highlight=d_stranz

cheers, Lars

mustermann.klaus@gmx.de
23rd November 2017, 11:44
Hey d_stranz,
very glad to hearing from you. One of your replies brought me here.

That is exactely my question. No, unfortunately it doesn't work like this. To me it seems that p=(1.51138e+12, 3) are the "inside-chart-coordinates". I can obtain them using mapFromScene(). I tried a little bit around and I approach the point doing tag->setPos(400, 300); Now: how to map from (1.51138e+12, 3) to (400, 300). I didn't find a way after all.

The "why" is more than clear due to http://www.qtcentre.org/threads/61459-MapToItem-MapToScene?highlight=d_stranz

cheers, Lars

Added after 1 55 minutes:

Hey d_stranz,

I fixed the code a littel so you perhaps can try out.




// PRO-File
QT += core gui \
widgets \
charts
TEMPLATE = app
SOURCES += main.cpp\


// cpp-file
#include <QApplication>
#include <QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QLegend>
#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QDateTimeAxis>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
#include <QtCharts/QAreaSeries>
#include <QtWidgets/QGraphicsView>
#include <QtCharts/QChartGlobal>
#include <QTime>
#include <QDebug>
#include <QHBoxLayout>

QT_CHARTS_USE_NAMESPACE

int main(int argc, char *argv[])
{

QApplication a(argc, argv);


// obtain time first
QDateTime currentTime = QDateTime::currentDateTime();

// create Chart
QChart *chart = new QChart();
chart->legend()->hide();
chart->setMinimumSize(640, 480);
chart->createDefaultAxes();


// costumize chart a little
QDateTimeAxis *dateTimeAxis = new QDateTimeAxis;
dateTimeAxis->setTickCount(7);
dateTimeAxis->setFormat("hh:mm");
dateTimeAxis->setTitleText("Time");
dateTimeAxis->setMin(currentTime.addSecs(-3600));
dateTimeAxis->setMax(currentTime.addSecs( 3600));


// config axes
QStringList categories;
categories << "Paris" << "London" << "Berlin" << "Madrid";

QBarCategoryAxis *categoryAxis = new QBarCategoryAxis();
categoryAxis->append(categories);

chart->addAxis(dateTimeAxis,Qt::AlignBottom);
chart->addAxis(categoryAxis, Qt::AlignLeft);

chart->axisY()->setRange(0, categories.length());


// use QChartView
QChartView *myChartView = new QChartView(chart);

// promote to QMainWindow
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(myChartView);

QWidget *window = new QWidget;
window->setLayout(layout);

QMainWindow *mainwindow = new QMainWindow();
mainwindow->setCentralWidget(window);

///////////////////

// add some life
QLineSeries *lowerSeries = new QLineSeries();
QLineSeries *upperSeries = new QLineSeries();
QAreaSeries *area = new QAreaSeries(upperSeries, lowerSeries);
area->setBrush(QColor(100,100,100,100));
chart->addSeries(area);

area->attachAxis(dateTimeAxis);
area->attachAxis(chart->axisY());


QPointF p1 = QPointF(currentTime.toMSecsSinceEpoch(), 1);
QPointF p3 = QPointF(currentTime.toMSecsSinceEpoch(), 2);
QPointF p2 = QPointF(currentTime.addSecs(2400).toMSecsSinceEpoc h(), 1);
QPointF p4 = QPointF(currentTime.addSecs(2400).toMSecsSinceEpoc h(), 2);

lowerSeries->append(p1);
lowerSeries->append(p2);
upperSeries->append(p3);
upperSeries->append(p4);


// now the problem:
QGraphicsSimpleTextItem *tag1 = new QGraphicsSimpleTextItem(chart);
QGraphicsSimpleTextItem *tag2 = new QGraphicsSimpleTextItem(chart);
QString x, y;
QPointF position;


// first try
position = chart->mapToPosition(p1, lowerSeries);

x = x.setNum(position.x());
y = y.setNum(position.y());

tag1->setText(x + " " + y);
tag1->setPos(position);


// approach randomly
position = QPointF(300,200);
x = x.setNum(position.x());
y = y.setNum(position.y());

tag2->setText(x + " " + y);
tag2->setPos(position);




mainwindow->show();
return a.exec();
}





Should work out of the box.

As you, or of course anybody else, might figure out this is supposed to be something like a timeline. I'd like to label the shown elements. If you (or anybody else) know a better way, the input would be highly appreciatet. QAreaSeries unfortunately has no setText(). I really struggled a lot.

Regards, Lars

d_stranz
23rd November 2017, 21:19
OK, here it is:



QPointF position = tag1->mapFromParent( chart->mapToPosition( p1 ) );
tag1->setPos( position );


Note that when you resize the window, the label doesn't move because it has been put into a fixed pixel position on the chart. You will need to implement resizeEvent() for the view and in that method you must recalculate the positions and move all the labels.

Also note that the origin (0,0) for the text is the upper left corner of the text's bounding rect. If you want the text to be closer to the actual series point, you'll need to adjust this. For example, this roughly centers the text on the point:



QPointF position = tag1->mapFromParent( chart->mapToPosition( p1 ) );
QRectF tagRect = tag1->boundingRect();
position.setX( position.x() - tagRect.width() / 2 );
position.setY( position.y() - tagRect.height() / 2 );
tag1->setPos( position );

mustermann.klaus@gmx.de
24th November 2017, 00:02
Oh Yeahhhhh,

that is pretty much what I needed. Thanks a million d_stranz. I am aware of the resize problem. Now I am at that stage to take care about that. Also thank you for the 2nd snippet. Very useful.
Most likely I'll use qlabel anyway since this can carry a pixmap.

cheers, Lars