最近开始学习Qt。发现Qt的资料比起不好用的MFC实在是不多啊。今天刚好看到了Qt多线程,就写了个小例子放出来,希望能帮到跟我一样的初学者。
简单讲下。程序就两个界面。一个主要的QDialog,一个显示图像的自定义控件MyPicBox 。还有就是动态创建了一个QMainWindow,因为我要用QMainWindow::setCentralWidget来放置MyPicBox。
软件的作用是多线程实现图像的灰度化(因为这个足够简单,毕竟这个DEMO的重点不是讲图像处理),然后把处理结果显示出来。大概呢,就是下面这个样子。
不要问我照片里的人是谁。哈哈哈哈~~~~
好了,我还是直接上代码吧。一切都在注释中:
首先是主对话框类.h(窗口标题是”选择图片“那个):
#pragma once#include <QDialog>#include <QImage>#include "my_thread.h"namespace Ui {class MainDialog;}/**
* @brief The MainDialog class* 主对话框类*/class MainDialog : public QDialog{Q_OBJECTpublic:
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);}
elsepicWindow->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:
然后是图像处理线程类.cpp#pragma once#include <QThread>#include <QImage>#include <QObject>/**
* @brief The MyImageProcessThread class* 图像处理的线程类,需要传入一个QImage图像指针* 同时包含两个自定义信号*/class MyImageProcessThread : public QThread{/** 注意:必须添加Q_OBJECT宏使其能够应用Qt的信号槽机制,*不然你就没法更新UI界面啦
*/Q_OBJECTpublic:
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;};
---------------------------------------------------------我是分隔符------------------------------------------------------------------------------------------------#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就更新一次UIif(counter == (int)(byteCount/100)){
counter = 0;hasDone +=1;emit processProgress(100,hasDone);this->sleep(1);//这里我让线程休眠1秒,不然进度条瞬间就走完了。}
elsecounter++;
}
emit processProgress(100,100);//生成新的QImageint 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
显示图像的QWidget.cpp#pragma once#include <QWidget>#include <QImage>namespace Ui {class MyPicBox;}/**
* @brief The MyPicBox class* 用于绘制QImage的自定义QWidget。同样需要你传入一个QImage指针* 当需要显示图片时,这个类(也就是这个QWidget)会被装进一个QMianWindow。*/class MyPicBox : public QWidget{Q_OBJECTpublic:
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;};
---------------------------------------------------------我是分隔符------------------------------------------------------------------------------------------------#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