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

最近开始学习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: 以下是一个使用 Qt 创建多线程应用程序的简单例子:#include <QtCore>class Worker : public QObject { Q_OBJECTpublic slots: void doWork() { QString result; /* ... here is the expensive or blocking operation ... */ emit resultReady(result); }signals: void resultReady(const QString &s); };int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Worker *worker = new Worker; QThread *thread = new QThread; worker->moveToThread(thread); thread->start(); QObject::connect(worker, &Worker::resultReady, [](const QString &s){ qDebug() << s; }); QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection); return a.exec(); } ### 回答2: 下面是一个简单Qt多线程例子: ```cpp #include <QtWidgets> #include <QtCore> // 继承自 QObject 并且 Q_OBJECT 宏 class WorkerThread : public QObject { Q_OBJECT public: explicit WorkerThread(QObject *parent = nullptr) : QObject(parent) {} public slots: // 在新线程中执行的耗时操作 void doWork() { // 模拟耗时操作 for (int i = 0; i < 1000000000; ++i) {} // 发射信号,通知主线程操作完成 emit workDone(); } signals: // 信号,通知主线程操作完成 void workDone(); }; int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建一个新的线程 QThread workerThread; // 创建一个 WorkerThread 的对象 WorkerThread worker; // 将 WorkerThread 对象移动到新线程中 worker.moveToThread(&workerThread); // 连接信号和槽,当 WorkerThread 发射 workDone 信号时,调用主线程的槽函数 QObject::connect(&worker, SIGNAL(workDone()), qApp, SLOT(quit()), Qt::QueuedConnection); // 当新线程启动时,调用 worker 的 doWork 槽函数 QObject::connect(&workerThread, SIGNAL(started()), &worker, SLOT(doWork())); // 当线程完成工作后,退出线程事件循环 QObject::connect(&worker, SIGNAL(workDone()), &workerThread, SLOT(quit())); // 通过调用 QThread::start,启动新线程,并进入事件循环 workerThread.start(); return app.exec(); } ``` 上面的例子中,我们创建了一个名为 WorkerThread 的类,继承自 QObject,并使用 Q_OBJECT 宏进行声明,以便使用 Qt 的信号和槽机制。WorkerThread 类中定义了一个 doWork() 槽函数,用来模拟一个耗时操作。当耗时操作完成后,通过发射 workDone 信号,通知主线程操作完成。 在主函数中,我们创建了一个新的线程 workerThread,然后创建一个 WorkerThread 对象 worker,并将其移动到 workerThread 线程中。然后我们通过连接信号和槽,将 workerThread 中的 started 信号与 worker 的 doWork 槽函数连接起来,以便在线程启动时调用 doWork()。还将 workerThread 中的 finished 信号与 workerThread 的 quit 槽函数连接,以便在线程完成工作后退出线程事件循环。 最后通过调用 workerThread.start() 启动线程,并运行 app.exec() 进入主线程的事件循环。当 worker 发射 workDone 信号时,会调用主线程的 quit() 槽函数退出事件循环,程序结束运行。 ### 回答3: 下面是一个使用Qt框架实现的一个简单多线程例子: ```cpp #include <QCoreApplication> #include <QThread> #include <QDebug> // 子线程类 class WorkerThread : public QThread { public: void run() override { qDebug() << "子线程开始执行"; // 模拟耗时操作 for (int i = 0; i < 5; i++) { // 每次休眠1秒 sleep(1); qDebug() << "子线程执行中..."; } qDebug() << "子线程执行完成"; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "主线程开始执行"; // 创建子线程对象 WorkerThread worker; // 启动子线程 worker.start(); // 模拟主线程执行其他任务 for (int i = 0; i < 3; i++) { // 每次休眠1秒 sleep(1); qDebug() << "主线程执行中..."; } qDebug() << "主线程执行完成"; // 等待子线程执行完毕 worker.wait(); return a.exec(); } ``` 这个例子创建了一个继承自QThread的子线程类WorkerThread,重写了run方法,在run方法中模拟了一个耗时操作。在主函数main中,首先创建了一个WorkerThread对象,并调用start方法启动子线程。然后主线程执行其他任务,最后使用wait方法等待子线程执行完毕。运行程序,可以观察到主线程和子线程的执行顺序和交替执行的效果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值