タイマーを使ってQt Widgetsでサブウィンドウを動かす:QMdiSubWindow::timerEvent()の応用例


例:ウィジェットの揺れアニメーション

以下は、QMdiSubWindow を左右に揺らすアニメーションを作成する例です。

class WigglyWindow : public QMDISubWindow {
public:
    WigglyWindow(QWidget *parent = nullptr);

protected:
    void timerEvent(QTimerEvent *event) override;

private:
    int m_angle; // 現在の角度
};

WigglyWindow::WigglyWindow(QWidget *parent) : QMDISubWindow(parent) {
    m_angle = 0;

    // タイマーを作成し、10ミリ秒ごとに期限切れになるように設定
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &WigglyWindow::timerEvent);
    timer->start(10);
}

void WigglyWindow::timerEvent(QTimerEvent *event) {
    // 角度を更新
    m_angle = (m_angle + 5) % 360;

    // ウィジェットを回転
    setContentsMargins(m_angle, 0, 0, 0);
}

この例では、timerEvent() メソッド内で m_angle 変数を5度ずつ更新しています。その後、setContentsMargins() メソッドを使用して、ウィジェットを m_angle 度回転させています。これにより、ウィジェットが左右に揺れるアニメーションが作成されます。

QMdiSubWindow::timerEvent() メソッドは、さまざまなアニメーションや時間依存タスクに使用できます。以下は、いくつかの例です。

  • カスタムグラフィックエフェクトの適用
  • ウィジェットの位置変更
  • ウィジェットのサイズ変更
  • ウィジェットのフェードイン/フェードアウト


ウィジェットのフェードイン/フェードアウト

class FadeWindow : public QMDISubWindow {
public:
    FadeWindow(QWidget *parent = nullptr);

protected:
    void timerEvent(QTimerEvent *event) override;

private:
    int m_opacity; // 現在の不透明度
};

FadeWindow::FadeWindow(QWidget *parent) : QMDISubWindow(parent) {
    m_opacity = 0;

    // タイマーを作成し、25ミリ秒ごとに期限切れになるように設定
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &FadeWindow::timerEvent);
    timer->start(25);
}

void FadeWindow::timerEvent(QTimerEvent *event) {
    // 不透明度を更新
    if (m_opacity < 255) {
        m_opacity += 5;
    } else {
        m_opacity = 0;
    }

    // ウィジェットの不透明度を設定
    setWindowOpacity(m_opacity / 255.0);
}

ウィジェットのサイズ変更

class ResizeWindow : public QMDISubWindow {
public:
    ResizeWindow(QWidget *parent = nullptr);

protected:
    void timerEvent(QTimerEvent *event) override;

private:
    int m_width; // 現在の幅
    int m_height; // 現在の高さ
};

ResizeWindow::ResizeWindow(QWidget *parent) : QMDISubWindow(parent) {
    m_width = 100;
    m_height = 100;

    // タイマーを作成し、50ミリ秒ごとに期限切れになるように設定
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &ResizeWindow::timerEvent);
    timer->start(50);
}

void ResizeWindow::timerEvent(QTimerEvent *event) {
    // サイズを更新
    if (m_width < 400 && m_height < 400) {
        m_width += 10;
        m_height += 10;
    } else {
        m_width = 100;
        m_height = 100;
    }

    // ウィジェットのサイズを設定
    resize(m_width, m_height);
}

この例では、timerEvent() メソッド内で m_widthm_height 変数を10ずつ増減しています。その後、resize() メソッドを使用して、ウィジェットのサイズを設定しています。これにより、ウィジェットが徐々に拡大縮小するアニメーションが作成されます。

class MoveWindow : public QMDISubWindow {
public:
    MoveWindow(QWidget *parent = nullptr);

protected:
    void timerEvent(QTimerEvent *event) override;

private:
    int m_x; // 現在の X 座標
    int m_y; // 現在の Y 座標
};

MoveWindow::MoveWindow(QWidget *parent) : QMDISubWindow(parent) {
    m_x = 0;
    m_y = 0;

    // タイマーを作成し、30ミリ秒ごとに期限切れになるように設定
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &MoveWindow::timerEvent);
    timer->start(30);
}

void MoveWindow::timerEvent(QTimerEvent *event) {
    // 位置を更新
    if (m_x < 200 && m_y < 200) {
        m_x += 5;
        m_y += 5;
    } else {
        m_x = 0;
        m_y = 0;
    }

    // ウィジェットの位置を設定
    move(m_x, m_y);
}


QPropertyAnimation を使用する

QPropertyAnimation は、Qt アニメーションフレームワークの一部であり、ウィジェットのプロパティをアニメーション化するための使いやすい方法を提供します。QMdiSubWindow の位置、サイズ、不透明度などのプロパティをアニメーション化するために使用できます。

QPropertyAnimation *animation = new QPropertyAnimation(window, "geometry");
animation->setDuration(1000); // アニメーションの長さ (ミリ秒)
animation->setStartValue(window->geometry()); // 開始値
animation->setEndValue(QRect(200, 100, 300, 200)); // 終了値
animation->start();

この例では、window ウィジェットの位置を (200, 100) から (300, 200) に 1 秒間かけてアニメーション化しています。

QStateMachine を使用する

QStateMachine は、複雑なアニメーションやステートマシンベースのロジックをモデル化するためのフレームワークです。複数のアニメーションを組み合わせたり、条件分岐などのロジックを組み込んだりすることができます。

QStateMachine *machine = new QStateMachine(window);

QState *initialState = new QState(machine);
QState *finalState = new QState(machine);

initialState->addTransition(QEvent::TimerEvent, finalState, 1000); // 1 秒後に finalState に遷移

finalState->addTransition(QEvent::TimerEvent, initialState, 1000); // 1 秒後に initialState に遷移

machine->addState(initialState);
machine->addState(finalState);

machine->setInitialState(initialState);
machine->start();

この例では、window ウィジェットの位置を 1 秒間隔で (200, 100) と (300, 200) の間で切り替えます。

カスタムタイマーを実装する

QTimerEvent を使用せずに、独自のタイマーイベントを処理することもできます。これにより、よりきめ細かな制御が可能になりますが、コードが複雑になる可能性があります。

class MyWindow : public QMDISubWindow {
public:
    MyWindow(QWidget *parent = nullptr);

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

private:
    int m_angle; // 現在の角度
};

MyWindow::MyWindow(QWidget *parent) : QMDISubWindow(parent) {
    m_angle = 0;

    // タイマーを開始
    startTimer(10); // 10ミリ秒ごとに timer() メソッドを呼び出す
}

void MyWindow::paintEvent(QPaintEvent *event) {
    QPainter painter(this);

    // ウィジェットを回転
    painter.translate(width() / 2, height() / 2);
    painter.rotate(m_angle);

    // ウィジェットの内容を描画
    // ...
}

void MyWindow::timer() {
    // 角度を更新
    m_angle = (m_angle + 5) % 360;

    // ウィジェットを再描画
    update();
}