1、简述

本博客主要记载开发过程中的不熟悉的Qt小知识点。本软件是上海火克公司开发的一款应用软件,软件实现的功能主要有三个部分,1是项目管理,2是计算管道覆盖比,3是自动生成word报告。

2、内容记录

2.1 QStatusBar的使用

效果图:

代码:

 // Initial the statusBarui.statusBar->setStyleSheet(QString("QStatusBar::item{border: 0px}"));QLabel* label0 = new QLabel("User: ");label0->setAlignment(Qt::AlignLeft);ui.statusBar->addPermanentWidget(label0,0);    // 第二个参数为所占比例m_qUserName = new QLabel;m_qUserName->setAlignment(Qt::AlignLeft);ui.statusBar->addPermanentWidget(m_qUserName,5);QLabel* label1 = new QLabel("mm");ui.statusBar->addPermanentWidget(label1,1);

2.1.1 QAction

说明:由于在使用QMenu的时候,发现其不能如同按钮一样,百思不得其解。最后请教师兄,师兄教我直接对statusBar添加QAction,槽函数响应triggered()信号。而且经过测试,发现QMenu并不具有triggered()信号。

代码:

 QAction *pProjects = new QAction("项目",nullptr);QAction *pCustomers = new QAction("客户", nullptr);QAction *pTemplates = new QAction("模板", nullptr);QAction *pVersion = new QAction("版本", nullptr);QAction *pAbout = new QAction("关于", nullptr);ui.menuBar->addAction(pProjects);ui.menuBar->addAction(pCustomers);ui.menuBar->addAction(pTemplates);ui.menuBar->addAction(pVersion);ui.menuBar->addAction(pAbout);connect(pCustomers, SIGNAL(triggered()), this, SLOT(OnBtCustomClicked()));connect(pTemplates, SIGNAL(triggered()), this, SLOT(OnBtTemplateClicked()));

2.2 设置弹窗的模态

功能:阻止父窗口

代码:

// m_pAddName是widget类型的子窗口m_pAddName->setWindowFlags( this->windowFlags() | Qt::Dialog);m_pAddName->setWindowModality(Qt::ApplicationModal); // 设置为模态

2.3 获取计算机用户名

代码:

 QString userName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);userName = userName.section("/", -1, -1);

2.4 QDoubleSpinBox的响应事件

功能:实现QDoubleSpinBox的键盘修改响应事件和点击增加减少的响应事件

代码:

 ui.m_dsbWidth->setKeyboardTracking(false);   // 键入数据失去焦点后才响应,如不添加,则一变就响应connect(ui.m_dsbWidth, SIGNAL(valueChanged(double)), this, SLOT(onDSBWidthValueChanged(double)));ui.m_dsbHeight->setKeyboardTracking(false);

2.5 QPainter

移动坐标轴,缩放坐标系,使作图区域填满并位于frame的中心。

代码:

 QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.translate(iCanvasWidth / 2 + dCanvasX, iCanvasHeight / 2 + dCanvasY);    // 将原点设为frame的中心double dEdgeWidth, dEdgeHeight;     // 矩形边框double scale;    // 坐标轴缩放因子dEdgeWidth = dShapeWidth + 40;dEdgeHeight = dShapeHeight + 40;double scaleWidth = (iCanvasWidth - 20) / dEdgeWidth;double scaleHeight = (iCanvasHeight - 20) / dEdgeHeight;scale = std::min(scaleWidth, scaleHeight);painter.scale(scale, scale);      // 缩放坐标系

2.6 QPainterPath

画槽形环

效果图:

代码:

 QPainterPath path;path.addRoundedRect(-dShapeWidth / 2, -dShapeHeight / 2, dShapeWidth,dShapeHeight, dShapeRadius, dShapeRadius);   // 外轮廓path.addRoundedRect(-dShapeWidth / 2 - 10, -dShapeHeight / 2 - 10, dShapeWidth + 20,dShapeHeight + 20, dShapeRadius + 10, dShapeRadius + 10); // 内轮廓painter.fillPath(path, Qt::red);

2.7 圆心定位算法

该算法是一道数学难题,很难得到一个通用的圆心坐标公式。经过与导师讨论,想到另外一种解决问题的方法。

解题思路:利用图像处理的方法遍历确定圆心

1.创建一个Mat类的灰度图,初始化为255。

2.给矩形槽区域外的像素赋值0。0像素代表不合理的位置,或者已存在圆。

3.由下向上遍历,并同时由中间向两边遍历。以(i,j)为中心的圆形区域,若没有0像素,则记录该位置。

由此方式可近似确定圆心坐标,其中精度由实际尺寸与像素比决定,即分辨率决定。

贴出一张结果图:输出圆心坐标,bool类型的是否溢出:0-溢出 1-未溢出

源码:

calcularCenter.h

#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;struct CableData
{int iAmount;double dOutDiameter;
};// 获取电缆圆心坐标
bool GetPoints(const vector<CableData>& vData, vector<Point2f>& vPoints, double dWidth, double dHeight, double dRadius);
// 添加一个圆,传出圆心坐标
bool AddCircle(Mat& image, Point2i &point, const int& radius);
// 将一个添加的圆涂黑
void PaintBlack(Mat& image, const Point2i& point, const int& diameter);
// 圆心在point的圆是否存在像素为0的点,true:没有像素为0的点,false:有像素为0的点
bool IsContain0(const Mat& image, const Point2i& point, const int& diameter);
// 计算2点之间距离的平方
int GetDistance(const Point2i& p1, const Point2i& p2);

calcularCenter.cpp

#include "calcularCenter.h"bool GetPoints(const vector<CableData>& vData, vector<Point2f>& vPoints, double dWidth, double dHeight, double dRadius)
{double scale;if (dWidth > dHeight)scale = 1000 / dWidth;elsescale = 1000 / dHeight;int nRow = ceil(dHeight * scale);int nCol = ceil(dWidth * scale);int nRadius = ceil(dRadius * scale);int nRadius_2 = ceil(dRadius * scale * dRadius * scale);Mat src(nRow,nCol,CV_8UC1,Scalar::all(255));if (!src.data){cout << "error!" << endl;return false;}//将圆角矩形外面涂黑for(int i = 0; i < nRow; i++)for (int j = 0;  j < nCol; j++){if (i < nRadius && j < nRadius && GetDistance(Point2i(j, i), Point2i(nRadius, nRadius)) > nRadius_2){src.data[i*nCol + j] = 0;}else if (i > nRow - nRadius && j < nRadius && GetDistance(Point2i(j, i), Point2i(nRadius, nRow - nRadius - 1)) > nRadius_2){src.data[i*nCol + j] = 0;}else if (i < nRadius && j > nCol - nRadius && GetDistance(Point2i(j, i), Point2i(nCol - nRadius - 1, nRadius)) > nRadius_2){src.data[i*nCol + j] = 0;}else if (i > nRow - nRadius && j > nCol - nRadius && GetDistance(Point2i(j, i), Point2i(nCol - nRadius - 1, nRow - nRadius - 1)) > nRadius_2){src.data[i*nCol + j] = 0;}else{}}bool flag = true;// 逐个添加圆int size = vData.size();int k;for (k = size - 1; k >= 0; k--){int radius = floor(vData[k].dOutDiameter * scale / 2);for (int m = 0; m < vData[k].iAmount; m++ ){Point2i point;if (AddCircle(src, point, radius)){Point2f _point;_point.x = point.x / scale - dWidth / 2;_point.y = point.y / scale - dHeight / 2;vPoints.push_back(_point);}elseflag = false;}}imshow("src",src);imwrite("1.bmp", src);waitKey(0);return flag;
}/***************************************************************************** 函数:AddCircle*  功能:添加一个圆,成功返回true; 不成功,则表示溢出,返回false.传出圆心坐标*  参数:image,已确定圆的图像,point,传出的圆心坐标, radius,添加圆的半径*  返回值:bool****************************************************************************/
bool AddCircle(Mat & image, Point2i& point, const int & radius)
{int nRow = image.rows;int nCol = image.cols;for (int i = nRow - 1; i > 0; i--){for (int j = nCol / 2 - 1; j > 0; j--){if (IsContain0(image, Point2i(j, i), radius)){PaintBlack(image, Point2i(j, i), radius);point = Point2i(j, i);return true;}}for (int j = nCol / 2; j < nCol; j++){if (IsContain0(image, Point2i(j, i), radius)){PaintBlack(image, Point2i(j, i), radius);point = Point2i(j, i);return true;}}}return false;
}/***************************************************************************** 函数:PaintBlack*  功能:将添加的圆涂黑*  参数:image,被修改的图片;point,圆心坐标;radius,半径大小*  返回值:void****************************************************************************/
void PaintBlack(Mat & image, const Point2i & point, const int & radius)
{int nRow = image.rows;int nCol = image.cols;int iR_2 = radius * radius;for (int i = point.y + radius; i >= point.y - radius; i--)for (int j = point.x + radius; j >= point.x - radius; j--){if (GetDistance(Point2i(j, i), point) <= iR_2){image.data[i*nCol + j] = 0;}}
}/***************************************************************************** 函数:IsContain0*  功能:判断圆心在该点的圆是否相交*  参数:image,已确定的圆的图像; point,圆心; radius,半径大小*  返回值:bool****************************************************************************/
bool IsContain0(const Mat & image, const Point2i & point, const int & radius)
{int nRow = image.rows;int nCol = image.cols;int iR_2 = radius * radius;if (point.x - radius < 0 || point.x + radius > nCol - 1|| point.y - radius < 0 || point.y + radius > nRow - 1)return false;for (int i = point.y + radius; i >= point.y - radius; i--)for (int j = point.x + radius; j >= point.x - radius; j--){if (GetDistance(Point2i(j, i), point) <= iR_2){if (image.data[i*nCol + j] == 0)return false;}}return true;
}/***************************************************************************** 函数:GetDistance*  功能:计算2点距离*  参数:p1,p2像素坐标*  返回值:double****************************************************************************/
int GetDistance(const Point2i & p1, const Point2i & p2)
{return  (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
}

main.cpp

#include "calcularCenter.h"int main()
{vector<CableData> vCableData;vector<Point2f> vPoints;vector<Point2f> vPoints1;bool flag;CableData data;data.iAmount = 3;data.dOutDiameter = 5;vCableData.push_back(data);data.iAmount = 3;data.dOutDiameter = 20;vCableData.push_back(data);flag = GetPoints(vCableData, vPoints, 100, 100, 50);for (int i = 0; i < vPoints.size(); i++){cout << "(" << vPoints[i].x << ", " << vPoints[i].y << ")" << endl;}cout << int(flag)<< endl;system("pause");return 0;
}

反思:

由于功能需要,在已插入小圆后,需要求用标准直径的小圆填充大圆的小圆个数。最初使用的方法是,直接在getPoints函数后面添加对溢出的判断,若未溢出,则对标准直接的圆addCircle(),nFiller数字为记录的数量。代码如下:

 // 逐个添加圆for (int k = size - 1; k >= 0; k--){int tmp = GetSleeveDiameter(vData[k].dOutDiameter);int radius = floor(tmp * scale / 2);for (int m = 0; m < vData[k].iAmount; m++){Point2i point;if (AddCircle(src, point, radius)){QPointF _point;_point.rx() = point.x / scale - dWidth / 2;_point.ry() = point.y / scale - dHeight / 2;vPoints.push_back(_point);vDiameter.push_back(vData[k].dOutDiameter);}elseflag = false;}}// add filler sleeves//if (flag)//{// Point2i point;//    int radius = floor(27 * scale / 2);//  while (AddCircle(src,point, radius))//  {//     nFiller[0]++;//   }// radius = floor(19 * scale / 2);//  while (AddCircle(src, point, radius))// {//     nFiller[1]++;//   }// //radius = floor(12 * scale / 2);//    //while (AddCircle(src, point, radius))//   //{//   //  nFiller[2]++;//   //}//}

其结果导致每一次刷新耗费大量事件计算十分卡顿。

总结

由于paintEvent()是一个不断重复执行的函数,如果每一次主要数据没有变化的情况下paintEvent()事件响应,都得执行该圆心的求解,十分耗时,界面卡顿。因此,要考虑在数据没有变化的情况下,直接利用上一次计算的结果,而不用重复计算。同理,对于求填充填充圆的个数时,这个数据只有在特定情况下才需要(本项目是在响应按钮save和report时),不必要对每次数据变化计算该值。最后解决的办法时将GetPoints函数中的Mat类型作为一个全局变量,在按钮save和report的槽函数添加计算该值的代码。如下:

 // add filler sleevesint a[2] = { 0 };if (m_isFill){Point2i point;int radius = floor(27 * scale / 2);while (AddCircle(src,point, radius)){a[0]++;}radius = floor(19 * scale / 2);while (AddCircle(src, point, radius)){a[1]++;}}m_penetration.iFillerSleeve27Amount = a[0];m_penetration.iFillerSleeve19Amount = a[1];

将 PaintBlack()替换为opencv自带的函数 circle(image, Point2i(j, i), radius, Scalar(0), -1);

2.8 排序输出

在项目中,得到一个vector<struct>数据,struct包含多种数据,要按照A方式输出,当A相同时,按照B输出。为了实现该功能,我采用Map<int, Map<String, int>>的数据结构,将数据改造成该种类型,利用map对key的排序,然后遍历map输出数据。

部分代码:

QMap<int, QMap<QString, int>> mapInsertSleeves;int sizeOfPene = m_qvPenetrations.size();for (int i = 0; i < sizeOfPene; i++){if (!mapInsertSleeves.contains(m_qvPenetrations[i].iFillerSleeve19Length)){mapInsertSleeves[m_qvPenetrations[i].iFillerSleeve19Length] = QMap<QString, int>();}QMap<QString, int> &_map = mapInsertSleeves[m_qvPenetrations[i].iFillerSleeve19Length];int sizeOfCable = m_qvPenetrations[i].qvCables.size();QString str;for (int j = 0; j < sizeOfCable; j++){int d = PenetrationDetails::GetSleeveDiameter(m_qvPenetrations[i].qvCables[j].dOutDiameter);if (d == 12)str = "D12/6";else if (d == 15)str = "D15/8";else if (d == 17)str = "D17/10";else if (d == 19)str = "D19/12";else if (d == 21)str = "D21/14";else if (d == 23)str = "D23/16";else if (d == 27)str = "D27/19";else if (d == 31)str = "D31/23";else if (d == 35)str = "D35/27";else if (d == 41)str = "D41/31";else if (d == 46)str = "D46/36";else if (d == 52)str = "D52/42";else if (d == 58)str = "D58/48";else if (d == 64)str = "D64/54";elsestr = "D70/60";if (_map.contains(str)){_map[str] += m_qvPenetrations[i].qvCables[j].iAmount;}else{_map[str] = m_qvPenetrations[i].qvCables[j].iAmount;}}}

回顾:

昨天学习数据结构与算法一书时,无意中在某博客看到:

priority_queue<pair<int, int>, vector<pair<int, int>>, greater(pair<int, int>)>的结构,在first相等时,可以按second排序,可以应用到此处。

2.9 QFileDialog::getSaveFileName

保存文件到桌面,并设置默认的文件名。代码:

 QString defaultSaveName = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "./" + m_penetration.qstrPenetration + ".doc";QString outFileName = QFileDialog::getSaveFileName(this, QStringLiteral("请输入要保存的名字:"), defaultSaveName, "Microsoft Word 97-2003(*.doc);;Microsoft Word 2007-2013(*.docx)");if (outFileName.isEmpty()) {QMessageBox::warning(this, tr("警告"), tr("输入的文件名为空!"), QMessageBox::Ok);return;}Report(outFileName);

2.10 Qt 输出word

使用封装好的源码QWordDemo.zip,源码出处已遗忘,以后若想起补上。

Qt开发一个小软件记录相关推荐

  1. 开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号。

    开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号. 处理:1.记录最多8条错误记录,对相同的错误记录(即文件名称和行号完全匹配)只记录一条,错误计数增加:(文件所在的目录不同,文 ...

  2. 基于Qt开发PC客户端软件

    Qt 软件库用来开发PC端软件非常方便,如果有一套系统,即有服务端.移动端及PC端,那么PC端最适合用Qt来开发,设计界面所见即所得.基于C++语言高效灵活.下面基于本人之前开发的一个小软件来说说. ...

  3. java学习笔记(二十八)——开发一个小项目(VMeeting3.0)

    上篇文章按照较规范的产品需求文档梳理了项目的逻辑,感觉开发起来明晰了很多:挂上一篇文章java学习笔记(二十七)--开发一个小项目(VMeeting2.0)_Biangbangbing的博客-CSDN ...

  4. 嵌入式Qt 开发一个视频播放器

    上篇文章:嵌入式 Qt开发一个音乐播放器,使用Qt制作了一个音乐播放器,并在OK3568开发板上进行了运行测试,实际测试效果还不错. 本篇继续来实现一个Qt视频播放器软件,可以实现视频列表的显示与选择 ...

  5. Qt开发上位机软件建立经典蓝牙通讯

    Qt开发上位机软件建立经典蓝牙通讯 之前做了一个具有经典蓝牙通讯功能的Windows上位机软件,在网上学习了相关博客以及参考了官方经典蓝牙例程之后,总结出了使用Qt建立经典蓝牙通讯的步骤,附带相关源码 ...

  6. [洪流学堂]Hololens开发入门篇3:使用基本功能开发一个小应用

    本文首发于"洪流学堂"公众号. 洪流学堂,让你快人几步 本教程基于Unity2017.2及Visual Studio 2017 本教程编写时间:2017年12月4日 本文内容提要 ...

  7. 【如何开发小程序?】如何快速开发一个小程序

    ​ 在过去,对于新手来说,如何开发一个小程序只需要半个月到一个月的时间来制作一个简单的小程序.在中间,您需要了解小程序代码的逻辑语言.您需要查看微信官方平台开发文档中的大量示例和示例.那么现在如何开发 ...

  8. 如何用matlab制作一个小软件

    转:https://www.ttin.top/2018/03/29/TT0012/ 制作一个小软件的方法很多,比如说c++的MFC,本站在之前的第一个MFC文章里就提到过:matlab的GUIDE,在 ...

  9. 制作ico图标的一个小软件

    最近在写软件的过程中,遇见了如下问题: 1.libpng warning:iCCP:known incorrect sRGB profile 在网上搜索解决方案为:将图片用QIMAGE读取再重新保存就 ...

最新文章

  1. select fd_set
  2. mongodb创建用户名和密码_mongodb用studio3T进行数据备份和用户的创建
  3. 前端学习(1713):前端系列javascript之运行
  4. android简单的夜间模式
  5. 5条件筛选功能_一分钟,彻底学会Excel高级筛选,坐等升职加薪!
  6. 图表示学习(Graph Representation Learning)笔记
  7. 人工智能必备数学知识· 学习笔记 ·001【线性回归,最小二乘法梯度下降法】
  8. Node.js 4.0 中的 ES 6 特性介绍
  9. Mozilla Firefox安装Firepath Firebug找xpath最新可用的方法附老版本Firefox下载链接
  10. [转贴]鲁棒性——健康的系统
  11. 非网络打印机/扫描仪无线解决方案
  12. EnergyPlus笔记
  13. PS修改图片上的文字
  14. 指针式万用表测量电容
  15. Android 兼容Android 7拍摄照片/打开相册/选择照片/剪裁照片/显示照片 带demo
  16. 硅谷录用的计算机专业大学排名,学计算机科学专业,必选硅谷附近的这些加州大学...
  17. html制作古诗念奴娇,古诗文《念奴娇 赤壁怀古》原文|注释|赏析 - 可可诗词网...
  18. Spring cloud微服务 Hystrix熔断器
  19. html实现读取读卡器,如何在web浏览器页面使用IC卡读卡器并且兼容所有浏览器
  20. 坐月子 请月嫂吗?如何请月嫂?

热门文章

  1. 前段黑客技术学习①——XSS
  2. micropython plc_合信plc编程软件下载-MagicWorks编程软件下载v2.16 官方版-西西软件下载...
  3. STM32 的 USB 控制器
  4. stm32 例程中lcd颜色初始化显示
  5. nowcoder 孤傲的A
  6. uniapp -- 本地数据存储
  7. Java重定向sendRedirect与请求转发forword的区别
  8. 请求转发(forword())和请求包含(include())
  9. Midori64 加解密的实现(Java代码)
  10. 光电二极管运放电路的线性和如何减小偏置