Kohshi's Blog
ラベル
QCustomPlot
の投稿を表示しています。
すべての投稿を表示
ラベル
QCustomPlot
の投稿を表示しています。
すべての投稿を表示
2014/03/08
QCustomPlotで表示したグラフをマウスホイールズームやDragシフトする際の範囲を制限する
[QCustomPlotでマウスホイールズーム&Dragでシフト](http://kohshi.blogspot.jp/2014/03/qcustomplotdrag.html)で紹介したようにほんの数行加えるだけで、 表示領域を操作できるのだが、0原点だけはズームやシフトしたい場合にも固定したい場合は結構多い。 そういう時はどういう風にするかを紹介する。 [ここ](http://www.qcustomplot.com/index.php/support/forum/289)で書かれている内容だが、 まず、制限したいQCPAxisのrangeChangedシグナルにslotを接続する↓。 ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); mpHistogramBars = new QCPBars(ui.histogram->xAxis, ui.histogram->yAxis); mpHistogramBars->setPen(QPen(Qt::gray));// 色指定 mpHistogramBars->setBrush(QBrush(Qt::gray));// 色指定 ui.histogram->addPlottable(mpHistogramBars);// 追加 ui.histogram->setInteraction(QCP::iRangeDrag, true); ui.histogram->setInteraction(QCP::iRangeZoom, true); ui.histogram->axisRect()->setRangeDrag(Qt::Vertical); ui.histogram->axisRect()->setRangeZoom(Qt::Vertical); // yAxisRangeChanged()はslot connect(ui.histogram->yAxis, SIGNAL(rangeChanged(const QCPRange &)), this, SLOT(yAxisRangeChanged(const QCPRange &))); } ``` のようにしておいてRangeが変わったら、すなわちズームやシフトが発生したらyAxisRangeChangedが呼ばれるようにしておき、 yAxisRangeChangedの中で以下のようにRangeを再セットしてやる。 ```cpp void MainWindow::yAxisRangeChanged(const QCPRange &newRange) { ui.histogram->yAxis->setRange(0, ui.histogram->yAxis->range().upper);// y軸の下限を0に固定 } ``` 当然、マイナスの値にはしたくないという制限の場合には以下のようになる。 ```cpp void MainWindow::yAxisRangeChanged(const QCPRange &newRange) { if(newRange.lower < 0){ ui.histogram->yAxis->setRange(0, ui.histogram->yAxis->range().upper); } } ``` 常に下限を0にした場合こんな感じになる
ツイート
2014/03/07
QCustomPlotでマウスホイールズーム&Dragでシフト
あんまり簡単なんで書くのもなんなのだが・・・。 ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); mpHistogramBars = new QCPBars(ui.histogram->xAxis, ui.histogram->yAxis); mpHistogramBars->setPen(QPen(Qt::gray));// 色指定 mpHistogramBars->setBrush(QBrush(Qt::gray));// 色指定 ui.histogram->addPlottable(mpHistogramBars);// 追加 // 以下を追加する事でマウスホイールズームとDragでシフトができるようになる ui.histogram->setInteraction(QCP::iRangeDrag, true); ui.histogram->setInteraction(QCP::iRangeZoom, true); // 例えばさらに垂直方向(y軸方向)だけズームやシフトすると制限する事も可能 ui.histogram->axisRect()->setRangeDrag(Qt::Vertical); ui.histogram->axisRect()->setRangeZoom(Qt::Vertical); } ``` これだけでマウスホイールズームとDragでのシフトができるようになる。 こんな感じ
[次回](http://kohshi.blogspot.jp/2014/03/qcustomplotdrag_8.html)ではシフト・ズーム範囲を制限してみる。
ツイート
2013/12/18
QCustomPlotで凡例をグラフの外に表示→ちょっとめんどい
凡例はデフォルトではグラフ領域の中に表示される+設定で場所を指定できるみたいだが、 例えば入力を次々変えていってグラフを表示したい場合とか、グラフがどの領域にくるかわからない場合があるんだが。 そんな場合凡例はグラフ内でなくてグラフ外に表示しておいてほしいのでそのやり方を書いてみる。 結果、今の所設定一発っていうわけではなくて以下のページにもあるみたいにLayoutを調整して対応しないと いけないので、たかが凡例をグラフ外にもっていく割にはめんどい印象・・。 * [Position of legend outside graph](http://www.qcustomplot.com/index.php/support/forum/63) 以下のQCPLegendのページにも以下のように書いてある。 * [QCPLegend Class Reference](http://www.qcustomplot.com/documentation/classQCPLegend.html) > To move the legend to another position inside the axis rect, use the methods of the QCPLayoutInset. > To move the legend outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface. 凡例を軸の内側に動かすためにはQCPLayoutInsetのmehodを使え。外に動かしたい場合はQCPLayout/QCPLayoutElementを使って外に配置しろ。 みたいなことをいってるので多分合ってると思うんだけど。 つまり例えば↓のようにQCPLayoutを使って自分で凡例を配置せよということらしい。
↑を実現するには、 1. もともと用意されているグラフを描画するLayoutにColumnを追加してQCPLayoutGridを配置し 2. そこに3つのRowを追加して上下にPadding用のQCPLayoutElementを配置し 3. 中央にQCPLegendを配置 という感じ。例によってHistogramを例にしてコードにすると ```cpp: MainWindow.cpp // When initialize QCustomPlot // Moved legend to outside of axis rect QCPLayoutGrid *subLayout = new QCPLayoutGrid; // グラフの右に挿入されるQCPLayoutGrid(subLayout) ui.histogram->plotLayout()->addElement(0, 1, subLayout); // (Row, Column) = (0,1)にsubLayoutを追加 subLayout->addElement(0, 0, new QCPLayoutElement); // subLayoutの(Row, Column) = (0,0)にPadding用QCPLayoutElementを subLayout->addElement(1, 0, ui.histogram->legend); // (Row, Column) = (1,0)に凡例(Legend)を追加 subLayout->addElement(2, 0, new QCPLayoutElement); // 同Paddingを追加 subLayout->setColumnStretchFactor(0, 0.01); // subLayoutの横幅はなるべく小さくする subLayout->setRowStretchFactor(1, 0.01); // subLayoutの凡例がはいるRow=1はなるべく小さくする ui.histogram->plotLayout()->setColumnStretchFactor(1, 0.01); // subLayoutが入るColumn=1の横幅はなるべく小さくする ``` もし、上記の設定なしだと以下の様に表示されるが、
上記のようにLayout設定すると以下のようになる。
なんとかグラフ領域の外にもっていけたけど、凡例の右側が切れてたりしてちょっとしっくりこないきもするなぁ。
ツイート
2013/12/16
QCustomPlotで凡例をクリックしてグラフの表示をToggle
複数のグラフを表示していて、見たいグラフが他のグラフとかぶっててじゃまなんで手前のグラフを 消したいなんて結構あると思う。 そんな時よくあるやりかたとして凡例から対象グラフをクリックするとそれが消えるみたいな。 そのやり方を書いてみる。わりと簡単だけど。 まずは凡例(legend)を表示する方法。QCustomPlotの初期化時に ``` // histogram=QCustomPlot ui.histogram->setAutoAddPlottableToLegend(true);// plottable(=graph)を追加したら自動で凡例に追加 ui.histogram->legend->setVisible(true); // 凡例を表示する for(int i = 0; i < numColorHistogram; i++){ // QCPBars* mpHistogrambars[]; mpHistogramBars[i] = new QCPBars(ui.histogram->xAxis, ui.histogram->yAxis); mpHistogramBars[i]->setName(CHART_NAMES[i]); mpHistogramBars[i]->setPen(QPen(CHART_COLORS[i])); mpHistogramBars[i]->setBrush(QBrush(CHART_COLORS[i])); ui.histogram->addPlottable(mpHistogramBars[i]);// graphをQCustomPlotに追加 } // 凡例がクリックされたらslot toggleVisibilityを呼ぶようにconnect connect(ui.histogram, SIGNAL(legendClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*)), this, SLOT(toggleVisibility(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*))); ``` そしてtoogleVisibilityで以下のようにvisibilityを変更 ``` void MainWindow::toggleVisibility(QCPLegend* legend, QCPAbstractLegendItem* item, QMouseEvent* event){ if(!item) return; QCPPlottableLegendItem *plItem = qobject_cast
(item); if(!plItem) return; plItem->plottable()->setVisible(!plItem->plottable()->visible()); } ``` これで凡例の対象グラフをクリックするとそのグラフの表示・非表示がtoggleされる。こんな感じ↓
気になる点はlegendをみてどのgraphが現在表示されているのがわからない点。これはまた調査しよう。
ツイート
2013/12/15
QCustomPlotでマウスホバー時に値を表示
前回[QCustomPlotがいい感じ](http://kohshi.blogspot.jp/2013/12/qcustomplot.html)でグラフ表示の基本を書いてみたので 次はマウスホバー+グラフ値の表示方法について書いてみる。 マウスホバーはQCPItemTracerをつかう。 QCustomPlotは横軸のことをkeyAxis, 縦軸をvalueAxisと呼ぶがQCPMouseTrace::setGraphKey(int key)でkeyを 更新すると、対応するvalue値を保持して、指定したindicatorでグラフ内の対象箇所を示してくれる機能がある。 でも一点難点があるのはQCPGraphにだけ対応していてQCPBarsには対応してない点。 なので前回あげたヒストグラムには適応できない。 というわけで、ちょっと強引にQCPItemTracerにsetBars()とsetBarsKey()を追加してメンバー変数にQCPBarsを持たせてみる。 ```qcustomplot.h void setBars(QCPBars *bars); void setBarsKey(double key); QCPBars *mBars; ``` ```qcustomplot.cppp 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
ツイート
2013/12/12
QCustomPlotがいい感じ
最近色々な機会で[Qt](http://qt-project.org/)を使う機会が増えたんだが、 いろいろ使ってるうちに手軽にグラフを描画したいケースがけっこうでてきた。 これまでは[QWT](http://qwt.sourceforge.net/)を使っていたんだが、 単純な棒グラフ作るだけでもわりと手続きが多くてめんどくさいのが難点。 あとドキュメントもいまいち。 で、代わりを探してみると[QCustomPlot](http://www.qcustomplot.com)っていうのを 最近よく見かけるようになってて、わりと評判もよさそうなんで使ってみた。 準備としては[QCustomPlot](http://www.qcustomplot.com)にアクセスしてDownloadのページから 最新のパッケージをダウンロードし、その中から * qcustomplot.h * qcustomplot.cpp を持ってきてプロジェクトに追加するだけ。 まず、Qt Designerでグラフを表示させたい箇所にWidget(QWidget)を配置。 配置したら、右クリックして格上げ先を指定を選択↓
格上げ先にQCustomPlotとqcostomplot.hを指定して「追加」を押し
追加されたQCustomPlotを選択して「格上げ」。
名前を例えばhistogramとかにしてDesignerでの作業は完了。
例えばヒストグラムを表示させたい場合にはQCPBarsを使う クラスメンバにQCPBars(QCPBars *mpHistogramBars)を追加し、コンストラクタでのように初期化する。 ```cpp: MainWindow.h namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; }; ``` ```cpp: MainWindow.cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); mpHistogramBars = new QCPBars(ui.histogram->xAxis, ui.histogram->yAxis); mpHistogramBars->setPen(QPen(Qt::gray));// 色指定 mpHistogramBars->setBrush(QBrush(Qt::gray));// 色指定 ui.histogram->addPlottable(mpHistogramBars);// 追加 } ``` あとは、描画したいタイミングで以下のようにQVector
を使ってデータをセットする。 ```cpp: MainWindow.cpp void MainWindow::drawHistogram(const QImage &image){ QVector
keys(256); QVector
values(256); for(int i = 0; i < keys.size(); i++){ keys[i] = i; values[i] = 0; } for(int j = 0; j < image.height(); j++){ for(int i = 0; i < image.width(); i++){ QRgb rgb = image.pixel(i, j);// RGB -> Y int y = qBound(0, (int)(0.299 * qRed(rgb) + 0.587 * qGreen(rgb) + 0.114 * qBlue(rgb) + 0.5), 255); values[y] += 1.0; } } mpHistogramBars->setData(keys, values); ui.histogram->rescaleAxes(); ui.histogram->replot(); } ``` QCustomPlotでは基本的にデータはdoubleで指定するもよう。 実行すると以下のような感じのヒストグラムが表示される。 (ただし、グラフ中の赤丸と座標点は表示されない。結構グラフを表示した後にマウスホバーで値を見たい場合はあるので、これの表示の仕方はまた別途)
結構見栄えがいい。 複数表示もいい感じにできます。
QWTだとsetIntervalだとか結構わかりにくいコードをいっぱいかかないといけなかった・・。 それとくらべてQCustomPlotは直感的でわかりやすいし、簡単! 次はQItemTracerでマウスホバーのやり方でもまとめる予定。
ツイート
前の投稿
ホーム
登録:
投稿 (Atom)