このページでは

アナログ時計

「アナログ時計」のサンプルでは、カスタムウィジェットの内容を描画する方法を示しています。

時針、分針、秒針を備えたアナログ時計

「アナログ時計」例のスクリーンショット

また、このサンプルでは、QPainter の変形および拡大縮小機能を利用して、カスタムウィジェットの描画を容易にする方法についても説明しています。

AnalogClock クラスの定義

AnalogClock クラスは、時針、分針、秒針を備え、1秒ごとに自動的に更新される時計ウィジェットを提供します。QWidget をサブクラス化し、標準のpaintEvent()関数を再実装して、文字盤を描画します:

class AnalogClock : public QWidget
{
    Q_OBJECT

public:
    AnalogClock(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
};

AnalogClock クラスの実装

ウィジェットの生成時に、現在の時刻を追跡するための1秒単位のタイマーを設定し、これを標準のupdate()スロットに接続します。これにより、タイマーがtimeout()シグナルを発生させた際に、時計の文字盤が更新されるようになります。最後に、ウィジェットが適切なサイズで表示されるよう、サイズ調整を行います。

AnalogClock::AnalogClock(QWidget *parent)
    : QWidget(parent)
{
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
    timer->start(1000);

    setWindowTitle(tr("Analog Clock"));
    resize(200, 200);
}

paintEvent() 関数は、ウィジェットのコンテンツを更新する必要があるたびに呼び出されます。これは、ウィジェットが最初に表示されたときや、非表示になった後に再び表示されたときに発生しますが、ウィジェットのupdate()スロットが呼び出されたときにも実行されます。タイマーのtimeout()シグナルをこのスロットに接続しているため、この関数は少なくとも1秒に1回は呼び出されます。

ペインターを設定して時計を描画する前に、まず、時針、分針、秒針に使用する3つのQPointリストと3つのQColorを定義します。palette() 関数を使用して、ライトモードとダークモードの両方で、ウィンドウの他の部分と調和する適切な色を取得します。時針と分針は前景色で描画され、秒針はアクセント色で描画されます。

また、時計の文字盤をウィジェット内に収めるために、ウィジェットの最短辺の長さを算出します。描画を開始する前に現在の時刻を確認しておくことも有用です。

void AnalogClock::paintEvent(QPaintEvent *)
{
    static const QPoint hourHand[4] = {
        QPoint(5, 14),
        QPoint(-5, 14),
        QPoint(-4, -71),
        QPoint(4, -71)
    };
    static const QPoint minuteHand[4] = {
        QPoint(4, 14),
        QPoint(-4, 14),
        QPoint(-3, -89),
        QPoint(3, -89)
    };

    static const QPoint secondsHand[4] = {
       QPoint(1, 14),
       QPoint(-1, 14),
       QPoint(-1, -89),
       QPoint(1, -89)
    };

    const QColor hourColor(palette().color(QPalette::Text));
    const QColor minuteColor(palette().color(QPalette::Text));
    const QColor secondsColor(palette().color(QPalette::Accent));

    int side = qMin(width(), height());

カスタムウィジェットの内容は、QPainter を使用して描画されます。ペインターは任意のQPaintDevice 上で描画するために使用できますが、通常はウィジェットと共に使用されるため、ウィジェットのインスタンスをペインターのコンストラクタに渡します。

    QPainter painter(this);
    QTime time = QTime::currentTime();

アンチエイリアシングを有効にするために、`QPainter::setRenderHint()` を `QPainter::Antialiasing ` 引数と共に呼び出します。これにより、斜めの線の描画がはるかに滑らかになります。

    painter.setRenderHint(QPainter::Antialiasing);

平行移動によって原点をウィジェットの中心に移動させ、拡大縮小操作を行うことで、その後の描画操作がウィジェット内に収まるようにします。ここで使用する拡大縮小倍率により、x 座標と y 座標を -100 から 100 の範囲で指定できるようになり、これらがウィジェットの最短辺の長さの範囲内に収まることが保証されます。

    painter.translate(width() / 2, height() / 2);
    painter.scale(side / 200.0, side / 200.0);

コードをシンプルにするため、ウィジェットの中心に収まるように位置とスケールを調整した、固定サイズの時計の文字盤を描画します。

ペインターは、ペイントイベント中に実行されるすべての変換を処理し、すべてが正しく描画されるようにします。カスタムウィジェットのコンテンツを描画するためだけに手動で計算を行うよりも、ペインターに変換処理を任せることが多くの場合、より簡単です。

文字盤上にウィジェットの座標系と原点を表示する

輪郭線を表示したくないため、ペンを `Qt::NoPen ` に設定し、時間を表示するのに適した色のソリッドブラシを使用します。ブラシは、多角形やその他の幾何学的形状を塗りつぶす際に使用されます。

    painter.setPen(Qt::NoPen);
    painter.setBrush(hourColor);

まず、現在の時と分によって決定される角度だけ座標系を反時計回りに回転させる式を使用して、時針を描画します。これにより、時針は必要な分だけ時計回りに回転した状態で表示されます。 以前の回転を考慮せずに分針を配置したいので、QPainterStateGuard をインスタンス化して、回転前後の変換行列を保存・復元します。

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
        painter.drawConvexPolygon(hourHand, 4);
    }

時計の縁に沿って、各時間ごとに時針と同じ色の目盛りを描画します。各目盛りを描画した後、座標系を回転させて、ペインターが次の目盛りの描画準備ができるようにします。

    for (int i = 0; i < 12; ++i) {
        painter.drawRect(73, -3, 16, 6);
        painter.rotate(30.0);
    }

分針は、時針と同様の方法で回転させ、描画します。

    painter.setBrush(minuteColor);

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.minute());
        painter.drawConvexPolygon(minuteHand, 4);
    }

秒針についても同様の処理を行い、視覚的なアクセントとして2つの円を追加します。

    painter.setBrush(secondsColor);

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.second());
        painter.drawConvexPolygon(secondsHand, 4);
        painter.drawEllipse(-3, -3, 6, 6);
        painter.drawEllipse(-5, -68, 10, 10);
    }

最後に、時計の縁に沿って分と秒を示す目盛りを描画します。今回は線を引くため、ペンをそれぞれの色に設定します。

    painter.setPen(minuteColor);

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

サンプルプロジェクト @ 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.