映射多变量/层系统的概括

sit*_*eal 5 c++ user-interface serialization qt

我用C++ / QT编写了一个应用程序,它与设备通信以读取/写入其变量,将它们放入/获取结构中,并在 gui 中呈现它们以用于查看/编辑目的。

1)该设备附带了一个示例 c 代码,该代码还定义了通信协议(以非常糟糕的方式),例如:

#define VALUE_1 0x12345678
#define VALUE_2 0xDEADBEEF
#define MY_DEVICE_VAR (VALUE_1 << 4) & (VALUE_2 >> 6)
#define MY_DEVICE_VAR_1 (MY_DEVICE_VAR & (VALUE_1 << 2)
#define MY_DEVICE_VAR_2 (MY_DEVICE_VAR & (VALUE_2 << 4)
#define MY_DEVICE_VAR_2 (MY_DEVICE_VAR & (VALUE_2 << 4)
// .. and 300 more lines like above
Run Code Online (Sandbox Code Playgroud)

因此该变量VAR_1表示为:MY_DEVICE_VAR_1

2)我有一个保存设备所有变量的结构:

struct MyDeviceData
{
    int var1;
    double var2;
    char var3;
    bool var4;
        .
        .
};
Run Code Online (Sandbox Code Playgroud)

它基本上是从设备读取的数据的存储/投影。有 4 种不同类型的 POD 变量。

3)最后我的 gui 有 gui 元素来显示和编辑 MyDeviceData 的实例

class MyGuI
{
    QLineEdit var1;
    QLineEdit var2;
    QComboBox var3;
    QCheckBox var4;
       .
       .
};
Run Code Online (Sandbox Code Playgroud)

现在我的问题是:

1)MY_DEVICE_VAR1我正在做-> MyDeviceData::var1-> MyGUI::var1withif和语句的映射switch/case,但我对此并不感到自豪。什么是更好的“编程”方式来进行映射?

2)当 gui 元素的值发生更改时,我只想将更新后的值发送到卡。textChanged除了重写“ ” 、 “ ”等事件的处理函数之外,selectedIndexChanged还有什么“更智能”的方法吗?(QSignalMapper?)

3)在这种项目中,是否可以推广整个苦差事?(代码生成器工具?模板?)

Rei*_*ica 2

我最近遇到了完全相同的问题,尽管我也是设计该设备、其固件和通信协议的人。

我认为必须使用模型/视图来保持理智。

我将所有变量作为派生自 的数据模型类中的元素QAbstractTableModel。这是因为简单参数(行)的数量是固定的,并且每个设备(列)的参数都是相同的。不过,很快我将不得不转向树模型,因为一些参数内部被构造为列表、向量或矩阵,并且将它们直接公开给视图会很有帮助,而不仅仅是作为格式化字符串。

模型类还有一些方便的 getter/setter,这样您就不必按行/列引用参数。通过 a 进行的行/列访问QModelIndex仅供视图使用。

我选择使用 UserRole 来直接表示值(主要是 SI 单位的双倍),并使用 Display 和 Edit 角色来向小部件呈现格式化/缩放的数据。

对于非视图控件,需要一个活页夹对象。QDataWidgetMapper由 Qt 提供,您最好使用它。

不久前,我没有注意到有小部件映射器,所以我编写了自定义的绑定器对象(从 QObject 派生),该对象为每个 GUI 控件进行实例化,以将模型的某个索引绑定到非视图 Qt控制小部件。另一种方法是使用QListViews,为每个视图提供一个仅公开一个元素的代理模型,并正确分配委托。但这会带来大量开销。活页夹对象方法是相当轻量级的。

模型视图方法还使人们能够轻松地找出每个控件的最新指示。

  • 当应用程序首次启动时,模型可以(通过专用角色)指示这些值无效。这可以在控件上放置一个 X 十字或理发杆,以清楚地表明那里没有有效值。

  • 当设备处于活动状态并且用户修改控件时,不同的角色可以指示模型中的值已更改,但尚未传播到设备。

  • 当设备通信代码从模型中获取更改并将其提交给设备时,它可以告诉模型相关信息,并且视图(实际上是 biner)将自动获取它并更新控件。

  • 向模型添加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();
}
Run Code Online (Sandbox Code Playgroud)