私は最近、まったく同じ問題に直面しましたが、デバイス、そのファームウェア、および通信プロトコルも設計しました。
正気を保つためにはモデル/ビューを使わなければならないと思います。
から派生したデータモデルクラスの要素としてすべての変数がありますQAbstractTableModel
。これは、単純なパラメーター(行)の数が固定されており、各デバイス(列)ごとに同じであるためです。ただし、すぐにツリーモデルに移行する必要があります。これは、一部のパラメーターが内部的にリスト、ベクトル、またはマトリックスとして構造化されているためです。これらのパラメーターは、フォーマットされた文字列としてだけでなく、直接ビューに公開すると便利です。 。
モデルクラスには便利なゲッター/セッターもいくつかあるので、行/列でパラメーターを参照する必要はありません。aを介した行/列へのアクセスQModelIndex
は、ビューのみが使用できます。
直接表現された値(ほとんどの場合、SI単位で2倍)にUserRoleを使用し、フォーマット/スケーリングされたデータをウィジェットに表示するために表示および編集の役割を使用することを選択しました。
ビュー以外のコントロールの場合、バインダーオブジェクトが必要です。QDataWidgetMapper
Qtによって提供され、理想的にはそれを使用する必要があります。
しばらく前、ウィジェットマッパーがあることに気づかなかったので、GUIコントロールごとにインスタンス化されるカスタムバインダーオブジェクト(QObjectから派生)を作成して、モデルの特定のインデックスを非ビューQtにバインドしました。コントロールウィジェット。もう1つのアプローチは、を使用しQListView
、ビューごとに1つの要素のみを公開するプロキシモデルを用意し、デリゲートを適切に割り当てることです。ただし、これにより多くのオーバーヘッドが発生します。バインダーオブジェクトのアプローチは非常に軽量です。
モデルビューアプローチにより、各コントロールの最新の表示を簡単に除外することもできます。
アプリケーションが最初に起動されたとき、モデルは(専用の役割を介して)値が無効であることを示すことができます。これにより、コントロールにXクロスまたは理髪店の看板ポールを配置して、そこに有効な値がないことを明確に示すことができます。
デバイスがアクティブで、ユーザーがコントロールを変更すると、別の役割で、モデルで値が変更されたが、まだデバイスに伝達されていないことを示すことができます。
デバイス通信コードがモデルから変更を取得してデバイスにコミットすると、モデルにその変更を通知でき、ビュー(実際にはバイナー)が自動的に取得してコントロールを更新します。
モデルにメソッドを追加するModel * clone() const
か、シリアル化/逆シリアル化演算子を追加すると、モデルのスナップショットを簡単に作成し、元に戻す/やり直し、コミット/元に戻すなどを実装できます。
バインダーの関連スニペットは次のとおりです。
// constructor
Binder::Binder(QAbstractItemModel * model_, const QModelIndex & index_, QObject * object) :
QObject(object),
model(model_),
index(index_),
sourceRole(Qt::DisplayRole),
property(""),
target(object),
lockout(false)
{
Q_ASSERT(index.isValid());
// replicate for each type of control
if (qobject_cast<QDoubleSpinBox*>(object)) {
connect(object, SIGNAL(valueChanged(double)), SLOT(doubleSpinBoxGet(double)));
connect(index.model(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(doubleSpinBoxSet(QModelIndex, QModelIndex)));
}
else if (....)
}
// getter/setter for QDoubleSpinBox
void Binder::doubleSpinBoxGet(double val)
{
if (lockout) return;
QScopedValueRollback<bool> r(lockout);
lockout = true;
model->setData(index, val, sourceRole);
}
void Binder::doubleSpinBoxSet(const QModelIndex & tl, const QModelIndex & br)
{
if (lockout) return;
if (! isTarget(tl, br)) return;
QScopedValueRollback<bool> r(lockout);
lockout = true;
if (! index.data().canConvert<double>()) return;
qobject_cast<QDoubleSpinBox*>(target)->setValue(index.data(sourceRole).toDouble());
}
// helper
bool Binder::isTarget(const QModelIndex & topLeft, const QModelIndex & bottomRight)
{
return topLeft.parent() == bottomRight.parent()
&& topLeft.parent() == index.parent()
&& topLeft.row() <= index.row()
&& topLeft.column() <= index.column()
&& bottomRight.row() >= index.row()
&& bottomRight.column() >= index.column();
}