[Qt]一个简单的Qt Widget多线程处理图像的例子

本文介绍了一个使用Qt多线程实现图像灰度化的简单示例。通过创建自定义线程类MyImageProcessThread处理图像,并在主线程更新UI显示处理结果。文章提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近开始学习Qt。发现Qt的资料比起不好用的MFC实在是不多啊。今天刚好看到了Qt多线程,就写了个小例子放出来,希望能帮到跟我一样的初学者。

简单讲下。程序就两个界面。一个主要的QDialog,一个显示图像的自定义控件MyPicBox 。还有就是动态创建了一个QMainWindow,因为我要用QMainWindow::setCentralWidget来放置MyPicBox。
软件的作用是多线程实现图像的灰度化(因为这个足够简单,毕竟这个DEMO的重点不是讲图像处理),然后把处理结果显示出来。大概呢,就是下面这个样子。
不要问我照片里的人是谁。哈哈哈哈~~~~
[Qt]一个简单的Qt Widget多线程处理图像的例子 - code monster - 仗剑天下的博客
  
好了,我还是直接上代码吧。一切都在注释中:
首先是主对话框类.h(窗口标题是”选择图片“那个):
#pragma once
#include <QDialog>
#include <QImage>
#include "my_thread.h"
namespace Ui {
class MainDialog;
}
/**
 * @brief The MainDialog class
 * 主对话框类
 */
class MainDialog : public QDialog
{
    Q_OBJECT
public:
    explicit MainDialog(QWidget *parent = 0);
    ~MainDialog();
private slots:
    void on_pushButton_selectImage_clicked();
    void on_pushButton_toGrayImage_clicked();
    /**
     * @brief showImageInNewWindow 接收由子线程信号传来的QImage指针,并显示这个QImage
     * @param image
     */
    void showImageInNewWindow(QImage* image);
    /**
     * @brief closeThread 如果关闭Dialog的时候子线程还没有结束,应该手动结束它
     */
    void closeThread();
    /**
     * @brief SetProgress 设置对话框中的进度条进度
     * @param max 进度条最大值
     * @param value 当前值
     */
    void SetProgress(int max,int value);
private:
    Ui::MainDialog *ui;
    MyImageProcessThread* thread;
    QString imagePath;
    //进度条的最大值是否已经设置,如果已经设置了就不需要重新设置了,不然UI会反复出现进度条增长
    bool hasProgressBarMaxSet;
    QImage* imageSelected;//原来的图片(无视,这里根本没用它)
    QImage* imageAfterProceess;//处理后的图片(无视,这里根本没用它)
};
然后是主对话框.cpp
#include <QDebug>
#include "my_thread.h"
#include "main_dialog.h"
#include "ui_maindialog.h"
#include "mypicbox.h"
#include <QFileDialog>
#include <QMainWindow>
MainDialog::MainDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MainDialog)
{
    ui->setupUi(this);
    ui->label_2->setVisible(false);
    ui->progressBar->setVisible(false);
    hasProgressBarMaxSet = false;
    this->thread = new MyImageProcessThread();
    //---------------为了方便显示,我固定了Dialog大小----------
    QSize size = this->size();
    this->setFixedSize(size);
    //------------------------------------------------------------
    connect(this,SIGNAL(destroyed()),this,SLOT(closeThread()));
    //在这里关联从图像处理线程发出的信号,然后用showImageInNewWindow来处理由信号传入的QImage指针
    connect(thread,SIGNAL(processFinished(QImage*)),this,SLOT(showImageInNewWindow(QImage*)));
    connect(thread,SIGNAL(processProgress(int,int)),this,SLOT(SetProgress(int,int)));
}
MainDialog::~MainDialog()
{
    delete ui;
}
void MainDialog::on_pushButton_selectImage_clicked()
{
    this->imagePath = QFileDialog::getOpenFileName();
    ui->lineEdit->setText(imagePath);
}
void MainDialog::on_pushButton_toGrayImage_clicked()
{
    if(this->imagePath.isEmpty())
    {
        qDebug()<<"imagePath is  empty !!!";
        return ;
    }
    qDebug()<<"Start a new thread !!!";
    //由于子线程的创建是在Dialog的构造函数中进行的,
那个时候图像路径还没有指定
     this->thread->setImagePath(this->imagePath);
     this->thread->start();
}


void MainDialog::showImageInNewWindow(QImage *image)
{
    QMainWindow*  picWindow = new QMainWindow(this);
    MyPicBox* picBox = new MyPicBox(image,picWindow);
    picWindow->setCentralWidget(picBox);
    //为了防止图像过大,这里对图像显示窗口的大小做了点儿限制
    if(image->width() > 600)
    {
	//压缩系数,以600宽度为准
        double scaleCoefficient =600.0/image->width();
	//重新设定窗口大小
        picWindow->resize(600,scaleCoefficient*image->height());
    }
    else if(image->height() > 800)
    {
        //压缩系数,以800高度为准
	double scaleCoefficient =800.0/image->height();
	//重新设定窗口大小
        picWindow->resize( scaleCoefficient*image->width(),800);
    }
   else
       picWindow->resize(image->width(),image->height());
   //----------------------------------------------------
    picWindow->setWindowTitle("处理结果");
    picWindow->move(100,100);//调整一下窗口位置
    picWindow->show();
}


void MainDialog::closeThread()
{
    if(this->thread->isRunning())
    {
        this->thread->terminate();
        qDebug()<<"Thread has been terminated !!!";
    }
}
void MainDialog::SetProgress(int max, int value)
{
  ui->label_2->setVisible(true);
  ui->progressBar->setVisible(true);
  if(!hasProgressBarMaxSet)
  {
      ui->progressBar->setMaximum(max);
      hasProgressBarMaxSet = true;
  }
  ui->progressBar->setValue(value);
}


---------------------------------------------------------我是分隔符------------------------------------------------------------------------------------------------
图像处理线程类.h:
#pragma once
#include <QThread>
#include <QImage>
#include <QObject>
/**
 * @brief The MyImageProcessThread class
 * 图像处理的线程类,需要传入一个QImage图像指针
 * 同时包含两个自定义信号
 */
class MyImageProcessThread : public QThread
{
    /*
     * 注意:必须添加Q_OBJECT宏使其能够应用Qt的信号槽机制,
     *不然你就没法更新UI界面啦
     */
    Q_OBJECT
public:
    MyImageProcessThread();
    MyImageProcessThread(QString& imagePath,QObject* parent = 0);
    ~MyImageProcessThread();
    /*
     *继承QThread类之后,必须实现 run() 函数。
     *在run函数中实现你要在子线程中运行的代码
     */
    virtual void run();
    QString getImagePath() const;
    void setImagePath(const QString &value);
signals:
    //自定义信号。表示处理结束了。
    //可以百度“自定义信号”查找更多内容
    void processFinished(QImage*);
    //自定义信号。用以表示算法进度。
    //maxValue表示进度最大值,progress表示当前值
    void processProgress(int maxValue,int progress);
private:
    QString imagePath;
    QImage* result;
};
然后是图像处理线程类.cpp
#pragma once
#include "my_thread.h"
#include <QDebug>
#include <QTime>
MyImageProcessThread::MyImageProcessThread()
{
}
MyImageProcessThread::MyImageProcessThread(QString &imagePath, QObject *parent):QThread(parent)
{
    if(!imagePath.isEmpty())
        this->imagePath = imagePath;
}
MyImageProcessThread::~MyImageProcessThread()
{
}
void MyImageProcessThread::run()
{
    //这里我们记录下当前系统时间,统计一下算法耗时
    QTime timer = QTime::currentTime();
    timer.start();//开始计时
   if(this->imagePath.isEmpty())
   {
        qDebug()<<"path is NULL";
       return;
    }
   //摘自某博文:
   //采用bits()方法的到的数据data中像素的组织形式应为ARGB,
   //但实际调试中发现,每个像素中从字节从低到高依次是BGRA,方向刚好反过来。
   QImage* image = new QImage(imagePath);
   unsigned char* data = image->bits();//取得QImage对应的byte数组
   int byteCount = image->byteCount();//byte数组长度
   unsigned char* dataNew = new unsigned char[byteCount];//再申请一个一样大小的数组
   int counter = 0;//循环记录处理了多少个字节
   int hasDone = 0;//记录累计处理的百分比
   for(int i  = 0 ; i <byteCount ; i+=4)
    {
        unsigned char grayByte = (unsigned char)(0.30 *data[ i+2] +0.59*data[ i + 1]+0.11*data[ i]);
        dataNew[i]     = grayByte;
        dataNew[i+1] = grayByte;
        dataNew[i+2] = grayByte;
        dataNew[i+3] = data[i+3];
        //每处理到字节总量的1/100就更新一次UI
        if(counter == (int)(byteCount/100))
          {
            counter = 0;
            hasDone +=1;
            emit processProgress(100,hasDone);
            this->sleep(1);//这里我让线程休眠1秒,不然进度条瞬间就走完了。
            }
        else
           counter++;
    }
   emit processProgress(100,100);
   //生成新的QImage
   int w = image->width();
   int h = image->height();
   QImage* newImage = new QImage(dataNew,w,h,image->format());
  //取得算法结束的时间
  int timeTaken = timer.elapsed();
  qDebug()<<"Processing takes "<<timeTaken<<"ms"<<endl;
  //处理完毕后,我们就可以发射信号让UI线程接收处理后的图像了
   qDebug()<<"process has been finished.Get ready for emitting the signal !!! ";
   emit processFinished(newImage);
}
QString MyImageProcessThread::getImagePath() const
{
    return imagePath;
}
void MyImageProcessThread::setImagePath(const QString &value)
{
    imagePath = value;
}
---------------------------------------------------------我是分隔符------------------------------------------------------------------------------------------------
显示图像的QWidget.h
#pragma once
#include <QWidget>
#include <QImage>
namespace Ui {
class MyPicBox;
}
/**
 * @brief The MyPicBox class
 * 用于绘制QImage的自定义QWidget。同样需要你传入一个QImage指针
 * 当需要显示图片时,这个类(也就是这个QWidget)会被装进一个QMianWindow。
 */
class MyPicBox : public QWidget
{
    Q_OBJECT
public:
    explicit MyPicBox(QWidget *parent = 0);
    explicit MyPicBox(QImage* image , QWidget *parent = 0);
    ~MyPicBox();
protected:
    //考虑到QLabel显示图像时的限制。我们这里直接把图像画出来。
    void paintEvent(QPaintEvent * event);
private:
    Ui::MyPicBox *ui;
    QImage* image;
};
显示图像的QWidget.cpp
#pragma once
#include "mypicbox.h"
#include "ui_mypicbox.h"
#include <QPainter>
#include <QRect>
#include <QDebug>
MyPicBox::MyPicBox(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyPicBox)
{
    ui->setupUi(this);
}
MyPicBox::MyPicBox(QImage *image, QWidget *parent):QWidget(parent),ui(new Ui::MyPicBox)
{
    ui->setupUi(this);
    if(image != NULL)
    {
       this->image = image;
    }
}
MyPicBox::~MyPicBox()
{
    delete ui;
    //记得要释放QImage对象
    if(this->image != NULL)
        delete this->image;
}
void MyPicBox::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QRect rect = this->geometry();
    painter.drawImage(rect,*(this->image));
}
---------------------------------------------------------我是分隔符------------------------------------------------------------------------------------------------
当然,最后还有main.cpp我就不上了。默认啥样就啥样。
全部工程源码我放在网盘了,感兴趣的童鞋自己去下载吧。
http://pan.baidu.com/s/1hqEe1nA
网易这代码排版我也是醉了。
最后,再次希望能够帮到跟我一样的初学者~





from: http://powful1.blog.163.com/blog/static/5662321620152911372943/?newFollowBlog

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值