QModelIndex/Role/Model介紹<二>

本文深入探讨了Qt中Model类的基本概念及其与Index的关系,解释了如何通过Model和Index进行数据访问和管理。

QModelIndex/Role/Model介紹<二>

Model类

基本概念
在model/view构架中,model为view和delegates使用数据提供了标准接口。在Qt中,标准接口QAbstractItemModel类中被定义。不管数据在底层以何种数据结构存储,QAabstractItemModel的子类会以层次结构的形式来表示数据,结构中包含了数据项表。我们按这种约定来访问model中的数据项,但这个约定不会对如何显示这些数据有任何限制。数据发生改变时,model通过信号槽机制来通知关联的views。

08144027_VsZg.jpg

Model Indexes

为了使数据存储与数据访问分开,引入了model index的概念。通过model index,可以引用model中的数据项,Views和delegates都使用indexes来访问数据项,然后再显示出来。因此,只有model需要了解如何获取数据,被model管理的数据类型可以非常广泛地被定义。Model indexes包含一个指向创建它们的model的指针,这会在配合多个model工作时避免混乱。
QAbstractItemModel *model = index.model();

model indexes提供了对一项数据信息的临时引用,通过它可以访问或是修改model中的数据。既然model有时会重新组织内部的数据结构,这时model indexes便会失效,因此不应该保存临时的model indexes。假如需要一个对数据信息的长期的引用,那么应该创建一个persistent model index。这个引用会保持更新。临时的model indexes由QModelIndex提供,而具有持久能力的model indexes则由QPersistentModelIndex提供。在获取对应一个数据项的model index时,需要考虑有关于model的三个属性:行数,列数,父项的model index。


行与列

在最基本的形式中,一个model可作为一个简单的表来访问,每个数据项由行,列数来定位。这必不意味着
底层的数据用数组结构来存储。行和列的使用仅仅是一种约定,它允许组件之间相互通讯。可以通过指定
model中的行列数来获取任一项数据,可以得到与数据项一一对应的那个index。
QModelIndex index = model->index(row, column, ...);
Model为简单的,单级的数据结构如list与tables提供了接口,它们如上面代码所显示的那样,不再需要别的信息被提供。当我们在获取一个model index时,我们需要提供另外的信息。
08144027_KNWB.jpg

上图代表一个基本的table model,它的每一项用一对行列数来定位。通过行列数,可以获取代表一个数据项的model index . 
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
一个model的顶级项,由QModelIndex()取得,它们上式被用作父项。

父项

类似于表的接口在搭配使用table或list view时理想的,这种行列系统与view显示的方式是确切匹配的。
然则,像tree views这种结构需要model提供更为灵活的接口来访问数据项。每个数据项可能是别的项的
父项,上级的项可以获取下级项的列表。
当获取model中数据项的index时,我们必须指定关于数据项的父项的信息。在model外部,引用一个数据
项的唯一方法就是通过model index,因此需要在求取model index时指定父项的信息。
QModelIndex index = model->index(row, column, parent);
08144027_2qB5.jpg

上图中,A项和C项作为model中顶层的兄弟项:
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
A有许多孩子,它的一个孩子B用以下代码获取:
QModelIndex indexB = model->index(1, 0, indexA);

项角色

model中的项可以作为各种角色来使用,这允许为不同的环境提供不同的数据。举例来说,Qt::DisplayRole被用于访问一个字符串,它作为文本会在view中显示。典型地,每个数据项都可以为许多不同的角色提供数据,标准的角色在Qt::ItemDataRole中定义。我们可以通过指定model index与角色来获取我们需要的数据:
QVariant value = model->data(index, role);
08144027_R4g0.jpg

角色指出了从model中引用哪种类型的数据。views可以用不同的形式显示角色,因此为每个角色提供正确
的信息是非常重要的。通过为每个角色提供适当数据,model也为views和delegates提供了暗示,如何正确地
把这些数据项显给用户。不同的views可以自由地解析或忽略这些数据信息,对于特殊的场合,也可以定义
一些附加的角色。

概念总结:
1,Model indexes为views与delegages提供model中数据项定位的信息,它与底层的数据结构无关。
2,通过指定行,列数,父项的model index来引用数据项。
3,依照别的组件的要求,model indexes被model构建。
4,使用index()时,如果指定了有效的父项的model index,那么返回得到的model index对应于父项的某个孩子。
5,使用index()时,如果指定了无效的父项的model index,那么返回得到的model index对应于顶层项的某个孩子。
6, 角色对一个数据项包含的不同类型的数据给出了区分。

使用Model Indexes

     QDirModel *model = new QDirModel;
     QModelIndex parentIndex = model->index(QDir::currentPath());
     int numRows = model->rowCount(parentIndex);
    for (int row = 0; row < numRows; ++row) {
         QModelIndex index = model->index(row, 0, parentIndex);
         tring text = model->data(index, Qt::DisplayRole).toString();
         // Display the text in a widget.

     }

以上的例子说明了从model中获取数据的基本原则:

1 model的尺寸可以从rowCount()与columnCount()中得出。这些函数通常都需要一个表示父项的model index。
2 model indexes用来从model中访问数据项,数据项用行,列,父项model index定位。
3 为了访问model顶层项,可以使用QModelIndex()指定。
4 数据项为不同的角色提供不同的数据。为了获取数据,除了model index之外,还要指定角色。

转载于:https://my.oschina.net/itissue/blog/112512

#include"mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QDir> #include <QMessageBox> #include <QGraphicsScene> #include <QGraphicsPixmapItem> #include <QSettings> #include <QTextStream> #include<QDebug> #include <QWheelEvent> #include <QMouseEvent> #include <QScrollBar> #include <QGraphicsLineItem> //#include <opencv2/opencv.hpp> #include <QString> #include <QDebug> #include <QDesktopServices> #include <QUrl> #include <QFileSystemModel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // 初始化参数 toothParams.Init();//矫正参数 laserParams.Init();//激光参数 currentPixmapItem = nullptr; currentLineItem = nullptr; currentScale = 1.0; isDragging = false; // 设置树形视图 //ui->treeWidget->setHeaderLabel("文件夹结构"); //qDebug() << "Original:" << "文件" << "UTF-8:" ; // 设置图形视图的属性 //ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag); ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); ui->graphicsView->setResizeAnchor(QGraphicsView::AnchorUnderMouse);//设置视图组件本身大小改变时的锚点 ui->graphicsView->setRenderHint(QPainter::Antialiasing); ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); // 连接信号槽 connect(ui->treeWidget,&QTreeWidget::itemClicked,this,&MainWindow::on_treeWidget_itemClicked); } MainWindow::~MainWindow() { delete ui; } // void listCameras() { // for (int i = 0;; ++i) { // cv::VideoCapture cap(i); // if (!cap.isOpened()) { // break; // } // qDebug() << "Found camera:" << i; // cap.release(); // } // } void MainWindow::on_browseButton_clicked() { QString folderPath = QFileDialog::getExistingDirectory(this, "选择标定文件夹"); if (!folderPath.isEmpty()) { currentFolderPath = folderPath; ui->folderPathEdit->setText(folderPath); updateTreeView(folderPath); } } void MainWindow::on_calibrateButton_clicked() { /* ---------- 1. 基础检查 ---------- */ if (currentFolderPath.isEmpty()) { QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("请先选择标定文件夹")); return; } if (ui->cameraNameEdit->text().trimmed().isEmpty()) { QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("请输入相机名称")); return; } updateParametersFromUI(); /* ---------- 2. 参数合法性检查 ---------- */ if (laserParams.CameraWidth <= 0 || laserParams.CameraHeight <= 0 || laserParams.CameraFocal <= 0) { QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("相机宽度、高度或焦距不能为 0")); return; } calibrationPoints.clear(); int validDirCount = 0; /* ---------- 3. 主循环 ---------- */ for (int disIndex = 60; disIndex < 400; disIndex += 10) { QString currDir = QDir(currentFolderPath).filePath(QString("%1_0").arg(disIndex)); QDir dir(currDir); if (!dir.exists()) { qDebug() << "跳过不存在的目录:" << currDir; continue; } QFileInfoList imgs = dir.entryInfoList(QStringList() << "*.bmp" << "*.jpg" << "*.png", QDir::Files); if (imgs.isEmpty()) { qDebug() << "目录无图像:" << currDir; continue; } ++validDirCount; QByteArray dirBA = currDir.toLocal8Bit(); char dirChar[512] = {0}; strncpy_s(dirChar, sizeof(dirChar) - 1, dirBA.constData(), _TRUNCATE); qDebug() << "DLL 输入路径:" << dirChar << "图像数量:" << imgs.size(); std::vector<ToothCalibPoint> vLineCaliPoint; bool ok = GetToothPositionModeOne(dirChar, toothParams, vLineCaliPoint); qDebug() << "GetToothPositionModeOne 返回:" << ok << "点数:" << vLineCaliPoint.size(); if (!ok || vLineCaliPoint.empty()) continue; double left = 0, right = 0; for (const auto &p : vLineCaliPoint) { if (p.PointMode == 1) left = p.CameraW; if (p.PointMode == 2) right = p.CameraW; } if (left > 0 && right > 0) toothParams.CenterPos = (left + right) / 2.0; QVector<ToothCalibPoint> qv; qv.reserve(int(vLineCaliPoint.size())); for (const auto &p : vLineCaliPoint) qv.append(p); calibrationPoints.append(qv); } /* ---------- 4. 检查有效目录 ---------- */ if (validDirCount == 0 || calibrationPoints.isEmpty()) { QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("未找到任何有效标定目录或标定点,请检查数据")); return; } /* ---------- 5. 保存 tooth.txt ---------- */ QString saveTxt = QDir(currentFolderPath).filePath("tooth.txt"); QFile file(saveTxt); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, QString::fromUtf8("错误"), QString::fromUtf8("无法创建标定文件")); return; } QTextStream out(&file); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) out.setCodec("UTF-8"); #else out.setEncoding(QStringConverter::Utf8); #endif int total = 0; for (const auto &vec : calibrationPoints) for (const auto &p : vec) { out << QString::number(p.CameraW, 'f', 3) << ',' << QString::number(p.CameraH, 'f', 3) << ',' << QString::number(p.WorldX, 'f', 3) << ',' << QString::number(p.WorldZ, 'f', 3) << ',' << p.FileName << ";\n"; ++total; } file.close(); qDebug() << "已保存" << total << "点到" << saveTxt; /* ---------- 6. 激光线标定 ---------- */ LaserLineModelResult res; res.Init(); // 设置输出目录为当前选择的文件夹下的"生成的标注结果"子文件夹 QString outputDir = QDir(currentFolderPath).filePath("生成的标注结果"); QDir().mkpath(outputDir); // 确保输出目录存在 QString cameraName = ui->cameraNameEdit->text().trimmed(); if (cameraName.isEmpty()) { cameraName = "default_camera"; } // 设置输出文件路径 QString dllPath = QDir(outputDir).filePath(cameraName + "-deep3.dll"); QString pcImagePath = QDir(outputDir).filePath(cameraName + "-pc.jpg"); QString rcImagePath = QDir(outputDir).filePath(cameraName + "-rc.jpg"); QString xImagePath = QDir(outputDir).filePath(cameraName + "-X.png"); QString zImagePath = QDir(outputDir).filePath(cameraName + "-Z.png"); qDebug()<<dllPath; QByteArray txtBA = saveTxt.toUtf8(); QByteArray nameBA = cameraName.toUtf8(); QByteArray dllDirBA = outputDir.toUtf8(); strncpy_s(laserParams.CalibPointFileName, sizeof(laserParams.CalibPointFileName) - 1, txtBA.constData(), _TRUNCATE); strncpy_s(laserParams.CameraName, sizeof(laserParams.CameraName) - 1, nameBA.constData(), _TRUNCATE); strncpy_s(res.CalibResultDir, sizeof(res.CalibResultDir) - 1, dllDirBA.constData(), _TRUNCATE); laserParams.MeasurePrecision = ui->precisionSpinBox->value(); // 调用标定函数 bool calibrationSuccess = GetLaserLineCalibUseLcModeThree(laserParams, res); if (calibrationSuccess) { /* ---------- 7. 重命名和移动生成的文件 ---------- */ QString defaultDllPath = QDir(outputDir).filePath("deep3.dll"); QString defaultPcPath = QDir(outputDir).filePath("pc.jpg"); QString defaultRcPath = QDir(outputDir).filePath("rc.jpg"); QString defaultXPath = QDir(outputDir).filePath("X.png"); QString defaultZPath = QDir(outputDir).filePath("Z.png"); // 重命名文件 if (QFile::exists(defaultDllPath)) { QFile::rename(defaultDllPath, dllPath); } if (QFile::exists(defaultPcPath)) { QFile::rename(defaultPcPath, pcImagePath); } if (QFile::exists(defaultRcPath)) { QFile::rename(defaultRcPath, rcImagePath); } if (QFile::exists(defaultXPath)) { QFile::rename(defaultXPath, xImagePath); } if (QFile::exists(defaultZPath)) { QFile::rename(defaultZPath, zImagePath); } /* ---------- 8. 验证文件生成 ---------- */ bool allFilesGenerated = true; QStringList missingFiles; if (!QFile::exists(dllPath)) { allFilesGenerated = false; missingFiles << "DLL文件"; } if (!QFile::exists(pcImagePath)) { allFilesGenerated = false; missingFiles << "pc.jpg"; } if (!QFile::exists(rcImagePath)) { allFilesGenerated = false; missingFiles << "rc.jpg"; } if (!QFile::exists(xImagePath)) { allFilesGenerated = false; missingFiles << "X.png"; } if (!QFile::exists(zImagePath)) { allFilesGenerated = false; missingFiles << "Z.png"; } if (allFilesGenerated) { QMessageBox::information(this, QStringLiteral("完成"), QString::fromUtf8("标定完成!文件已生成在:%1").arg(outputDir)); // 可选:打开生成的文件夹 QDesktopServices::openUrl(QUrl::fromLocalFile(outputDir)); } else { QMessageBox::warning(this, QStringLiteral("部分完成"), QString::fromUtf8("标定完成,但缺少以下文件:%1\n请检查DLL输出路径。") .arg(missingFiles.join(", "))); } } else { QMessageBox::critical(this, QStringLiteral("错误"), QString::fromUtf8("标定失败!请检查输入参数和数据。")); } } void MainWindow::updateTreeView(const QString &folderPath) { ui->treeWidget->clear(); QDir dir(folderPath); if (!dir.exists()) return; // 添加文件夹和子文件夹 QTreeWidgetItem *rootItem = new QTreeWidgetItem(ui->treeWidget); rootItem->setText(0, dir.dirName()); rootItem->setData(0, Qt::UserRole, folderPath); // 遍历子文件夹 QFileInfoList folders = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QFileInfo &folderInfo, folders) { QTreeWidgetItem *folderItem = new QTreeWidgetItem(rootItem); folderItem->setText(0, folderInfo.fileName()); folderItem->setData(0, Qt::UserRole, folderInfo.absoluteFilePath()); // 添加图片文件 QDir subDir(folderInfo.absoluteFilePath()); QFileInfoList images = subDir.entryInfoList(QStringList() << "*.bmp" << "*.jpg" << "*.png", QDir::Files); foreach (const QFileInfo &imageInfo, images) { QTreeWidgetItem *imageItem = new QTreeWidgetItem(folderItem); imageItem->setText(0, imageInfo.fileName()); imageItem->setData(0, Qt::UserRole, imageInfo.absoluteFilePath()); } } ui->treeWidget->expandAll(); // 将文件夹和图片全部展示出 } // void MainWindow::on_calibrateButton_clicked() // { // if (currentFolderPath.isEmpty()) // { // QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("请先选择文件夹")); // return; // } // // 更新参数 // updateParametersFromUI(); // // 执行标定过程 // calibrationPoints.clear(); // for (int disIndex = 60; disIndex < 400; disIndex += 10) { // std::vector<ToothCalibPoint> vLineCaliPoint; // // 使用一致的路径分隔符 // //QString currSingleFileDir = currentFolderPath + "/" + QString::number(disIndex) + "_0"; // std::string currSingleFileDir = currentFolderPath.toStdString() + "\\" + std::to_string(disIndex) + "_0"; // //qDebug() << "Current Single File Directory:" << currSingleFileDir; // // 检查目录是否存在 // /*QDir dir(currSingleFileDir); // if (!dir.exists()) { // qDebug() << "Directory does not exist:" << currSingleFileDir; // continue; // }*/ // // 检查目录中是否有图像文件 // /*QStringList images = dir.entryList(QStringList() << "*.bmp" << "*.jpg" << "*.png", QDir::Files); // if (images.isEmpty()) { // qDebug() << "No images found in directory:" << currSingleFileDir; // continue; // }*/ // //qDebug() << "Found" << images.size() << "images in directory"; // char currSingleFileDir_char[512]; // 假设 512 足够 // strcpy_s(currSingleFileDir_char, sizeof(currSingleFileDir_char), currSingleFileDir.c_str()); // // 转换为本地编码(确保外部库兼容) // //QByteArray ba = currSingleFileDir.toLocal8Bit(); // //char currSingleFileDir_char[512]; // //strncpy_s(currSingleFileDir_char, sizeof(currSingleFileDir_char), ba.constData(), _TRUNCATE); // qDebug() << "Calling GetToothPositionModeOne with path:" << currSingleFileDir_char; // // 调用标定函数 // bool success = GetToothPositionModeOne(currSingleFileDir_char, toothParams, vLineCaliPoint); // qDebug() << "GetToothPositionModeOne result:" << success; // qDebug() << "vLineCaliPoint size:" << vLineCaliPoint.size(); // if (!vLineCaliPoint.empty()) { // double leftPos = 0; // double rightPos = 0; // for (auto& p : vLineCaliPoint) { // qDebug() << "Point mode:" << p.PointMode << "CameraW:" << p.CameraW; // if (p.PointMode == 1) leftPos = p.CameraW; // if (p.PointMode == 2) rightPos = p.CameraW; // } // if (leftPos > 0 && rightPos > 0) { // toothParams.CenterPos = (leftPos + rightPos) / 2.0; // qDebug() << "Calculated center position:" << toothParams.CenterPos; // } // QVector<ToothCalibPoint> qVector; // for (const auto& point : vLineCaliPoint) { // qVector.append(point); // } // calibrationPoints.append(qVector); // } else { // qDebug() << "No calibration points found for distance:" << disIndex; // } // } // // 保存标定文件 // QString saveTxtName = currentFolderPath + "/tooth.txt"; // QFile file(saveTxtName); // if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { // QTextStream out(&file); // // 设置编码为 UTF-8 // #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // out.setCodec("UTF-8"); // #else // out.setEncoding(QStringConverter::Utf8); // #endif // int pointCount = 0; // for (const auto& points : calibrationPoints) { // for (const auto& pt : points) { // out << QString::number(pt.CameraW, 'f', 3) << "," // << QString::number(pt.CameraH, 'f', 3) << "," // << QString::number(pt.WorldX, 'f', 3) << "," // << QString::number(pt.WorldZ, 'f', 3) << "," // << pt.FileName << ";\n"; // pointCount++; // } // } // file.close(); // qDebug() << "Saved" << pointCount << "calibration points to" << saveTxtName; // if (pointCount == 0) { // QMessageBox::warning(this, QString::fromUtf8("警告"), QString::fromUtf8("未找到任何标定点,请检查输入数据和参数设置")); // return; // } // } else { // qDebug() << "Failed to open file for writing:" << saveTxtName; // QMessageBox::warning(this, QString::fromUtf8("错误"), QString::fromUtf8("无法创建标定文件")); // return; // } // // 执行激光线标定 // LaserLineModelResult currResultOut; // currResultOut.Init(); // QByteArray ba1 = saveTxtName.toUtf8(); // strncpy_s(laserParams.CalibPointFileName, ba1.constData(), sizeof(laserParams.CalibPointFileName)); // QByteArray ba2 = ui->cameraNameEdit->text().toUtf8(); // strncpy_s(laserParams.CameraName, ba2.constData(), sizeof(laserParams.CameraName)); // QString cameraSaveDir = QApplication::applicationDirPath() + "/Calibrate/" + // ui->cameraNameEdit->text() + "_dll"; // QDir().mkpath(cameraSaveDir); // QByteArray ba3 = cameraSaveDir.toUtf8(); // strncpy_s(currResultOut.CalibResultDir, ba3.constData(), sizeof(currResultOut.CalibResultDir)); // currResultOut.MeasurePrecision = ui->precisionSpinBox->value(); // GetLaserLineCalibUseLcModeThree(laserParams, currResultOut); // QMessageBox::information(this, QStringLiteral("完成"), QString::fromLocal8Bit("标定完成!")); // } // void MainWindow::on_calibrateButton_clicked() // { // /* ---------- 1. 基础检查 ---------- */ // if (currentFolderPath.isEmpty()) // { // QMessageBox::warning(this, QString::fromUtf8("警告"), // QString::fromUtf8("请先选择标定文件夹")); // return; // } // if (ui->cameraNameEdit->text().trimmed().isEmpty()) // { // QMessageBox::warning(this, QString::fromUtf8("警告"), // QString::fromUtf8("请输入相机名称")); // return; // } // updateParametersFromUI(); // 把 UI 值读到 toothParams / laserParams // /* ---------- 2. 参数合法性检查 ---------- */ // if (laserParams.CameraWidth <= 0 || laserParams.CameraHeight <= 0 || // laserParams.CameraFocal <= 0) // { // QMessageBox::warning(this, QString::fromUtf8("警告"), // QString::fromUtf8("相机宽度、高度或焦距不能为 0")); // return; // } // calibrationPoints.clear(); // 清空旧数据 // int validDirCount = 0; // 统计有效目录数 // /* ---------- 3. 主循环 ---------- */ // for (int disIndex = 60; disIndex < 400; disIndex += 10) // { // QString currDir = QDir(currentFolderPath).filePath(QString("%1_0").arg(disIndex)); // // QString currDir = currentFolderPath.toStdString() + "\\" + std::to_string(disIndex); // QDir dir(currDir); // /* 3.1 目录是否存在 */ // if (!dir.exists()) // { // qDebug() << "跳过不存在的目录:" << currDir; // continue; // } // /* 3.2 是否有图像 */ // QFileInfoList imgs = dir.entryInfoList(QStringList() << "*.bmp" << "*.jpg" << "*.png", // QDir::Files); // if (imgs.isEmpty()) // { // qDebug() << "目录无图像:" << currDir; // continue; // } // ++validDirCount; // /* 3.3 准备 char* 路径 */ // QByteArray dirBA = currDir.toLocal8Bit(); // 本地编码,兼容 DLL // char dirChar[512] = {0}; // strncpy_s(dirChar, sizeof(dirChar) - 1, // dirBA.constData(), _TRUNCATE); // qDebug() << "DLL 输入路径:" << dirChar // << "图像数量:" << imgs.size(); // /* 3.4 调用 DLL */ // std::vector<ToothCalibPoint> vLineCaliPoint; // bool ok = GetToothPositionModeOne(dirChar, toothParams, vLineCaliPoint); // qDebug() << "GetToothPositionModeOne 返回:" << ok // << "点数:" << vLineCaliPoint.size(); // if (!ok || vLineCaliPoint.empty()) // continue; // /* 3.5 计算中心 & 转 Qt 容器 */ // double left = 0, right = 0; // for (const auto &p : vLineCaliPoint) // { // if (p.PointMode == 1) left = p.CameraW; // if (p.PointMode == 2) right = p.CameraW; // } // if (left > 0 && right > 0) // toothParams.CenterPos = (left + right) / 2.0; // QVector<ToothCalibPoint> qv; // qv.reserve(int(vLineCaliPoint.size())); // for (const auto &p : vLineCaliPoint) qv.append(p); // calibrationPoints.append(qv); // } // /* ---------- 4. 一个有效目录都没跑 ---------- */ // if (validDirCount == 0 || calibrationPoints.isEmpty()) // { // QMessageBox::warning(this, QString::fromUtf8("警告"), // QString::fromUtf8("未找到任何有效标定目录或标定点,请检查数据")); // return; // } // /* ---------- 5. 保存 tooth.txt ---------- */ // //QString saveTxt = QApplication::applicationDirPath() + "/resources/tooth.txt"; // QString saveTxt = QDir(currentFolderPath).filePath("tooth.txt"); // QFile file(saveTxt); // if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) // { // QMessageBox::warning(this, QString::fromUtf8("错误"), // QString::fromUtf8("无法创建标定文件")); // return; // } // QTextStream out(&file); // #if QT_VERSION < QT_VERSION_CHECK(6,0,0) // out.setCodec("UTF-8"); // #else // out.setEncoding(QStringConverter::Utf8); // #endif // int total = 0; // for (const auto &vec : calibrationPoints) // for (const auto &p : vec) // { // out << QString::number(p.CameraW, 'f', 3) << ',' // << QString::number(p.CameraH, 'f', 3) << ',' // << QString::number(p.WorldX, 'f', 3) << ',' // << QString::number(p.WorldZ, 'f', 3) << ',' // << p.FileName << ";\n"; // ++total; // } // file.close(); // qDebug() << "已保存" << total << "点到" << saveTxt; // /* ---------- 6. 激光线标定 ---------- */ // LaserLineModelResult res; // res.Init(); // /* 6.1 填充激光参数 & 输出路径 */ // QByteArray txtBA = saveTxt.toUtf8(); // QByteArray nameBA = ui->cameraNameEdit->text().toUtf8(); // QByteArray dirBA = (QApplication::applicationDirPath() // + "/Calibrate/" // + ui->cameraNameEdit->text() + "_dll").toUtf8(); // strncpy_s(laserParams.CalibPointFileName, // sizeof(laserParams.CalibPointFileName) - 1, // txtBA.constData(), _TRUNCATE); // strncpy_s(laserParams.CameraName, // sizeof(laserParams.CameraName) - 1, // nameBA.constData(), _TRUNCATE); // strncpy_s(res.CalibResultDir, // sizeof(res.CalibResultDir) - 1, // dirBA.constData(), _TRUNCATE); // laserParams.MeasurePrecision = ui->precisionSpinBox->value(); // QDir().mkpath(QString::fromUtf8(dirBA)); // 确保输出目录存在 // GetLaserLineCalibUseLcModeThree(laserParams, res); // QMessageBox::information(this, QStringLiteral("完成"), // QString::fromUtf8("标定完成!")); // } //当树形控件中的某个项目被点击时触发,用于加载或显示被选中的内容 void MainWindow::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column) { Q_UNUSED(column); QString filePath = item->data(0, Qt::UserRole).toString(); if (QFileInfo(filePath).isFile()) { // 查找对应的x坐标 QString fileName = QFileInfo(filePath).fileName(); QString distanceStr = QFileInfo(filePath).dir().dirName(); bool flag; double distance = distanceStr.split("_").first().toDouble(&flag); if (flag) { double xPosition = 0; // 在标定数据中查找对应的x坐标 for (const auto& points : calibrationPoints) { for (const auto& pt : points) { if (qAbs(pt.WorldZ - distance) < 0.1) { xPosition = pt.CameraW + pt.WorldX; break; } } } displayImage(filePath, xPosition); } } } void MainWindow::displayImage(const QString &imagePath, double xPosition) { // 清除现有场景 if (ui->graphicsView->scene()) { delete ui->graphicsView->scene(); } QGraphicsScene *scene = new QGraphicsScene(this); QPixmap pixmap(imagePath); if (!pixmap.isNull()) { // 添加图像到场景 currentPixmapItem = scene->addPixmap(pixmap); scene->setSceneRect(pixmap.rect()); // 添加竖线 if (xPosition > 0) { QPen redPen(Qt::red); redPen.setWidth(2); currentLineItem = scene->addLine(xPosition, 0, xPosition, pixmap.height(), redPen); } else { currentLineItem = nullptr; } ui->graphicsView->setScene(scene); // 初始适应视图 ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); currentScale = 1.0; // 启用缩放和平移 ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag); ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); ui->graphicsView->setResizeAnchor(QGraphicsView::AnchorUnderMouse); // 显示图像信息 QString info = QString("图像尺寸: %1 x %2\n文件路径: %3") .arg(pixmap.width()) .arg(pixmap.height()) .arg(imagePath); QGraphicsTextItem *textItem = scene->addText(info); textItem->setPos(10, 10); textItem->setDefaultTextColor(Qt::red); textItem->setZValue(1); // 确保文本显示在最上层 } } // 添加鼠标滚轮事件处理以实现缩放 void MainWindow::wheelEvent(QWheelEvent *event) { if (ui->graphicsView->underMouse()) { double scaleFactor = 1.15; if (event->angleDelta().y() > 0) { // 放大 ui->graphicsView->scale(scaleFactor, scaleFactor); currentScale *= scaleFactor; } else { // 缩小 ui->graphicsView->scale(1.0 / scaleFactor, 1.0 / scaleFactor); currentScale /= scaleFactor; } event->accept(); } else { QMainWindow::wheelEvent(event); } } // 添加鼠标按下事件处理以实现自定义拖拽 void MainWindow::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && ui->graphicsView->underMouse()) { lastMousePos = event->pos(); setCursor(Qt::ClosedHandCursor); event->accept(); } else { QMainWindow::mousePressEvent(event); } } // 添加鼠标移动事件处理以实现自定义拖拽 void MainWindow::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton && ui->graphicsView->underMouse()) { QPoint delta = event->pos() - lastMousePos; lastMousePos = event->pos(); // 移动视图 QScrollBar *hBar = ui->graphicsView->horizontalScrollBar(); QScrollBar *vBar = ui->graphicsView->verticalScrollBar(); hBar->setValue(hBar->value() - delta.x()); vBar->setValue(vBar->value() - delta.y()); event->accept(); } else { QMainWindow::mouseMoveEvent(event); } } // 添加鼠标释放事件处理 void MainWindow::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { setCursor(Qt::ArrowCursor); event->accept(); } else { QMainWindow::mouseReleaseEvent(event); } } // 添加重置视图的槽函数 void MainWindow::onResetView() { if (ui->graphicsView->scene()) { ui->graphicsView->fitInView(ui->graphicsView->scene()->sceneRect(), Qt::KeepAspectRatio); currentScale = 1.0; } } // 添加绘制红线的函数 void MainWindow::drawRedLine(double xPosition) { if (ui->graphicsView->scene() && currentPixmapItem) { // 移除现有的红线 if (currentLineItem) { ui->graphicsView->scene()->removeItem(currentLineItem); delete currentLineItem; } // 创建新红线 QPen redPen(Qt::red); redPen.setWidth(2); QPixmap pixmap = currentPixmapItem->pixmap(); currentLineItem = ui->graphicsView->scene()->addLine( xPosition, 0, xPosition, pixmap.height(), redPen); } } void MainWindow::updateParametersFromUI() { toothParams.CenterDis = ui->centerDisSpinBox->value(); toothParams.SideDis = ui->sideDisSpinBox->value(); toothParams.ToothNum = ui->toothNumSpinBox->value(); toothParams.DeepThresh = ui->deepThreshSpinBox->value(); toothParams.DeepStep = ui->deepStepSpinBox->value(); toothParams.CenterPos = ui->centerPosSpinBox->value(); laserParams.CameraWidth = ui->cameraWidthSpinBox->value(); laserParams.CameraHeight = ui->cameraHeightSpinBox->value(); laserParams.CameraOffsetYCalib = ui->offsetYCalibSpinBox->value(); laserParams.CameraOffsetYMap = ui->offsetYMapSpinBox->value(); laserParams.CameraMapHeight = ui->mapHeightSpinBox->value(); laserParams.RealXOffset = ui->realXOffsetSpinBox->value(); laserParams.RealZOffset = ui->realZOffsetSpinBox->value(); laserParams.CameraPiexSize = ui->pixelSizeSpinBox->value(); laserParams.CameraFocal = ui->focalSpinBox->value(); } void MainWindow::updateUIfromParameters() { ui->centerDisSpinBox->setValue(toothParams.CenterDis); ui->sideDisSpinBox->setValue(toothParams.SideDis); ui->toothNumSpinBox->setValue(toothParams.ToothNum); ui->deepThreshSpinBox->setValue(toothParams.DeepThresh); ui->deepStepSpinBox->setValue(toothParams.DeepStep); ui->centerPosSpinBox->setValue(toothParams.CenterPos); ui->cameraWidthSpinBox->setValue(laserParams.CameraWidth); ui->cameraHeightSpinBox->setValue(laserParams.CameraHeight); ui->offsetYCalibSpinBox->setValue(laserParams.CameraOffsetYCalib); ui->offsetYMapSpinBox->setValue(laserParams.CameraOffsetYMap); ui->mapHeightSpinBox->setValue(laserParams.CameraMapHeight); ui->realXOffsetSpinBox->setValue(laserParams.RealXOffset); ui->realZOffsetSpinBox->setValue(laserParams.RealZOffset); ui->pixelSizeSpinBox->setValue(laserParams.CameraPiexSize); ui->focalSpinBox->setValue(laserParams.CameraFocal); } void MainWindow::on_saveParamsButton_clicked() { QString fileName = QFileDialog::getSaveFileName(this, "保存参数", "", "配置文件 (*.ini)"); if (!fileName.isEmpty()) { QSettings settings(fileName, QSettings::IniFormat); settings.beginGroup("ToothParameters"); settings.setValue("CenterDis", toothParams.CenterDis); settings.setValue("SideDis", toothParams.SideDis); settings.setValue("ToothNum", toothParams.ToothNum); settings.setValue("DeepThresh", toothParams.DeepThresh); settings.setValue("DeepStep", toothParams.DeepStep); settings.setValue("CenterPos", toothParams.CenterPos); settings.endGroup(); settings.beginGroup("LaserParameters"); settings.setValue("CameraWidth", laserParams.CameraWidth); settings.setValue("CameraHeight", laserParams.CameraHeight); settings.setValue("CameraOffsetYCalib", laserParams.CameraOffsetYCalib); settings.setValue("CameraOffsetYMap", laserParams.CameraOffsetYMap); settings.setValue("CameraMapHeight", laserParams.CameraMapHeight); settings.setValue("RealXOffset", laserParams.RealXOffset); settings.setValue("RealZOffset", laserParams.RealZOffset); settings.setValue("CameraPiexSize", laserParams.CameraPiexSize); settings.setValue("CameraFocal", laserParams.CameraFocal); settings.setValue("CameraName", QString(laserParams.CameraName)); settings.endGroup(); } } void MainWindow::on_loadParamsButton_clicked() { QString fileName = QFileDialog::getOpenFileName(this, "加载参数", "", "配置文件 (*.txt)"); if (!fileName.isEmpty()) { QSettings settings(fileName, QSettings::IniFormat); settings.beginGroup("ToothParameters"); toothParams.CenterDis = settings.value("CenterDis", 0).toDouble(); toothParams.SideDis = settings.value("SideDis", 0).toDouble(); toothParams.ToothNum = settings.value("ToothNum", 50).toInt(); toothParams.DeepThresh = settings.value("DeepThresh", 30).toInt(); toothParams.DeepStep = settings.value("DeepStep", 16).toInt(); toothParams.CenterPos = settings.value("CenterPos", 0).toInt(); settings.endGroup(); settings.beginGroup("LaserParameters"); laserParams.CameraWidth = settings.value("CameraWidth", 0).toInt(); laserParams.CameraHeight = settings.value("CameraHeight", 0).toInt(); laserParams.CameraOffsetYCalib = settings.value("CameraOffsetYCalib", 0).toInt(); laserParams.CameraOffsetYMap = settings.value("CameraOffsetYMap", 0).toInt(); laserParams.CameraMapHeight = settings.value("CameraMapHeight", 0).toInt(); laserParams.RealXOffset = settings.value("RealXOffset", 0).toDouble(); laserParams.RealZOffset = settings.value("RealZOffset", 0).toDouble(); laserParams.CameraPiexSize = settings.value("CameraPiexSize", 0).toDouble(); laserParams.CameraFocal = settings.value("CameraFocal", 0).toDouble(); QString cameraName = settings.value("CameraName", "").toString(); strncpy_s(laserParams.CameraName, cameraName.toUtf8().data(), sizeof(laserParams.CameraName)); settings.endGroup(); updateUIfromParameters(); } } 结合我的代码来实现
09-12
namespace view::equipment { equipment_home::equipment_home(QWidget *parent) : QWidget(parent), ui(new view::equipment::Ui::equipment_home) { ui->setupUi(this); data::Equipment::buildDB(); data::Equipment::EquipmentClass::createEquipmentClassTable(); data::Equipment::EquipmentInstnace::createEquipmentInstanceTable(); //初始化模型 dataModel::EquipmentDataModel *modelRent = new dataModel::EquipmentDataModel(this); fliterModel::FilterProxyMdel *rentFilterProxyMdel = new fliterModel::FilterProxyMdel(this); rentFilterProxyMdel->setSourceModel(modelRent); //给视图指定模型 ui->tableView->setModel(rentFilterProxyMdel); ui->tableView->hideColumn(dataModel::EquipmentDataModel::Col_ID); ui->tableView->hideColumn(dataModel::EquipmentDataModel::Col_Count); ui->tableView->hideColumn(dataModel::EquipmentDataModel::Col_RentId); ui->tableView->hideColumn(dataModel::EquipmentDataModel::Col_ClassId); // 调整列宽以显示数据 ui->tableView->resizeColumnsToContents(); ui->tableView->horizontalHeader()->setStretchLastSection(true); // 加载数据 modelRent->fetchData(); // 调试信息 qDebug() << "Model row count:" << modelRent->rowCount(); qDebug() << "Model column count:" << modelRent->columnCount(); } 根据它完善下列代码: void equipment_home::on_kadd_clicked() //添加器材 { // 创建对话框并设置父对象(自动内存管理) kaddmanage *dialog = new kaddmanage(this); // 连接对话框的dataAdded信号到主界面的刷新函数(on_kreall_clicked) connect(dialog, &kaddmanage::dataAdded, this, [this]() { on_kreall_clicked(); // 调用整理库数据功能,刷新tableView }); // 显示对话框(模态或非模态均可,此处用show()非模态) dialog->show(); } void equipment_home::on_kreall_clicked() //整理库数据 { // 1. 确保数据库及表结构存在(原有功能) // 2. 加载所有设备完整记录 QList<data::Equipment::fullEquipmentRecord> records = data::Equipment::loadFullEquipmentRecords(); // 3. 按 "item" 字段升序排序(假设 "item" 对应设备名称 "name" 字段) // 使用 Qt 的字符串比较(支持本地化,类似Excel排序) std::sort(records.begin(), records.end(), [](const data::Equipment::fullEquipmentRecord& a, const data::Equipment::fullEquipmentRecord& b) { return QString::localeAwareCompare(a.name, b.name) < 0; // 升序排列 }); // 4. 将排序后的数据展示到 tableView(假设 UI 中构件名为 tableView) // 清空原有数据 QStandardItemModel* model = qobject_cast<QStandardItemModel*>(ui->tableView->model()); if (!model) { model = new QStandardItemModel(this); // 设置表头(仅首次初始化时设置) model->setHorizontalHeaderLabels({ "ID", "设备名称", "状态","类别名称", "创建时间" }); ui->tableView->setModel(model); } model->removeRows(0, model->rowCount()); // 清空旧数据 // 填充排序后的数据 for (const auto& rec : records) { QList<QStandardItem*> items; items << new QStandardItem(QString::number(rec.id)) // ID << new QStandardItem(rec.name) // 设备名称(排序字段) << new QStandardItem(rec.status) // 状态 << new QStandardItem(rec.inDate.toString("yyyy-MM-dd hh:mm:ss")) // 创建时间 << new QStandardItem(QString::number(rec.class_id)) // 类别ID << new QStandardItem(rec.type); // 类别名称 model->appendRow(items); } // 可选:自动调整列宽 ui->tableView->resizeColumnsToContents(); log(service::LogLevel::INFO) << "数据库整理完成"; }
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值