映射多变量/多层系统的一般化 [英] generalization of mapping a multi-variable/layer system

查看:56
本文介绍了映射多变量/多层系统的一般化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用 C ++ / QT 编写了一个应用程序,该程序与设备进行通信以读取/写入其变量,将其放入/存储在结构中,然后将它们显示在gui中出于查看/编辑的目的.

I wrote an application with C++ / QT that communicates with a device to read/write its variables, puts/gets them in a struct and presents them in a gui for both viewing/editing purposes.

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

1) The device comes with a sample c code that also defines the communication protocol (in a very bad way) like:

#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

因此变量 VAR_1 表示为: MY_DEVICE_VAR_1 .

2):我有一个可容纳设备所有变量的结构:

2) I've got a struct that holds all variables of the device:

struct MyDeviceData
{
    int var1;
    double var2;
    char var3;
    bool var4;
        .
        .
};

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

It's basically a storage for / a projection of the data read from the device. There are 4 different types of POD variables.

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

3) Finally my gui has gui elements to show and edit the instance of MyDeviceData

class MyGuI
{
    QLineEdit var1;
    QLineEdit var2;
    QComboBox var3;
    QCheckBox var4;
       .
       .
};

现在我的问题是:

1)我正在映射 MY_DEVICE_VAR1 -> MyDeviceData :: var1 -> MyGUI :: var1 if switch/case 语句,我对此并不感到骄傲.哪种更好的编程"方式进行映射?

1) I'm doing the mapping of MY_DEVICE_VAR1 -> MyDeviceData::var1 -> MyGUI::var1 with if and switch/case statements which I'm not proud with. What would be a better "programmatic" way to do the mapping?

2)当gui元素的值更改时,我想将更新后的值发送到卡中.除了覆盖诸如" textChanged selectedIndexChanged "等事件的处理函数外,还有更智能"的方法吗?(QSignalMapper?)

2) When the value of a gui element gets changed, I want to send only updated value to the card. besides overriding the handler functions of events like "textChanged, selectedIndexChanged" etc. Are there any "smarter" methods? (QSignalMapper?)

3)在这种项目中,是否可以概括整个繁琐的工作?(代码生成器工具?模板?)

3) In this kind of project, is it possible to generalize whole drudge work? (a code-generator tool? templates?)

推荐答案

尽管我是设计设备,固件和通信协议的人,但我最近也面临着完全相同的问题.

I have recently faced exactly the same problem, although I was the one designing the device, its firmware, and the communication protocol as well.

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

I think that one must use model/view to keep one's sanity.

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

I have all the variables as elements in a data model class deriving from QAbstractTableModel. That's because there's a fixed number of simple parameters (rows), and they are the same per each device (column). Quite soon, though, I'll have to move to a tree model, because some parameters internally are structured as lists, vectors or matrices, and it'd be helpful to expose them to views directly as such, and not merely as formatted strings.

model类还具有一些便利的getter/setter方法,因此您不必通过其行/列来引用参数.通过 QModelIndex 进行的行/列访问仅由视图使用.

The model class also has some convenience getters/setters so that you don't have to refer to the parameters by their row/column. The row/column access via a QModelIndex is only for use by the views.

我选择将UserRole用于直接表示的值(通常以SI单位加倍),并使用Display和Edit角色将格式化/缩放的数据呈现给小部件.

I chose to use the UserRole for directly represented values (mostly doubles in SI units), and Display and Edit roles to present formatted/scaled data to the widgets.

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

For non-view controls, one needs a binder object. QDataWidgetMapper is provided by Qt, and you should ideally use it.

前一段时间我没有注意到有小部件映射器,所以我编写了一个自定义的绑定对象(源自QObject),该绑定对象针对每个GUI控件实例化,以将模型的某个索引绑定到非绑定对象.-查看Qt控件小部件.另一种方法是使用 QListView ,在每个视图中都有一个仅显示一个元素的代理模型,并适当地分配委托.但是,这会带来很多开销.活页夹对象方法非常轻巧.

A while ago I didn't notice that there was the widget mapper, so I wrote custom a binder object (deriving from QObject), that gets instantiated for each GUI control, to bind a certain index of the model to a non-view Qt control widget. Another approach would be to use QListViews, have a proxy model per each view that exposes just one element, and properly assign delegates. This would introduce a lot of overhead, though. The binder object approach is quite lightweight.

模型视图方法还使人们能够轻松排除每个控件的最新指示.

The model-view approach also enables one to easily factor out the up-to-dateness indication of each control.

  • 在首次启动应用程序时,模型可以(通过专用角色)指示这些值无效.这样可以在控件上放一个x叉或理发杆,以清楚地指示那里没有有效值.

  • When the application is first started up, the model can indicate (via a dedicated role), that the values are invalid. This can put an x-cross or barber pole on the control to clearly indicate that there is no valid value there.

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

When the device is active, and the user modifies a control, a different role can indicate that the value was changed in the model, but not propagated to the device yet.

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

When the device communications code picks up the change from the model and commits it to the device, it can tell the model about it, and the view (the biner, really) will automatically pick it up and update the control.

向模型添加 Model * clone()const 方法,或添加序列化/反序列化运算符,可让您轻松获取模型快照,并实现撤消/重做,提交/还原等

Adding a Model * clone() const method to the model, or adding serialization/deserialization operators, allows you to trivially take snapshots of the model, and implement Undo/Redo, Commit/Revert, etc.

活页夹的相关代码段在这里:

Relevant snippets of the binder are here:

// 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();
}

这篇关于映射多变量/多层系统的一般化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆