Qt 是目前最先进、最完整的跨平台C++开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。
Combo Widget Mapper(组合小部件映射器)示例展示了如何使用自定义委托将信息从模型映射到表单上的特定小部件。
在简单小部件映射器(Simple Widget Mapper Example)示例中,我们展示了小部件映射器的基本用法,将模型公开的数据与用户界面中的简单输入小部件联系起来。然而有时我们希望使用输入小部件将数据作为选项公开给用户,例如QComboBox,并且需要一种方法将用户的输入与存储在模型中的值关联起来。
这个示例与简单部件映射器示例非常相似,同样创建了一个具有几乎相同用户界面的Window类,除了提供一个旋转框以便输入每个人的年龄之外,还提供了一个组合框来允许他们的地址被分类为“Home”、“Work”或“Other”。
点击获取Qt Widget组件下载(Q技术交流:166830288)
Window类定义
这个类提供了一个构造函数,一个插槽来保持按钮的更新,以及一个私有函数来设置模型:
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = nullptr);
private slots:
void updateButtons(int row);
private:
void setupModel();
QLabel *nameLabel;
QLabel *addressLabel;
QLabel *typeLabel;
QLineEdit *nameEdit;
QTextEdit *addressEdit;
QComboBox *typeComboBox;
QPushButton *nextButton;
QPushButton *previousButton;
QStandardItemModel *model;
QStringListModel *typeModel;
QDataWidgetMapper *mapper;
};
除了QDataWidgetMapper对象和用于组成用户界面的控件之外,我们还使用QStandardItemModel保存数据,使用QStringListModel保存关于可以应用于每个人的数据的地址类型的信息。
Window类实现
Window类的构造函数可以分三部分解释,在第一部分中,我们设置了用于用户界面的小部件:
Window::Window(QWidget *parent)
: QWidget(parent)
{
setupModel();
nameLabel = new QLabel(tr("Na&me:"));
nameEdit = new QLineEdit();
addressLabel = new QLabel(tr("&Address:"));
addressEdit = new QTextEdit();
typeLabel = new QLabel(tr("&Type:"));
typeComboBox = new QComboBox();
nextButton = new QPushButton(tr("&Next"));
previousButton = new QPushButton(tr("&Previous"));
nameLabel->setBuddy(nameEdit);
addressLabel->setBuddy(addressEdit);
typeLabel->setBuddy(typeComboBox);
typeComboBox->setModel(typeModel);
请注意,我们以与其他小部件相同的方式设置组合框映射,但是将组合框自己的模型应用到组合框上,以便组合框将显示来自它自己的模型typeModel的数据,替代来自包含关于每个人的数据的模型。
接下来,我们设置小部件映射器,将每个输入小部件与调用setModel()指定的模型中的列关联起来:
mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->addMapping(nameEdit, 0);
mapper->addMapping(addressEdit, 1);
mapper->addMapping(typeComboBox, 2, "currentIndex");
对于组合框,我们传递一个额外的参数来告诉小部件映射器哪个属性与模型中的值相关。因此,用户能够从组合框中选择一个项目,存储在小部件currentIndex属性中的相应值将存储在模型中。
构造函数的其余部分非常类似于简单部件映射器示例:
connect(previousButton, &QAbstractButton::clicked,
mapper, &QDataWidgetMapper::toPrevious);
connect(nextButton, &QAbstractButton::clicked,
mapper, &QDataWidgetMapper::toNext);
connect(mapper, &QDataWidgetMapper::currentIndexChanged,
this, &Window::updateButtons);
QGridLayout *layout = new QGridLayout();
layout->addWidget(nameLabel, 0, 0, 1, 1);
layout->addWidget(nameEdit, 0, 1, 1, 1);
layout->addWidget(previousButton, 0, 2, 1, 1);
layout->addWidget(addressLabel, 1, 0, 1, 1);
layout->addWidget(addressEdit, 1, 1, 2, 1);
layout->addWidget(nextButton, 1, 2, 1, 1);
layout->addWidget(typeLabel, 3, 0, 1, 1);
layout->addWidget(typeComboBox, 3, 1, 1, 1);
setLayout(layout);
setWindowTitle(tr("Delegate Widget Mapper"));
mapper->toFirst();
}
模型在窗口的setupModel()函数中初始化,在这里,我们创建了一个5行3列的标准模型。在每一行中插入一个名称、地址和一个指示地址类型的值,地址类型存储在字符串列表模型中。
void Window::setupModel()
{
QStringList items;
items << tr("Home") << tr("Work") << tr("Other");
typeModel = new QStringListModel(items, this);
model = new QStandardItemModel(5, 3, this);
QStringList names;
names << "Alice" << "Bob" << "Carol" << "Donald" << "Emma";
QStringList addresses;
addresses << "<qt>123 Main Street<br/>Market Town</qt>"
<< "<qt>PO Box 32<br/>Mail Handling Service"
"<br/>Service City</qt>"
<< "<qt>The Lighthouse<br/>Remote Island</qt>"
<< "<qt>47338 Park Avenue<br/>Big City</qt>"
<< "<qt>Research Station<br/>Base Camp<br/>Big Mountain</qt>";
QStringList types;
types << "0" << "1" << "2" << "0" << "2";
for (int row = 0; row < 5; ++row) {
QStandardItem *item = new QStandardItem(names[row]);
model->setItem(row, 0, item);
item = new QStandardItem(addresses[row]);
model->setItem(row, 1, item);
item = new QStandardItem(types[row]);
model->setItem(row, 2, item);
}
}
当我们将每一行插入模型时,就像数据库中的记录一样,存储了对应于每个人的地址类型的typeModel中的项的值。当小部件映射器从每行的最后一列读取这些值时,它将需要使用它们作为typeModel中值的引用,如下图所示。
为了完整起见,我们展示了updateButtons()插槽的实现:
void Window::updateButtons(int row)
{
previousButton->setEnabled(row > 0);
nextButton->setEnabled(row < model->rowCount() - 1);
}
组合框单独模型的使用提供了一个选项菜单,这些选项与存储在主模型中的数据是分开的。使用将组合框的currentIndex属性与模型中的列相关联的命名映射,可以有效地在模型中存储查找值。
然而,在小部件映射器上下文之外读取模型时,我们需要了解typeModel,以便理解这些查找值。如果能够将数据和typeModel所持有的选项都存储在一个地方,将会非常有用。