以下は完全な例で、長さは 71 行で、文芸的なプログラミング スタイルで示されています。githubでも入手できます。この例は、表示されていない qmake.pro
ファイルとmain.cpp
、以下に全体を示す で構成されています。この例の構造は次のとおりです。
- ヘッダ
- カードアイテム
- ステート マシンの動作
- 主要
- フッター

主要
まず、シーンを設定しましょう。
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QGraphicsScene scene;
QGraphicsView view{&scene};
scene.addItem(new CardItem(0, 0, "A"));
scene.addItem(new CardItem(20, 0, "B"));
ステート マシンには次の 3 つの状態があります。
QStateMachine machine;
QState s_idle{&machine}; // idle - no card selected
QState s_selected{&machine}; // card selected, waiting 1/2 second
QState s_ready{&machine}; // ready with card selected
machine.setInitialState(&s_idle);
ヘルパー関数を使用して、マシンに動作を宣言的に追加します。これは唯一の可能なパターンではありませんが、機能し、かなり簡単に適用できます。まず、アイテムが選択されると、状態が からs_idle
に変わりs_selected
ます。
on_selected(&s_idle, &scene, true, &s_selected);
次に、タイムアウトの後、状態が次のように変わりますs_ready
。
on_delay(&s_selected, 500, &s_ready);
アイテムの選択が解除された場合は、次のように戻りますs_idle
。
on_selected(&s_selected, &scene, false, &s_idle);
on_selected(&s_ready, &scene, false, &s_idle);
やるべきことはあまりないので、s_ready
状態に入ったら、単純にすべてのアイテムの選択を解除できます。これにより、状態が入力されたことが明確になります。もちろん、選択が解除されるとすぐに残りますが、上で示しs_idle
たのは項目が選択されていないときの状態です。
QObject::connect(&s_ready, &QState::entered, &scene, &QGraphicsScene::clearSelection);
これで、マシンを起動してアプリケーションを実行できます。
machine.start();
view.show();
return app.exec();
}
明示的な動的メモリ割り当ての使用が最小限であり、手動のメモリ管理がまったくないことに注意してください。
カードアイテム
クラスはCardItem
単純なカード グラフィック アイテムです。項目は選択可能です。可動することもあります。インタラクションは、グラフィック ビュー フレームワークによって自動的に処理されます。マウスのプレス/ドラッグ/リリースを手動で解釈する必要はありません。少なくともまだです。
class CardItem : public QGraphicsObject {
Q_OBJECT
const QRect cardRect { 0, 0, 80, 120 };
QString m_text;
QRectF boundingRect() const Q_DECL_OVERRIDE { return cardRect; }
void paint(QPainter * p, const QStyleOptionGraphicsItem*, QWidget*) {
p->setRenderHint(QPainter::Antialiasing);
p->setPen(Qt::black);
p->setBrush(isSelected() ? Qt::gray : Qt::white);
p->drawRoundRect(cardRect.adjusted(0, 0, -1, -1), 10, 10);
p->setFont(QFont("Helvetica", 20));
p->drawText(cardRect.adjusted(3,3,-3,-3), m_text);
}
public:
CardItem(qreal x, qreal y, const QString & text) : m_text(text) {
moveBy(x, y);
setFlags(QGraphicsItem::ItemIsSelectable);
}
};
ステート マシンの動作
ステート マシンの動作を、特定の状態での動作を宣言するために使用できる関数に分解すると便利です。
まず、遅延 -src
状態に入り、指定されたミリ秒数が経過すると、マシンは目的の状態に移行します。
void on_delay(QState * src, int ms, QAbstractState * dst) {
auto timer = new QTimer(src);
timer->setSingleShot(true);
timer->setInterval(ms);
QObject::connect(src, &QState::entered, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(src, &QState::exited, timer, &QTimer::stop);
src->addTransition(timer, SIGNAL(timeout()), dst);
}
選択シグナルをインターセプトするには、一般的なシグナルを発するヘルパー クラスが必要です。
class SignalSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void sig();
SignalSource(QObject * parent = Q_NULLPTR) : QObject(parent) {}
};
次に、このようなユニバーサル信号ソースを利用して、指定されたシーンにselected
true の場合に選択がある場合、またはselected
falseの場合に選択がない場合に、目的の状態に遷移する動作を記述します。
void on_selected(QState * src, QGraphicsScene * scene, bool selected, QAbstractState * dst) {
auto signalSource = new SignalSource(src);
QObject::connect(scene, &QGraphicsScene::selectionChanged, signalSource, [=] {
if (scene->selectedItems().isEmpty() == !selected) emit signalSource->sig();
});
src->addTransition(signalSource, SIGNAL(sig()), dst);
}
ヘッダーとフッター
この例は、次のヘッダーで始まります。
// https://github.com/KubaO/stackoverflown/tree/master/questions/sm-cards-37656060
#include <QtWidgets>
次のフッターで終了します。これは、moc によって生成されたシグナルの実装と、SignalSource
クラスのオブジェクト メタデータで構成されます。
#include "main.moc"