关于QModelIndex、QStandardItem、QSelectionModel以及QTableview视图模型的实际运用与一些理解。

关于QModelIndex、QStandardItem、QSelectionModel以及QTableview视图模型的实际运用与一些理解。

首先先介绍一下它们的概念:

QStandardItemModel:基于项数据的标准数据模型,可以处理二维数据。维护一个二维的项数据数组,每个项是一个 QStandardltem 类的变量,用于存储项的数据、字体格式、对齐方式等。

QModelIndex:是 Qt 中用于表示数据模型中项的索引的类。它包含了描述项位置的行号、列号和父索引等信息,用于在数据模型和视图之间进行数据交互。

QItemSelectionModel:一个用于跟踪视图组件的单元格选择状态的类,当在 QTableView 选择某个单元格,或多个单元格时,通过 QItemSelectionModel 可以获得选中的单元格的模型索引,为单元格的选择操作提供方便。

QTableView:二维数据表视图组件,有多个行和多个列,每个基本显示单元是一个单元格,通过 setModel() 函数设置一个 QStandardItemModel 类的数据模型之后,一个单元格显示 QStandardItemModel 数据模型中的一个项。

再介绍一下任务需求:此次需要实现一个简易的opcua客户端,大致的功能是实现对opcua服务器数据的连接断开连接数据加载显示以及对服务端opc数据读写功能。因此会用到上述所介绍的数据模型;

先看一下整体实现效果

image-20240122102635455

实际ua客户端是同步的

image-20240122102657163

使用tableView为标注读写点的视图框,tableview是从上方右侧框,双击数据将其添加到tableview进行数据读取与显示,最后可在tableview里面进行写值,写入到opcua服务端

部分代码如下:

创建数据模型:

    m_pItemModel = new QStandardItemModel(ui.tableView);
    m_pItemModel->setColumnCount(3);
    m_pItemModel->setHeaderData(0, Qt::Horizontal, u8"OPC名称");
    m_pItemModel->setHeaderData(1, Qt::Horizontal, u8"读取值");
    m_pItemModel->setHeaderData(2, Qt::Horizontal, u8"写入值");
    ui.tableView->setModel(m_pItemModel);
    ui.tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui.tableView->setColumnWidth(0, 520);
    ui.tableView->setColumnWidth(1, 80);
    ui.tableView->setColumnWidth(2, 80);

读取数据:

void ReadWriteOPC::DoReadValue(const QPoint& point) {
    QListWidgetItem* clickedItem = ui.listWidget2->itemAt(point);
    if (clickedItem) {
        std::string opcName = clickedItem->text().toStdString();
        UaString str[1];
        str[0] = UaString(opcName.c_str());
        //假设返回信息的个数dwItemCnt为1;
       // boost::shared_array<UaNodeValue>& rdValues;
        boost::shared_array<UaNodeValue>rdValues;
        UaNodeValue wrt[1];
        wrt[0].wDataType = UA_DATATYPE_INT16;
        // wrt[0].wDataType= UA_DATATYPE_INT16;
        if (m_OpcuaClient.Read(str, rdValues, 1, AttributeValue) == false) {
            qDebug() << "OPC点位值读取失败" << rdValues[0].val.iValue << ";";
        }
        else {
            int ReadValue = rdValues[0].val.iValue;
            qDebug() << "OPC点位值读取成功" << ReadValue << ";";
            m_mapOpcList.insert(std::make_pair(opcName, ReadValue));
            RefreshOpcTableView(m_mapOpcList);
        }
    }
}

写入数据:

void ReadWriteOPC::DoWriteValue()
{
    int rows = ui.tableView->model()->rowCount();
    for (int r = 0; r < rows; r++) {
		QModelIndex OpcNameIndex = ui.tableView->model()->index(r, 0);  // 获取指定行索引和第一列的模型索引
		QModelIndex OpcValueIndex2 = ui.tableView->model()->index(r, 2);

		QVariant OpcName = ui.tableView->model()->data(OpcNameIndex);
		QVariant OpcValue = ui.tableView->model()->data(OpcValueIndex2);

		///这种是传数据模型item;
		//QVariant value1 = item->model()->data(item->model()->index(row, 0)); // 第一列的值
		//QVariant value2 = item->model()->data(item->model()->index(row, 1)); // 第二列的值
		if (OpcValue.isValid()) {
			UaString str[1];
			// QString firstValue = item->text();
			 //int WriteValue = item->data(Qt::UserRole).toInt();
			QString OpcNameValue = OpcName.value<QString>();
			int WriteValue = OpcValue.value<int>();
			std::string strOpcNameValue = OpcNameValue.toStdString();
			str[0] = UaString(strOpcNameValue.c_str());
			boost::shared_array<uint32_t>wrtRlts;
			UaNodeValue wrt[1];
			wrt[0].val.iValue = WriteValue;
			wrt[0].wDataType = UA_DATATYPE_INT16;
			if (m_OpcuaClient.Write(str, wrt, 1, wrtRlts) == false) {
				qDebug() << "OPC点位写入失败";
			}
			else {
				qDebug() << "OPC点位写入成功";
				for (auto it = m_mapOpcList.begin(); it != m_mapOpcList.end(); ++it) {
					// 检查当前值是否为"Rccs"
					if (it->first == strOpcNameValue) {
						// 修改为新的值
						it->second = WriteValue;
					}
				}
			}
		}
		else {
			qDebug() << "写入点无效";
		}
    }
}

每次更新tableview:

void ReadWriteOPC::RefreshOpcTableView(std::map<std::string, int>mapMonItem)
{
    m_pItemModel->clear();
	int nIndex = 0;

	for (const auto& iter : mapMonItem) {
		m_pItemModel->setItem(nIndex, 0, new QStandardItem(iter.first.c_str()));
		m_pItemModel->setItem(nIndex, 1, new QStandardItem(QString::number(iter.second)));
        m_pItemModel->setItem(nIndex, 2, new QStandardItem(QString::number(iter.second)));
        
		++nIndex;
	}
	m_pItemModel->setHeaderData(0, Qt::Horizontal, u8"OPC名称");
	m_pItemModel->setHeaderData(1, Qt::Horizontal, u8"读取值");
    m_pItemModel->setHeaderData(2, Qt::Horizontal, u8"写入值");
    ui.tableView->setModel(m_pItemModel);
	ui.tableView->setColumnWidth(0, 520);
	ui.tableView->setColumnWidth(1, 80);
    ui.tableView->setColumnWidth(2, 80);
}

之前在界面写入数据触发更新写入数据:

connect(m_pItemModel, &QStandardItemModel::itemChanged, this, &ReadWriteOPC::DoWriteValue);

用到了itemChanged,即每次对tableview里面的数据模型进行改动时,即触发写值操作,但是有一个弊端,即每次往tableview里面添加读取的数据后即触发了itemchanged,但此时并未进行写值,不需要触发写值操作,虽然在表面上看不出,但是在debug模式时,相当于改一次值触发了两次写值操作,遂放弃这种方式。它传的参数即是QStandardItem模型

后面又了解到数据选择模型,于是用到了currentChanged

 connect(m_pItemSeleModel, &QItemSelectionModel::currentChanged, this, &ReadWriteOPC::DoWriteValue);

但是发现它触发信号的条件是选择项的改变,但是有时候造成多余操作了,比如:我只需要改变一项,因此导致信号无法触发。但是这个模型也给到了一些启发,通过它传的参数QModelIndex的模型,了解到可以直接通过index取值,而不需要通过QStandarItemModel模型来取。

最后采用了按钮触发的操作,每次都一次性写入一次性更新

connect(ui.btnSaveWriteVal, &QPushButton::clicked, this, &ReadWriteOPC::DoWriteValue);
  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KJammyHenry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值