Return to Linux Life Edit

  ホーム · 全てのクラス · メインのクラス · 注釈付き · グループ別 · 関数一覧

座標系

Qt での任意の ペイントデバイス は、描画可能な 2 次元平面である。 QWidget QPixmap QPicture QPrinter といったものは全てペイントデバイスである。 QPainter は、あらゆるペイントデバイスに対してオブジェクトを描画できる。

ペイントデバイスのデフォルトの座標系は、左上の角が原点となっている。 x 軸は右方向が正、 y 軸は下方向が正である。デフォルトの単位はピクセル単位で、プリンタ上の寸法は 1 ピクセルが 1 ポイント (= 1/72 インチ) となっている。

以下に示す図は、ペイントデバイスの左上角を拡大して表示したものである。

このソースコードは、矩形と線分を表示するものである。(図中では、このコード内容の他にグリッド線の追加や色補正が行われている。)

    void MyWidget::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setPen(Qt::darkGray);
        painter.drawRect(1, 2, 4, 3);
        painter.setPen(Qt::lightGray);
        painter.drawLine(9, 2, 7, 7);
    }

ここで注意すべき点は、 QPainter::drawRect() で指定した矩形の大きさは 4x3 であるのに対し、実際に描画された矩形の包含領域は 5x4 となっていることである。最近のグラフィックス用 API では、ほとんどがこのように描画するようになっている。

描画時に指定するサイズは、通常では線幅を考慮しない数学的な大きさであり、これは図形の変換(拡大縮小や剪断など)を考慮すると合理的なものである。この法則は、 QPainter の関連する全ての関数( drawRoundRect() drawEllipse()など)に同様に適用される。

一方、 QPainter::drawLine() では上記の法則とは異なり、線分の両端点が描かれる。

以下に、座標系と関連の深いクラスを列挙する。幾つかのクラスは、 intベースのバージョンと、 qrealベースの二つのバージョンが存在する。そのようなクラスでは、 qreal バージョンの方にはクラス名の最後に 「F」の文字が追加されている。

クラス説明
QPoint(F)2次元座標系上の単一の点。点を扱う Qt の関数の多くは、引数として QPoint型、 QPointF型、 int型数値 2 個、または qreal型数値 2 個のいずれかを取る。
QSize(F)単一の 2 次元ベクトル。内部データ的には、 QPoint QSize は同じものであるが、点と大きさは概念的には別であるので、クラスとしては別々に存在する。これもまた、多くの関数で QSizeF型、 QSize型、 int型数値 2 個、 qreal型数値 2 個のいずれを引数として受け取る多くの関数がある。
QRect(F)2 次元矩形。多くの関数引数として、 QRectF型、 QRect型、 int型数値 4 個、 qreal型数値 4 個が扱われている。
QLine(F)2 次元線分。始点と終点によって形状が定義される。
QPolygon(F)2 次元多角形。多角形は QPoint(F)型のベクトル(配列)である。多角形の始点と終点の位置が一致するとき、閉じた多角形となる。
QPaintDevice描画が可能なデバイス。Qt には、最初から用意されている幾つかのデバイス (ウィジェット、画素集合、画像、描画用クラス) があり、さらに新たなデバイスを定義することも可能である。
QPainter描画を行うクラス。同一のコードで、全てのデバイスに描画を行うことができる。デバイス間には様々な相違点が存在するが( QPrinter::newPage() などはその好例)、 QPainter は全てのデバイスに対して同じように動作する。
QMatrix3 行 3 列の一次変換用行列。 QMatrix を用いることで、座標系上で回転移動、剪断、拡大縮小、平行移動を表現できる。
QPainterPath汎用的な定義が可能な 2 次元形状。ペインターパスは、矩形、楕円形、スプライン曲線などの組み合わせや、経路を表現する点集合として形状を表現できる、究極的な形状表現クラスである。また、経路は曲線としてだけでなく、領域の境界としても利用できる。
QPaintEngine描画機構のための抽象基底クラス。通常、利用者がこのクラスを直接利用することはない。
QRegionペイントデバイス中の領域を表し、 QRectの集合として表現されている。領域を指定する方法としては、 QPainterPath の方を QRegion よりも推奨する。これは、 QPainterPath の方が座標変換処理により適しているという理由からである。

座標変換

Qt におけるデフォルトの座標系は、前述したように左上が (0, 0) となる座標系である。 QPainter は、行列変換による任意の変換をサポートする。 この行列を利用して、図形の方向と位置を決定していく。 Qt では、この行列を操作するための関数として QPainter::rotate()、 QPainter::scale()、 QPainter::translate() などが用意されている。

QPainter::save() と QPainter::restore() は、この行列を保存・復元する。 QMatrix オブジェクトおよび、 QPainter::matrix() と QPainter::setMatrix() を利用すれば、同じ変換を反復するときにも対応できる。

様々なペイントデバイスで同じ描画処理を再利用するとき、一つの変換行列が頻繁に必要となることがある。変換を行わないとき、描画結果はペイントデバイスの解像度に依存することになる。プリンタでは、通常は 1 インチあたりのドット数は 600 となるが、モニタスクリーンの場合の 1 インチあたりのドット数は 75 から 100 くらいとなってしまい、実際の図形の大きさは著しく異なってしまうことになる。

以下のコードは、 アナログ時計ウィジェット のサンプル中のもので、行列変換を使って時計の外観を描画している例である。まずは、先を読み進める前にこのコードをコンパイルし、実際に実行してみることを勧める。特に、ウィンドウのサイズ変更を試みてほしい。

    void AnalogClock::paintEvent(QPaintEvent *)
    {
        static const int hourHand[6] = { 7, 8, -7, 8, 0, -40 };
        static const int minuteHand[6] = { 7, 8, -7, 8, 0, -70 };
        QColor hourColor(127, 0, 127);
        QColor minuteColor(0, 127, 127, 191);
        int side = qMin(width(), height());
        QTime time = QTime::currentTime();
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.translate(width() / 2, height() / 2);
        painter.scale(side / 200.0, side / 200.0);

最初に行うことはペインターのセットアップである。座標系の原点を左上角からウィジェットの中心にもってくるように、translate() によって座標変換を行っている。また、scale() によって ( side /100) 倍の拡大(あるいは縮小)を行っている。ここで、 side はウィジェットの縦幅と横幅のうち短い方の幅である。これにより、ウィジェットが縦長や横長になっていても、時計そのものは正方形として表示される。

その結果、原点が中央にある 100x100 の正方形領域を得ることができる。それは、ウィジェット中に描画可能な最大の正方形ということになる。

        painter.save();
        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
        painter.drawConvexPolygon(QPolygon(3, hourHand));
        painter.restore();

座標系の回転と、 QPainter::drawConvexPolygon() を利用することで、時計の短針を描いている。回転の恩恵によって、針の向きは正しい方向で描かれる。

多角形は、 x y の値が交互に格納されている static 配列「 hourHand 」(関数の始めに定義されている) によって形状が指定されている。これは 4 点 (2, 0)、(0, 2)、(-2, 0)、(0, -25) に対応している。(訳注: この部分は原文の間違いだと思われる。)

さらに、座標変換による計算誤差が蓄積してしまうのを避けるため、 QPainter::save() と QPainter::restore() でコードを囲む。

        painter.save();
        painter.rotate(6.0 * (time.minute() + time.second() / 60.0));
        painter.drawConvexPolygon(QPolygon(3, minuteHand));
        painter.restore();

長針も同様に、4 点 (1, 0)、(0, 1)、(-1, 0)、(0, -40) に対応して生成する。(訳注: ここもやはり原文の間違いと思われる。) ここでは、長針(訳注: おそらく原文で「短針」と間違えている) よりも細長い形状が指定されている。

        for (int j = 0; j < 60; ++j) {
            if ((j % 5) != 0)
                painter.drawLine(92, 0, 96, 0);
            painter.rotate(6.0);
        }

最後に、30度間隔で配置される 12 本の短線を時計の表面に描画する。この部分はあまり賢明な方法とは言えないが、描画の際には問題とはならない。


Copyright © 2005 Trolltech Trademarks
Qt 4.0.0