「Qtライブラリ - Qwt」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
265行目: 265行目:
  item->attach(m_pPlot);
  item->attach(m_pPlot);
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== 画像の縮小 ==
以下の例では、ボタンを押下する時、プロットの中心を基準に画像を縮小している。<br>
<syntaxhighlight lang="c++">
void MyPlot::ZoomOut()
{
    QRectF rcNew;
    // 現在のズーム枠を取得
    QRectF rc = m_pZoomer->zoomRect();
    rc.normalized();
    // ズームアウト枠を設定
    rcNew.setLeft(rc.left() - dRange * 0.1);
    rcNew.setRight(rc.right() + dRange * 0.1);
    rcNew.setTop(rc.top() - dRange * 0.1);
    rcNew.setBottom(rc.bottom() + dRange * 0.1);
    // ズームアウト枠を適用
    m_pZoomer->zoom( rcNew );
    // ズームスタックをリセット
    m_pZoomer->setZoomBase();
    canvas()->update();
}
</syntaxhighlight>
<br>
画像の縮小機能を実装する場合、ズームスタックは、都度リセットする方が無難である。<br>
また、パンニングの時もズームスタックを都度リセットして、ズームベースを更新する。<br>
<u>ズームベースとは、ズームの初期化時のズーム範囲のこと。</u><br>
<syntaxhighlight lang="c++">
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
  // コンストラクタでパンニング完了時のシグナル・スロットを設定
  QObject::connect(m_pPanner, SIGNAL(panned(int, int)), this, SLOT(updateZoom(int, int)));
  // ...略
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// QwtPlotクラスを継承した派生クラスのcppファイル
// パンニング完了時のスロット
void MyPlot::updateZoom(int nX, int nY)
{
    QRectF rc = m_pZoomer->zoomRect();
    QPointF pnt = rc.topLeft();
    // 現在のズーム枠を移動してズームベースを更新
    m_pZoomer->setZoomBase(QRectF(QPointF(pnt.x() + nX, pnt.y() + nY), rc.size()));
    canvas()->replot();
}
</syntaxhighlight>
<br><br>
== 目盛りの設定 ==
標準では、目盛りは実数値が表示されるが、目盛りの値を変更することができる。<br>
<br>
以下の例では、度単位の数値を度分秒表記にしている。<br>
まず、<code>QwtScaleDraw</code>クラスを継承した派生クラスを作成する。<br>
<syntaxhighlight lang="c++">
// 縦軸クラス
class DMSScaleDrawLat : public QwtScaleDraw
{
public:
    DMSScaleDrawLat()
    {
      setLabelAlignment( Qt::AlignLeft | Qt::AlignVCenter );
    }
    // 与えられた数値を度分秒に変換
    virtual QwtText label( double dVal ) const
    {
      int nM;
      double dS;
      int nD    = (int)floor( dVal );
      double dS = (dVal - nD)*60;
      int nM    = (int)floor( dS );
      double dS = (dS - nM) * 60;
      return QObject::tr( "%1d\n %2' %3\"" ).arg(nD).arg(nM).arg(dS);
    }
};
// 横軸クラス
// 縦軸クラスの派生クラスとして、ラベルの向きだけを変更
class DMSScaleDrawLon : public DMSScaleDrawLat
{
public:
    DMSScaleDrawLon()
    {
      setLabelRotation( -90.0 );
    }
};
</syntaxhighlight>
<br>
上記で定義した縦軸および横軸クラスを、<code>QwtPlot</code>クラスを継承した派生クラスの各軸に設定する。<br>
以下の例では、<code>QwtPlot</code>クラスを継承した派生クラスのコンストラクタで設定している。<br>
<syntaxhighlight lang="c++">
MyPlot::MyPlot(QWidget *parent) : QwtPlot(parent)
{
    // ...略
    // 縦軸および横軸クラスを設定する
    setAxisScaleDraw(QwtPlot::xBottom, new DMSScaleDrawLon());
    setAxisScaleDraw(QwtPlot::yLeft, new DMSScaleDrawLat());
}
</syntaxhighlight>
<br>
標準の軸ラベルは、与えられた数値に対して、<code>QLocale().toString(value)</code>で返される文字列を表示する。<br>
(qwt_abstract_scale_draw.hファイルの<code>label</code>メソッド)<br>
<br>
この時、大きい数値は指数型の文字列を返す。<br>
指数型の文字列を表示させない場合も、上記のように、<code>QwtPlot</code>クラスを継承した派生クラスを作成する必要がある。<br>
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]

2021年3月17日 (水) 10:45時点における版

概要

Qwt(Qt Widget for Technical Application)は、Qtの拡張ライブラリであり、プロット、ダイアル、メータ等のウィジェットを提供する。

ここでは、Qwtのインストールと使用方法を記載する。


Qwtのインストール

Qwtのビルドおよびコンパイルに必要な依存関係のライブラリをインストールする。
これらライブラリは、ビルドおよびコンパイル時のみ必要である。

sudo zypper install Mesa-devel Mesa-KHR-devel


Qwtの公式Webサイトから最新のQwtをダウンロードする。
ダウンロードしたQwtを解凍するため、以下のコマンドを実行する。

tar xf qwt-x.x.x.tar.bz2


解凍したQwtディレクトリに移動して、qwtconfig.priファイルを以下のように編集する。

vi qwtconfig.pri


qwtconfig.priファイル

# 変更前
unix {
   QWT_INSTALL_PREFIX   = /usr/local/qwt-$$QWT_VERSION
   # QWT_INSTALL_PREFIX = /usr/local/qwt-$$QWT_VERSION-qt-$$QT_VERSION
}

win32 {
   QWT_INSTALL_PREFIX   = C:/Qwt-$$QWT_VERSION
   # QWT_INSTALL_PREFIX = C:/Qwt-$$QWT_VERSION-qt-$$QT_VERSION
}

# 変更後
unix {
   QWT_INSTALL_PREFIX   = <Qtのインストールディレクトリ>/Qwt-$$QWT_VERSION
   # QWT_INSTALL_PREFIX = /usr/local/qwt-$$QWT_VERSION-qt-$$QT_VERSION
}

win32 {
   QWT_INSTALL_PREFIX   = C:/Qwt-$$QWT_VERSION
   # QWT_INSTALL_PREFIX = C:/Qwt-$$QWT_VERSION-qt-$$QT_VERSION
}


Qwtプロジェクトをビルドおよびコンパイルして、インストールする。

qmake qwt.pro
make -j 8
make install


<Qtのインストールディレクトリ>/Qwt-x.x.x/plugins/designer/libqwt_designer_plugin.soファイルを、以下のディレクトリにコピーする。

  • Qt Creatorに統合されているQt Designer
    <Qtのインストールディレクトリ>/Tools/QtCreator/lib/Qt/plugins/designerディレクトリ
    正常に読み込まれたプラグインおよび読み込みに失敗したプラグインを確認するには、
    [ツール] - [フォームエディター] - [QtDesignerプラグインについて]を選択する。

  • スタンドアロンのQt Designer
    <Qtのインストールディレクトリ>/5.15.2/gcc_64/plugins/designerディレクトリ
    正常に読み込まれたプラグインおよび読み込みに失敗したプラグインを確認するには、[ヘルプ] - [プラグイン]を選択する。


また、Qwtの公式Webサイトには、QwtPolarQwtPlot3DQtiPlot等も存在する。


Qtプロジェクトの設定

Qwtを使用するため、Qtプロジェクトファイルに以下の設定を追記する。

# QWT
QWT_LOCATION = /home/suse/InstallSoftware/Qt/Qwt-6.1.6
INCLUDEPATH += $${QWT_LOCATION}/include/
LIBS += -L$${QWT_LOCATION}/lib -lqwt


Qt Designer画面を開いて、Qwtウィジェットが存在するか確認する。


PyQwt

PythonでQwtが使用できるPyQwtライブラリも存在する。


QwtPlot

プロット上に曲線や散布図を表示する場合、まず、Qt Designer画面にてQwtPlotウィジェットを配置する。
次に、QwtPlotItemクラスのインスタンスにデータを渡して、QwtPlotクラスのインスタンスにアタッチする。

QwtPlotの目盛りは自動的に最適に表示される。

一般的には、QwtPlotクラスまたはQwtPlotItemクラスを継承した派生クラスを作成して使用する。
Qwtライブラリのほとんどのサンプルでは、派生クラスを作成している。


プロット画面のコントロールを設定

ズームやパンニングを行うプロット画面の制御は、それぞれQwtPlotZoomerクラス、QwtPlotPannerクラスを使用する。
QwtPlotZoomerクラスの設定は必須であり、設定しない場合は何も表示されない。

QwtPlotZoomerクラスは、ズーム枠を描画するため、QwtPlotPickerクラス(ラバーバンドを扱うクラス)の派生クラスであり、
QwtPlotPannerクラスは、QwtPannerクラスの派生クラスであるため、設定方法は両者で異なる。

以下の例では、QwtPlotクラスの派生クラスを作成している。

 // MyPlot.h(QwtPlotクラスの派生クラス)
 #pragma once
 
 #include "qwt_plot.h"
 
 class MyPlot : public QwtPlot
 {
    Q_OBJECT
  
 public:
    MyPlot(QWidget *parent = NULL);
    ~MyPlot();
 
 private:
    // メンバ変数宣言
    QwtPlotZoomer *m_pZoomer;
    QwtPlotPanner *m_pPanner;
 };


QwtPlotZoomerクラスにおいて、MouseSelect1はズーム開始、MouseSelect2はリセット、MouseSelect3は1つ前のズームに戻るに対応させる。
ズームレベルはズームスタックに積まれており、通常は、ズームスタックを1つ戻ることでズームアウトする。

 // MyPlot.cpp
 
 MyPlot::MyPlot(QWidget *parent) : QwtPlot(parent)
 {
    // Zoomerの設定
    m_pZoomer = new QwtPlotZoomer(canvas());
 
    // ズーム枠の色を設定
    m_pZoomer->setRubberBandPen(QColor(Qt::darkBlue));
 
    // マウスの座標値の色を設定
    m_pZoomer->setTrackerPen(QColor(Qt::darkBlue));
 
    // マウスボタンの割り当て
    m_pZoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton);
    m_pZoomer->setMousePattern(QwtEventPattern::MouseSelect2, Qt::RightButton, Qt::ControlModifier);
    m_pZoomer->setMousePattern(QwtEventPattern::MouseSelect3, Qt::RightButton);
 
    // 座標値の表示はズーム枠がアクティブなときのみ
    m_pZoomer->setTrackerMode(QwtPicker::ActiveOnly);
 
    // Pannerの設定
    m_pPanner = new QwtPlotPanner(canvas());
 
    // パンニングのボタンを中ボタンに設定
    m_pPanner->setMouseButton(Qt::MiddleButton);
 
    setAutoReplot(true);
 }



プロットの縦横比を固定

プロットウィジェットの目盛りは、初期状態では、プロットウィンドウの縦横比に応じて変化する。

ウィンドウの縦横比に関わらず、縦軸と横軸の目盛り幅を一定にする場合は、QwtPlotRescalerクラスを使用する。
QwtPlotRescalerクラスを使用する場合、QwtPlotクラスの派生クラスを作成することを推奨する。

特に、画像をプロットする場合、表示される画像の縦横比がウィンドウにより変化する。

 // MyPlot.h(QwtPlotクラスの派生クラス)
 #pragma once
 
 #include "qwt_plot.h"
 #include <qwt_plot_rescaler.h>
 
 class MyPlot : public QwtPlot
 {
 Q_OBJECT
 
 public:
    MyPlot(QWidget *parent = NULL);
    ~MyPlot();
 
 private:
    QwtPlotRescaler *m_pRescaler;
 };


 // MyPlot.cpp
 #include "MyPlot.h"
 
 MyPlot::MyPlot(QWidget *parent) : QwtPlot(parent)
 {
    // QwtPlotRescalerのコンストラクタ、第1引数はQwtPlotCanvasオブジェクトへのポインタ、
    // 第2引数は目盛り幅の基準軸、第3引数のように指定することで縦横比を固定
    m_pRescaler = new QwtPlotRescaler(canvas(), QwtPlot::xBottom, QwtPlotRescaler::Fixed);
    m_pRescaler->setEnabled(true);
 
    // X軸は目盛りが両側に引き伸ばされるように指定
    m_pRescaler->setExpandingDirection(QwtPlot::xBottom, QwtPlotRescaler::ExpandBoth);
 
    // Y軸は下方向に引き伸ばされるように指定
    m_pRescaler->setExpandingDirection(QwtPlot::yLeft, QwtPlotRescaler::ExpandDown);
 
    // 目盛りの縦横比を指定
    m_pRescaler->setAspectRatio(1.0);
 
    // 目盛り幅設定を実行
    m_pRescaler->rescale();
 
    setAutoReplot(true);
 }



画像のプロット

QwtPlotクラスは、グラフ等のデータをプロットすることが主目的であるが、画像をプロットすることもできる。

画像をプロットするには、まず、画像用のQwtPlotItemクラスを継承した派生クラスを作成する。

以下の例では、プロット範囲の矩形と画像データをメンバ変数として定義している。
また、QwtPlotItemクラスのdrawメソッドをオーバーライドして、画像を描画している。

※注意
プロットの基準位置は、画像の左下であることに注意すること。
一般的には、コンストラクタの引数で座標を指定することを推奨する。

 // ImageItem.h
 #pragma once
 
 #include "qwt_plot_item.h"
 
 class ImageItem : public QwtPlotItem
 {
 public:
    ImageItem( QImage *pImg );
    ~ImageItem(void);
 
    virtual void draw(QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &rc) const;
 
 private:
    QImage *m_pimg;
    QRectF m_rc;
 };


 // ImageItem.cpp
 #include "ImageItem.h"
 #include <qwt_painter.h>
 #include <qwt_scale_map.h>
 
 ImageItem::ImageItem(QImage *pImg)
 {
    // 画像をプロットする座標を指定する
    m_pimg = pImg;
    m_rc = QRectF(QPoint( 256, 512 ), pImg->size());
 }
 
 ImageItem::~ImageItem()
 {
 }
 
 void ImageItem::draw(QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &rc) const
 {
    // 描画位置を指定する
    // プロット位置とは異なり、ウィンドウ座標である
    // 以下のように記述することにより、ビューのパンニングを実行すると画像も動く
    QRectF rcImage = QwtScaleMap::transform(xMap, yMap, m_rc);
    QwtPainter::drawImage(painter, rcImage, *m_pimg);
 }


画像プロットにアタッチするには、他のプロットと同様に記述する。

 ImageItem *item = new ImageItem(&m_qimg);
 item->attach(m_pPlot);



画像の縮小

以下の例では、ボタンを押下する時、プロットの中心を基準に画像を縮小している。

 void MyPlot::ZoomOut()
 {
    QRectF rcNew;
 
    // 現在のズーム枠を取得
    QRectF rc = m_pZoomer->zoomRect();
    rc.normalized();
 
    // ズームアウト枠を設定
    rcNew.setLeft(rc.left() - dRange * 0.1);
    rcNew.setRight(rc.right() + dRange * 0.1);
    rcNew.setTop(rc.top() - dRange * 0.1);
    rcNew.setBottom(rc.bottom() + dRange * 0.1);
 
    // ズームアウト枠を適用
    m_pZoomer->zoom( rcNew );
 
    // ズームスタックをリセット
    m_pZoomer->setZoomBase();
 
    canvas()->update();
 }


画像の縮小機能を実装する場合、ズームスタックは、都度リセットする方が無難である。
また、パンニングの時もズームスタックを都度リセットして、ズームベースを更新する。
ズームベースとは、ズームの初期化時のズーム範囲のこと。

 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
 {
   // コンストラクタでパンニング完了時のシグナル・スロットを設定
   QObject::connect(m_pPanner, SIGNAL(panned(int, int)), this, SLOT(updateZoom(int, int)));
 
   // ...略
 }


 // QwtPlotクラスを継承した派生クラスのcppファイル
 
 // パンニング完了時のスロット
 void MyPlot::updateZoom(int nX, int nY)
 {
    QRectF rc = m_pZoomer->zoomRect();
    QPointF pnt = rc.topLeft();
 
    // 現在のズーム枠を移動してズームベースを更新
    m_pZoomer->setZoomBase(QRectF(QPointF(pnt.x() + nX, pnt.y() + nY), rc.size()));
 
    canvas()->replot();
 }



目盛りの設定

標準では、目盛りは実数値が表示されるが、目盛りの値を変更することができる。

以下の例では、度単位の数値を度分秒表記にしている。
まず、QwtScaleDrawクラスを継承した派生クラスを作成する。

 // 縦軸クラス
 class DMSScaleDrawLat : public QwtScaleDraw
 {
 public:
    DMSScaleDrawLat()
    {
       setLabelAlignment( Qt::AlignLeft | Qt::AlignVCenter );
    }
 
    // 与えられた数値を度分秒に変換
    virtual QwtText label( double dVal ) const
    {
       int nM;
       double dS;
       int nD    = (int)floor( dVal );
       double dS = (dVal - nD)*60;
       int nM    = (int)floor( dS );
       double dS = (dS - nM) * 60;
 
       return QObject::tr( "%1d\n %2' %3\"" ).arg(nD).arg(nM).arg(dS);
    }
 };
 
 // 横軸クラス
 // 縦軸クラスの派生クラスとして、ラベルの向きだけを変更
 class DMSScaleDrawLon : public DMSScaleDrawLat
 {
 public:
    DMSScaleDrawLon()
    {
       setLabelRotation( -90.0 );
    }
 };


上記で定義した縦軸および横軸クラスを、QwtPlotクラスを継承した派生クラスの各軸に設定する。
以下の例では、QwtPlotクラスを継承した派生クラスのコンストラクタで設定している。

 MyPlot::MyPlot(QWidget *parent) : QwtPlot(parent)
 {
    // ...略
 
    // 縦軸および横軸クラスを設定する
    setAxisScaleDraw(QwtPlot::xBottom, new DMSScaleDrawLon());
    setAxisScaleDraw(QwtPlot::yLeft, new DMSScaleDrawLat());
 }


標準の軸ラベルは、与えられた数値に対して、QLocale().toString(value)で返される文字列を表示する。
(qwt_abstract_scale_draw.hファイルのlabelメソッド)

この時、大きい数値は指数型の文字列を返す。
指数型の文字列を表示させない場合も、上記のように、QwtPlotクラスを継承した派生クラスを作成する必要がある。