我最近遇到了完全相同的问题,尽管我是设计设备、固件和通信协议的人。
我认为必须使用模型/视图来保持理智。
我将所有变量作为派生自QAbstractTableModel
. 那是因为有固定数量的简单参数(行),并且每个设备(列)都相同。不过,很快,我将不得不转向树模型,因为某些参数内部结构为列表、向量或矩阵,将它们直接暴露给视图会很有帮助,而不仅仅是格式化字符串.
模型类也有一些方便的 getter/setter,这样你就不必通过它们的行/列来引用参数。通过 a 进行的行/列访问QModelIndex
仅供视图使用。
我选择将 UserRole 用于直接表示的值(通常是 SI 单位的两倍),并使用 Display 和 Edit 角色将格式化/缩放的数据呈现给小部件。
对于非视图控件,需要一个活页夹对象。QDataWidgetMapper
由 Qt 提供,您最好使用它。
前段时间我没有注意到有小部件映射器,所以我编写了一个自定义绑定对象(从 QObject 派生),它为每个 GUI 控件实例化,以将模型的某个索引绑定到非视图 Qt控制小部件。另一种方法是使用QListView
s,每个视图都有一个代理模型,只公开一个元素,并正确分配委托。不过,这会带来很多开销。binder 对象方法非常轻量级。
模型视图方法还使人们能够轻松地分解出每个控件的最新指示。
当应用程序第一次启动时,模型可以指示(通过专用角色)这些值是无效的。这可以在控件上放置一个 x-cross 或理发杆,以清楚地指示那里没有有效值。
当设备处于活动状态并且用户修改控件时,不同的角色可以指示模型中的值已更改,但尚未传播到设备。
当设备通信代码从模型中获取更改并将其提交给设备时,它可以告诉模型,视图(实际上是 biner)将自动获取并更新控件。
向模型添加Model * clone() const
方法,或添加序列化/反序列化操作符,可以让您轻松拍摄模型的快照,并实现 Undo/Redo、Commit/Revert 等。
活页夹的相关片段在这里:
// 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();
}