このページでは

2D ペインティングの例

「2D 描画」の例では、QPainter とQOpenGLWidget を組み合わせて使用し、対応ハードウェア上でアクセラレーションされた 2D グラフィックスを表示する方法を示しています。

NativeとOpenGLでの画像比較を示すスクリーンショット

QPainter クラスは、QWidget やQImage といったQPaintDevice のサブクラスが提供するペイントデバイス上に、2D グラフィックプリミティブを描画するために使用されます。

QOpenGLWidget はQWidget のサブクラスであるため、QWidget と同様に、そのpaintEvent()を再実装し、QPainter を使用してデバイス上に描画することが可能です。唯一の違いは、システムのOpenGLドライバが対応している場合、描画操作がハードウェアで高速化されるという点です。

この例では、QWidget とQOpenGLWidget に対して同じ描画操作を実行します。QWidget はアンチエイリアシングが有効な状態で表示され、QOpenGLWidget も、システムのOpenGLドライバが必要な拡張機能をサポートしている場合はアンチエイリアシングを使用します。

概要

QOpenGLWidget のサブクラスへの描画結果と、QWidget のサブクラスでのネイティブ描画結果を比較できるようにするため、両方のウィジェットを並べて表示します。これを行うために、QWidget とQOpenGLWidget のサブクラスを派生させ、それぞれに対して同じ描画操作を実行する独立したHelper クラスを使用し、それらをWindow クラスが提供するトップレベルウィジェット内に配置します。

ヘルパークラスの定義

この例では、描画処理はヘルパークラスによって行われます。これは、QWidget のサブクラスとQOpenGLWidget のサブクラスの両方で、同じ描画処理を実行したいからです。

Helper クラスは極めて簡素な構成となっています:

class Helper
{
public:
    Helper();

public:
    void paint(QPainter *painter, QPaintEvent *event, int elapsed);

private:
    QBrush background;
    QBrush circleBrush;
    QFont textFont;
    QPen circlePen;
    QPen textPen;
};

コンストラクタを除けば、ウィジェットのサブクラスのいずれかから提供されたペインターを使用して描画を行うpaint() 関数のみを提供しています。

ヘルパークラスの実装

このクラスのコンストラクタは、ウィジェット上にコンテンツを描画するために必要なリソースを設定します:

Helper::Helper()
{
    QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
    gradient.setColorAt(0.0, Qt::white);
    gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));

    background = QBrush(QColor(64, 32, 64));
    circleBrush = QBrush(gradient);
    circlePen = QPen(Qt::black);
    circlePen.setWidth(1);
    textPen = QPen(Qt::white);
    textFont.setPixelSize(50);
}

実際の描画は `paint() ` 関数内で実行されます。この関数は、ペイントデバイス(QWidget またはQOpenGLWidget のいずれか)への描画用にすでにセットアップ済みの `QPainter `、描画対象領域に関する情報を提供する `QPaintEvent `、およびペイントデバイスが最後に更新されてからの経過時間(ミリ秒単位)を受け取ります。

void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
{
    painter->fillRect(event->rect(), background);
    painter->translate(100, 100);

まず、ペイントイベントに含まれる領域を塗りつぶすことから描き始め、その後、座標系の原点を移動させて、残りの描画操作がペイントデバイスの中心に向かってずれるようにします。

指定された経過時間を使用して、円が座標系の原点を中心に外側へ移動しているように見えるようにアニメーションさせながら、円の螺旋パターンを描画します。

    painter->save();
    painter->setBrush(circleBrush);
    painter->setPen(circlePen);
    painter->rotate(elapsed * 0.030);

    qreal r = elapsed / 1000.0;
    int n = 30;
    for (int i = 0; i < n; ++i) {
        painter->rotate(30);
        qreal factor = (i + r) / n;
        qreal radius = 0 + 120.0 * factor;
        qreal circleRadius = 1 + factor * 20;
        painter->drawEllipse(QRectF(radius, -circleRadius,
                                    circleRadius * 2, circleRadius * 2));
    }
    painter->restore();

この処理中に座標系が何度も回転されるため、事前に `save()` で `QPainter` の状態を保存し、処理終了後に `restore()` で元に戻します。

    painter->setPen(textPen);
    painter->setFont(textFont);
    painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, QStringLiteral("Qt"));
}

効果を完成させるために、原点にテキストを描画します。

ウィジェットクラスの定義

Widget クラスは、Helper クラスによって描画されるシンプルなアニメーションを表示するために使用する、基本的なカスタムウィジェットを提供します。

class Helper;

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(Helper *helper, QWidget *parent);

public slots:
    void animate();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Helper *helper;
    int elapsed;
};

コンストラクタを除けば、このクラスには、カスタマイズされたコンテンツを描画するための `paintEvent()` 関数と、そのコンテンツをアニメーション化するために使用されるスロットのみが含まれています。1 つのメンバ変数は、ウィジェットがコンテンツの描画に使用する `Helper ` を追跡し、もう 1 つは、前回の更新からの経過時間を記録します。

ウィジェットクラスの実装

コンストラクタは、メンバー変数の初期化(渡されたHelper オブジェクトの格納と基底クラスのコンストラクタの呼び出し)を行うほか、ウィジェットのサイズを固定するように設定します:

Widget::Widget(Helper *helper, QWidget *parent)
    : QWidget(parent), helper(helper)
{
    elapsed = 0;
    setFixedSize(200, 200);
}

animate() スロットは、後述するタイマーがタイムアウトするたびに呼び出されます:

void Widget::animate()
{
    elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
    update();
}

ここでは、タイマーが前回タイムアウトしてから経過した間隔を算出し、ウィジェットを再描画する前に既存の値にそれを加算します。Helper クラスで使用されるアニメーションは1秒ごとにループするため、剰余演算子を使用して、elapsed 変数が常に1000未満になるようにすることができます。

Helper クラスが実際の描画をすべて行うため、ウィジェットのQPainter を設定し、ヘルパークラスのpaint() 関数を呼び出すpaintイベントを実装するだけで済みます:

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    helper->paint(&painter, event, elapsed);
    painter.end();
}

GLWidget クラスの定義

GLWidget クラスの定義は、QOpenGLWidget を継承している点を除けば、基本的にWidget クラスと同じです。

class Helper;

class GLWidget : public QOpenGLWidget
{
    Q_OBJECT

public:
    GLWidget(Helper *helper, QWidget *parent);

public slots:
    void animate();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Helper *helper;
    int elapsed;
};

ここでも、メンバ変数には、ウィジェットの描画に使用されるHelper と、前回の更新からの経過時間が記録されています。

GLWidget クラスの実装

コンストラクタは、Widget クラスのコンストラクタとは若干異なります。

GLWidget::GLWidget(Helper *helper, QWidget *parent)
    : QOpenGLWidget(parent), helper(helper)
{
    elapsed = 0;
    setFixedSize(200, 200);
    setAutoFillBackground(false);
}

elapsed メンバー変数が初期化され、ウィジェットの描画に使用されるHelper オブジェクトが格納されます。

animate() スロットは、Widget クラスが提供するものと全く同じです:

void GLWidget::animate()
{
    elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
    update();
}

paintEvent() は、Widget クラスにあるものとほぼ同じです:

void GLWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    helper->paint(&painter, event, elapsed);
    painter.end();
}

アンチエイリアシングは利用可能な場合に自動的に有効になるため、ウィジェットにQPainter を設定し、ヘルパーのpaint() 関数を呼び出すだけで、ウィジェットの内容を表示できます。

Windowクラスの定義

Window クラスの定義は、基本的かつ最小限のものとなっています:

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private:
    Helper helper;
};

このクラスには、すべてのウィジェット間で共有される単一のHelper オブジェクトが含まれています。

Windowクラスの実装

コンストラクタがすべての処理を行い、各タイプのウィジェットを作成し、ラベルとともにレイアウトに挿入します:

Window::Window()
{
    setWindowTitle(tr("2D Painting on Native and OpenGL Widgets"));

    Widget *native = new Widget(&helper, this);
    GLWidget *openGL = new GLWidget(&helper, this);
    QLabel *nativeLabel = new QLabel(tr("Native"));
    nativeLabel->setAlignment(Qt::AlignHCenter);
    QLabel *openGLLabel = new QLabel(tr("OpenGL"));
    openGLLabel->setAlignment(Qt::AlignHCenter);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(native, 0, 0);
    layout->addWidget(openGL, 0, 1);
    layout->addWidget(nativeLabel, 1, 0);
    layout->addWidget(openGLLabel, 1, 1);
    setLayout(layout);

    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, native, &Widget::animate);
    connect(timer, &QTimer::timeout, openGL, &GLWidget::animate);
    timer->start(50);
}

アニメーション処理のために、タイムアウトが50ミリ秒のタイマーが生成され、Widget およびGLWidget オブジェクトのanimate() スロットに接続されます。起動されると、ウィジェットは毎秒約20フレームのペースで更新されるはずです。

例の動作

この例では、Widget とGLWidget で、同じ描画処理が同時に実行される様子を示しています。GLWidget でのレンダリングの品質と速度は、お使いのシステムのOpenGLドライバが提供するマルチサンプリングおよびハードウェアアクセラレーションのサポートレベルに依存します。これらのいずれかのサポートが不足している場合、ドライバは品質を犠牲にして速度を優先するソフトウェアレンダラーに切り替わる可能性があります。

code.qt.io のサンプルプロジェクト

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.