前回QCustomPlotがいい感じでグラフ表示の基本を書いてみたので
次はマウスホバー+グラフ値の表示方法について書いてみる。
マウスホバーはQCPItemTracerをつかう。
QCustomPlotは横軸のことをkeyAxis, 縦軸をvalueAxisと呼ぶがQCPMouseTrace::setGraphKey(int key)でkeyを
更新すると、対応するvalue値を保持して、指定したindicatorでグラフ内の対象箇所を示してくれる機能がある。
でも一点難点があるのはQCPGraphにだけ対応していてQCPBarsには対応してない点。
なので前回あげたヒストグラムには適応できない。
というわけで、ちょっと強引にQCPItemTracerにsetBars()とsetBarsKey()を追加してメンバー変数にQCPBarsを持たせてみる。
void setBars(QCPBars *bars);
void setBarsKey(double key);
QCPBars *mBars;
void QCPItemTracer::setBars(QCPBars *bars){
if(bars)
{
if(bars->parentPlot() == mParentPlot){
position->setType(QCPItemPosition::ptPlotCoords);
position->setAxes(bars->keyAxis(), bars->valueAxis());
mBars = bars;
updatePosition();
} else
qDebug() << Q_FUNC_INFO << "bars isn't in same QCustomPlot instance as this item";
} else
{
mBars = 0;
}
}
void QCPItemTracer::setBarsKey(double key){
setGraphKey(key);
}
// QCPItemTracer::updatePosition()の末尾に以下を追加
else if(mBars){
if(mBars->data()->size() > 1){
QCPBarDataMap::const_iterator first = mBars->data()->constBegin();
QCPBarDataMap::const_iterator last = mBars->data()->constEnd()-1;
if (mGraphKey < first.key())
position->setCoords(first.key(), first.value().value);
else if (mGraphKey > last.key())
position->setCoords(last.key(), last.value().value);
else
{
QCPBarDataMap::const_iterator it = mBars->data()->lowerBound(mGraphKey);
if (it != first) // mGraphKey is somewhere between iterators
{
QCPBarDataMap::const_iterator prevIt = it-1;
if (mInterpolating)
{
// interpolate between iterators around mGraphKey:
double slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
} else
{
// find iterator with key closest to mGraphKey:
if (mGraphKey < (prevIt.key()+it.key())*0.5)
it = prevIt;
position->setCoords(it.key(), it.value().value);
}
} else // mGraphKey is exactly on first iterator
position->setCoords(it.key(), it.value().value);
}
}else if (mBars->data()->size() == 1)
{
QCPBarDataMap::const_iterator it = mBars->data()->constBegin();
position->setCoords(it.key(), it.value().value);
} else
qDebug() << Q_FUNC_INFO << "graph has no data"; //Add a comment to this line
}
これでQCPItemTracerでQCPBarsの値を保持するようになった(はず)。
でもってQCPCustomPlotでグラフを描画したいクラスで以下のようにQItemTracerを初期化する。
mpMouseTracer = new QCPItemTracer(ui.histogram);
ui.histogram->addItem(mpMouseTracer);// Graph上に表示を設定(histogram = QCustomPlot)
mpMouseTracer->setBars(mpHistogramBars);// ここ以外はオリジナルQCPItemTracerと同じ
mpMouseTracer->setGraphKey(0.0);// 初期key
mpMouseTracer->setStyle(QCPItemTracer::tsCircle);// 対象点を●で表示他にもCrossなどがある
mpMouseTracer->setPen(QPen(Qt::red));// ●の線の色
mpMouseTracer->setBrush(Qt::red);// ●の色
mpMouseTracer->setSize(7);//●の大きさ
// 今回はグラフマウスホバーで値を表示したいのでQCustomPlot::mouseMove signal時にslotを読んでもらう
connect(ui.histogram, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(histogramMouseMoved(QMouseEvent*)));
void MainWindow::histogramMouseMoved(QMouseEvent *event){
QPoint pos = event->pos();
double key = floor(mpHistogramBars->keyAxis()->pixelToCoord(pos.x()));
mpMouseTracer->setBarsKey(key); // 最新のkey値をセット
ui.histogram->replot();// repolotして初めて新しいポジションに●が移動する
}
とすると↓のように●でマウスホバー点をTraceしてくれる。
テキスト(key, value)の表示にはQCPItemTextを使う。
mpMouseTracerと同様にこんな感じで設定して・・
mpMouseText = new QCPItemText(ui.histogram);
ui.histogram->addItem(mpMouseText);
mpMouseText->position->setType(QCPItemPosition::ptAxisRectRatio);
mpMouseText->setFont(QFont(font().family(), 12));
mpMouseText->setPositionAlignment(Qt::AlignLeft);
mpMouseText->position->setCoords(QPointF(0, 0));
mpMouseText->setText("(-, -)");
histogramMouseMoved slotで更新する。
void MainWindow::histogramMouseMoved(QMouseEvent *event){
QPoint pos = event->pos();
double key = floor(mpHistogramBars->keyAxis()->pixelToCoord(pos.x()));
mpMouseTracer->setBarsKey(key);
double x, y;
// 以下は文字がグラフ外に出て行かないように適当にclipしている
x = qBound(0., mpMouseTracer->position->key() / mpHistogramBars->keyAxis()->range().size(), 0.75);
y = qBound(0., 1. - mpMouseTracer->position->value() / mpHistogramBars->valueAxis()->range().size(), 0.9);// value coord is TopLeft origin
mpMouseText->position->setCoords(x, y);
mpMouseText->setText("(" + QString::number(mpMouseTracer->position->key()) + ", " + QString::number(mpMouseTracer->position->value()) + ")");
ui.histogram->replot();
}
これでOK.
UPDATE: こんな感じ↓
参考までに
* tsCrosshair
* tsPlug
* tsSquare