QT编程小技巧

一 QT定时器

  Qt有两种定时器:一种是QObject类的定时器,另一种是QTimer类的定时器。

 1.1 QObject类的定时器:

  QObject类提供了一个基本的定时器,通过函数startTimer()来启动,通过killTimer()来结束,通过QTimerEvent来处理定时器事件。

  三个主要函数:

  ①stratTimer(int interval,Qt::TimerType timerType=Qt::CoarseTimer);
  ②void killTimer(int id);
  ③void QObject::timerEvent(QTimer *event);

   startTimer(int interval)启动一个时间间隔为interval毫秒的定时器,返回一个定时器标识符,如果未能启动成功,则返回0。

  该定时器只能使用killTime()来杀死,killTimer(int id)通过定时器标识符来杀死定时器。

  如果有多个定时器,可以通过QTimerEvent::timerId()来获取已经启动的定时器标识符。

 1.1.1 使用的示例代码:
int m_timerId;  //定时器ID    在头文件中定义
 void DynamicTrackDlg::on_btnToTrack_clicked()
 {
   //启动定时器,设置检查定时器的时间间隔,单位ms
    m_timerId = startTimer(20);
 }
void DynamicTrackDlg::timerEvent(QTimerEvent *event)   
{
      if (event->timerId() == m_timerId)
     {
           ...
           killTimer(m_timerId);   //关闭定时器
     }
}

 1.2 QTimer类的定时器

  QTimer类定时器是QObject类定时器的扩展版或者说升级版,因为它可以提供更多的功能。比如说,它支持单次触发和多次触发。

使用QTimer类定时器的步骤:

  1. 创建一个QTimer定时器实例:QTimer *timer = new QTimer(this);
  2. 连接超时信号与槽:connect(timer, SIGNAL(timeout()), this, SLOT(testFunc()));
  3. 启动定时器start();
  4. 适时关闭定时器:stop();
  5. 删除定时器实例:delete timer;

二 QT界面布局

 2.1 如何将布局上下按比例设置大小?

这里写图片描述

   方法:先点全选,右侧的布局“layoutRowStretch”and“layoutColumnStretch”进行设置即可。

三 QT5+VS2013程序发布

 3.1 骤如下:

  1. 首先,在VS界面选择Release+x64(你要发布32,则选择win32,前提是你装了32位的 Qt)。
  2. 生成-重新生成解决方案。
  3. 打开-文件夹Release文件,选择生成的exe文件。
  4. 桌面新建文件,并将exe文件拷贝至此。
  5. 使用Qt自带工具查找dll等文件。

    ①.将Qt安装目录的bin的路径添加到环境变量

    我的Qt-bin路径

    添加变量:计算机-右键-属性-高级系统设置-环境变量-系统变量-path-编辑(记得变 量之间用分号隔开);

    ②.点击开始-cmd-回车进入命令提示符,使用cd命令进入刚刚创建的A文件夹

    ③.现在开始关键一步:接着输入:windeployqt xx.exe (xx为exe文件名)。

  6. 现在A文件夹下会出现许多文件,把A文件夹拷贝给其他人的电脑就可用了。

四 int与QString相互转化

 4.1 int转化为QString

//头文件
#include <sstream>
#include<QString>        
//函数实现
int imgNumber=50stringstream NextImg;
NextImg << imgNumber;
ui.editImgNum->setText(NextImg.str().c_str());  //  void setText(const QString &);  

 4.2 将QString转化为Int

//头文件
#include<QString>     
//函数实现
QString mString = ui.lineEditPixelThreshold->text();
int nPixelThreshold = mString.toInt();

五 设置打开文件路径+读取该目录下的所有图片

 5.1 设置打开文件路径的程序中无中文时

  前提:在该工程文件下,先新建一个文件夹image,在其下新建两个文件夹 L和R。

//头文件
#include<QFileDialog>
public slots:
  void on_setPathButton_clicked();  //此为ui中设置打开文件路径(pushButton)中的按钮名;
//函数实现
void on_setPathButton_clicked()
{
     //设置左右相机拍摄图像的上一级目录
    QString imgPath = QFileDialog::getExistingDirectory(this,“Set Path”,"./image",QFileDialog::Option::ShowDirsOnly);
    //确定左右图像路径
     QString   imgPathL = imgPath + "/L";
     QString   imgPathR = imgPath + "/R";
     //如果此时需要将界面中的下一个按钮设置为可用
    ui.btnOpenCams->setEnabled(true);  //此处 btnOpenCams为打开相机的pushButton; 同时需要将ui中的btnOpenCams属性的enabled后面的勾号去掉。
}

 5.2 设置打开文件路径的程序中有中文时

  5.2.1 前提:
  • 在该工程文件下,先新建一个文件夹ImgFiles。
  • 在项目中包含头文件:GBK.h
  • 做好的界面如下:
    这里写图片描述
  5.2.2 代码示例:
//头文件中
#include<QFileDialog>
#include<GBK.h>
public slots:
    void on_setPathButton_clicked();  //此为ui中设置打开文件路径(pushButton)中的按钮名;
//函数实现:
void on_setPathButton_clicked()
{
   //设置图像的上一级目录
   QString imgPath = QFileDialog::getExistingDirectory(this, GBK::ToUnicode("设置图像      打开路径"), "D:/ImgFiles/", QFileDialog::Option::ShowDirsOnly);
    //读取该文件夹下的所有图片
    QDir dir(imgPath);
    QStringList nameFilters;
    nameFilters << "*.jpg";  //此处根据文件目录下存放的图片格式来定 .bmp 等
    dir.setNameFilters(nameFilters);
    dir.setSorting(QDir::Name);
    //获取路径中图像名列表
    imgNameList = dir.entryList();
    QString tem = imgPath;
    tem = tem + "/";
    //imgNameList存放着每张图片的路径
    for (int i = 0; i < imgNameList.size();i++)
     {
           imgNameList[i].prepend(tem);  //pretend()是插入的意思。
      }
      //将图片总数显示出来
     std::stringstream str;
     str << imgNameList.size();
     ui.editTotalNum->setText(str.str().c_str()); //editTotalNum为对应的lineEdit名
     //如果此时需要将界面中的下一个按钮设置为可用
    ui.btnReadImg->setEnabled(true);  //此处btnReadImg 为读取图片的pushButton; 同时需要将ui中的btnReadImg 属性的enabled后面的勾号去掉。
}

 5.3 读取该路径下的图片

  5.3.1 在5.2的基础上
//头文件
#include<QPixel>
public slots:
    //读取当前图片
    void on_btnReadImg_clicked();
public:
    //待检测图像名列表
    QStringList imgNameList;
   //用于存储当前显示图像的序号
    int imgNum;
   //存储当前显示图像pixmap
    QPixmap *pixmap;
  //当前图像的宽高
    int imgWidth;
    int imgHeight;
  //存储当前待检测图像的Mat
    Mat drawImg;//三通道
    Mat grayImg;//单通道图像

// 函数实现
void 类名::on_btnReadImg_clicked()
{
        pixmap = new QPixmap(imgNameList[imgNum]);
        ui.ImgLabel ->setPixmap(*pixmap);  //ImgLabel为ui中图片显示的控件名
        imgWidth = pixmap->width();     //读取图像宽度
        imgHeight = pixmap->height();  //读取图像高度
        drawImg = cv::imread(imgNameList[imgNum].toStdString(), CV_LOAD_IMAGE_COLOR);   //转化为Mat
        cvtColor(drawImg, grayImg, CV_BGR2GRAY);
        stringstream PicNum;
        int PicNumber = imgNum + 1;
        PicNum << PicNumber;
        ui.editImgNum->setText(PicNum.str().c_str()); //显示当前图片序号
        ui.btnNextImg->setEnabled(true); //开启读取下一张图片
        ui.btnToDetectSampleImgs->setEnabled(true); //开启检测图片按钮
}
  5.3.2 读取下一张图片的按钮
//头文件
#include<QPixel>
public slots:
   //下一张图片
  void on_btnNextImg_clicked();


//函数实现
  void 类名::on_btnNextImg_clicked()
{
        //清空显示结果的lineEdit内容
        ui.editResult->clear();
        imgNum++;
        //和读取图片的过程相似
        pixmap = new QPixmap(imgNameList[imgNum]);
        ui.ImgLabel->setPixmap(*pixmap);
        imgWidth = pixmap->width();
        imgHeight = pixmap->height();
        drawImg = cv::imread(imgNameList[imgNum].toStdString(), CV_LOAD_IMAGE_COLOR);
        cvtColor(drawImg, grayImg, CV_BGR2GRAY);
        //显示下一张图片张数
        stringstream NextImg;
        int imgNumber = imgNum + 1;
        NextImg << imgNumber;
        ui.editImgNum->setText(NextImg.str().c_str());

        //判断图片是否结束
        imgNum++;
        if (imgNum==imgNameList.size())
        {
               ui.btnNextImg->setEnabled(false);
        }
        imgNum--;
}

六 界面与对话框的退出

 6.1 QT整个界面的退出

1)头文件
public slots:
    //退出
        void on_btnExit_clicked();
2)函数实现
void 类名::on_btnExit_clicked()
{
        exit(0);
}

 6.2 QT对话框的关闭

1)头文件
public slots:
    //停止跟踪编码点
    void on_btnStop_clicked();

2)函数实现
void 类名::on_btnStopTracking_clicked()
{
        this->close();
}

七 子窗口访问父窗口的成员函数与成员变量

父窗口定义的类:Father;

子窗口定义的类:Son;

 void Son::on_pushButton_OutputText_clicked()
{
    //通过调用如下这句,子窗口便可以调用父窗口中的成员函数和成员变量了。
    Father *p = (Father *) parentWidget(); //关键之处就是这里!
    m_SonUi.plainTextEdit_OutputText->appendPlainText( p->GetString());
}

八 删除文件夹内或者文件夹内的所有文件

 8.1 头文件

include <QtCore/QCoreApplication>
#include <QDir>
#include <QFileInfoList>
#include <qfileinfo>
#include <QFile>
#include <QDebug>

 8.2 函数实现

void Dynamic3DTracking::removefilesindir(const QString& path)
{
        QDir dir(path);
        QFileInfoList info_list = dir.entryInfoList(QDir::Files | QDir::Hidden |  QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::AllDirs);
        foreach(QFileInfo file_info, info_list)
        {
               if (file_info.isDir())
               {
                       removefilesindir(file_info.absoluteFilePath());
               }
               else if (file_info.isFile())
               {
                       QFile file(file_info.absoluteFilePath());
                       qDebug() << "remove file  : " << file_info.absoluteFilePath();
                       file.remove();
               }
        }
        QDir temp_dir;
        //temp_dir.rmdir(path) ; //删除文件夹及里面的文件 
        temp_dir.remove(path); //删除文件夹里面的文件,不包括文件夹本身
        qDebug() << "remove empty dir : " << path;
}

 8.3 主函数

int main(int argc, char* argv[])
{
  QCoreApplication a(argc, argv);
 //删除该文件夹下的所有文件
  removefilesindir("G:/ProjectFiles/QTProjectFiles/DynamicTrack3D(QT)_new/Dynamic3DTracking/DataFiles/ImageFiles/image/R/");
  return a.exec();
}

九 如何在lineEdit中输出int类型的变量

int imgNumBeDealed=20;
   stringstream str;
   str << imgNumBeDealed;
   ui.lineEditImgNum->setText(str.str().c_str());

十 Mat与QImage相互转化的函数

 10.1 Mat转化成QImage

QImage DynamicTrackDlg::cvMat2QImage(const cv::Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS = 1  
    if (mat.type() == CV_8UC1)
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        // Set the color table (used to translate colour indexes to qRgb values)  
        image.setColorCount(256);
        for (int i = 0; i < 256; i++)
        {
            image.setColor(i, qRgb(i, i, i));
        }
        // Copy input Mat  
        uchar *pSrc = mat.data;
        for (int row = 0; row < mat.rows; row++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, mat.cols);
            pSrc += mat.step;
        }
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3  
    else if (mat.type() == CV_8UC3)
    {
        // Copy input Mat  
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat  
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return image.rgbSwapped();
    }
    else if (mat.type() == CV_8UC4)
    {
        qDebug() << "CV_8UC4";
        // Copy input Mat  
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat  
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
}

 10.2 QImage转化成Mat的函数

cv::Mat DynamicTrackDlg::QImage2cvMat(QImage image)
{
    cv::Mat mat;
    qDebug() << image.format();
    switch (image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
        cv::cvtColor(mat, mat, CV_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    }
    return mat;
}

十一 QGraphicsView类的简单用法

 11.1 封装好的.h和.cpp文件

 11.2 关于上述的.h和.cpp文件的使用方法:

      m_scene = new QGraphicsScene();
      m_scene->addItem(&imgItem);
      ui.graphicsView->setScene(m_scene);
      ui.graphicsView->setDragMode(QGraphicsView::NoDrag);
      z = new Graphics_view_zoom(ui.graphicsView);
      z->set_modifiers(Qt::NoModifier);

十二 关于QT多线程

    由于最近琢磨了好久,利用多线程来控制相机外触发拍摄图片,因而,此处将项目中的多线程知识做个梳理。

    QT中自带多线程类,因而我是直接继承使用QT中QThread类的。

 12.1 在工程中添加一个类,继承QThread

这里写图片描述

这里写图片描述
这里写图片描述

  //开辟两个线程
        CamCapThread *threadL,threadR;
        threadL = new CamCapThread(LRThread::L);
    threadR = new CamCapThread(LRThread::R);
    //线程的入口
        threadL->start();
    threadR->start();
        //线程的执行
    threadL->run(LRThread::L,vecImgL1,NumRetrieveBuffer);
    threadR->run(LRThread::R,vecImgR1,NumRetrieveBuffer);
    //关闭线程 需要先后调用这三个函数 quit() wait() delete thread
    threadL->quit();
    threadR->quit();
    threadL->wait();
    threadR->wait();
    delete threadL;
    delete threadR;

十三 从主窗口启动子窗口

  主窗口的类:QuickCalculate
  子窗口的类:imgshowDlg

void QuickCalculate::on_btn_ShowImg_clicked()
{
    imgshowDlg imgshowDlgModel(this);
    imgshowDlgModel.exec();
}

十四 QMessageBox消息窗口的调用

  以下提供几段代码来说明QMessageBox的常见用法:
  头文件:include”QMessageBox”

QMessageBox::critical(this,GBK::ToUnicode("坏消息"),GBK::ToUnicode("左相机关闭失败,请检查"),
            QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Ok);
QMessageBox::information(NULL, GBK::ToUnicode("友情提示"), GBK::ToUnicode("图片已经拍摄完成"));
QMessageBox::critical(this,GBK::ToUnicode("坏消息"),GBK::ToUnicode("左相机连接失败,请检查"));

十五 vs2015+QT5添加多个子对话框

  在主对话框中Form Files上右键->添加->(最下面)Add Qt Class->Qt GUI Class->修改类名,则能生成对应ui文件、.h文件及.cpp文件啦。注意:此处的基类选择:QDialog

  注意:Form Files上右键之前,点击Form Files,QT插件中,点击“convert project to qmake generated project”

十六 QT中如何显示调试界面

  打开QT工程——>项目属性:
这里写图片描述

  在命令行输入:

editbin /SUBSYSTEM:CONSOLE "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).exe"

十七 QT如何处理密集型耗时的事情(频繁调用QApplication::processEvents)

  
有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致界面无法响应,处于“假死”状态。例如:在应用程序中保存文件到硬盘上,从开始保存直到文件保存完毕,程序不响应用户的任何操作,窗口也不会重新绘制,从而处于“无法响应”状态,这是一个非常糟糕的体验 。

在这种情况下,有一种方法是使用多线程,即在子线程中处理文件保存,主线程负责界面相关。

而如果不想使用多线程,最简单的办法就是在文件保存过程中频繁调用QApplication::processEvents()。该函数的作用是让程序处理那些还没有处理的事件,然后再把使用权返回给调用者。

bool MyApp::writeFile(const QString &filename)
{
     QFile file(filename);
...
    QApplication::setOverrideCursor(Qt::WaitCursor);
     for(int r = 0; r != rowCount; ++r)
     {
          for(int c = 0; c != colCount; ++c)
          {
               out << table(r,c);   
               qApp.processEvents();
          }
     }
    QApplication::restoreOverrideCursor();
}

这样一来,程序就能响应了。

但是,该方法有一个问题:可能正在保存文件的过程中,用户不小心又单击了保存,或不小心关闭了程序主窗口,这样会产生意想不到的后果。

解决这个问题的最简单的办法是替换成:

qApp->processEvents(QEventLoop::ExcludeUserInputEvents);//它可以忽略用户的输入(鼠标和键盘事件)。

这里写图片描述

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

3D视觉工坊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值