为什么使用多线程
QT程序在运行时,部分事件可能会占用较长的事件而导致后续事件不能被处理,程序不能被操作,通常被称为卡死状态。在卡死状态下,用户不知道程序发生何种错误而产生疑惑,影响使用。为此,提供多线程技术,将占用时间较长的事件放在一个额外的线程下处理,同时给用户明确的进度提示。
界面线程与工作线程
界面线程指的是QT程序运行的主线程,在程序运行时被直接使用;工作线程则需要用户进行创建、调用,以便处理一些占用时间教程的事件,避免界面卡死。
线程相关的类:
QTread 线程;
QMutex 互斥锁;
QSemapaphore 信号量。
QThread应用实例:
{
创建线程:
派生一个线程类
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
~MyThread();
void run(); //线程的入口函数
}
启动线程start();
回收线程外套();
设置状态与进度:
状态:int GetStatus();
已完成(1) 正在进行(0) 发生错误,已终止(-1)
进度:int GetProgress();
0-100
}
Demo例子:
ShowWidget.h文件:#ifndef H_SHOW_WIDGET
#define H_SHOW_WIDGET
#include <QWidget>
class QVBoxLayout;
class QLineEdit;
class QPushButton;
class QHBoxLayout;
class ShowWidget : public QWidget
{
Q_OBJECT
public:
ShowWidget();
~ShowWidget();
private slots:
void OnBtnSelect();
void OnSend();
private:
QVBoxLayout *main_layout_;
QHBoxLayout *file_;
QPushButton *browse_;
QLineEdit *file_edit_;
QPushButton *uploading_btn_;
};
#endif //H_SHOW_WIDGET
ShowWidget.cpp文件:
1、设计界面显示;2、设计点击事件;3、实现选择文件与上传文件的槽函数
#include <QtGui>
#include <QTextCodec>
#include <QFileDialog>
#include "ShowWidget.h"
#include "SendDlg.h"
ShowWidget::ShowWidget()
{
QTextCodec::setCodecForTr(QTextCodec::codecForName("gbk"));
main_layout_ = new QVBoxLayout;
file_ = new QHBoxLayout;
file_edit_ = new QLineEdit;
file_edit_->setPlaceholderText( tr("选择文件") );
browse_ = new QPushButton;
browse_->setText( tr("浏览") );
file_->addWidget(file_edit_);
file_->addWidget(browse_);
uploading_btn_ = new QPushButton;
uploading_btn_->setText( tr("传送") );
main_layout_->addLayout(file_);
main_layout_->addWidget(uploading_btn_);
this->setLayout(main_layout_);
connect( browse_, SIGNAL( clicked() ), this, SLOT( OnBtnSelect() ) );
connect( uploading_btn_, SIGNAL( clicked() ), this, SLOT( OnSend() ) );
}
ShowWidget::~ShowWidget()
{
}
void ShowWidget::OnBtnSelect()
{
/*https://blog.csdn.net/hongxingabc/article/details/50265777*/
//选择文件
QString filepath_ = QFileDialog::getOpenFileName(this, tr("选择文件") );
if (filepath_.length() > 0)
{
file_edit_->setText(filepath_);
}
}
void ShowWidget::OnSend()
{
QString filepath = file_edit_->text();
SendDlg dlg(filepath, this);
dlg.exec();
}
SendDlg.h文件
#ifndef H_SEND_DLG
#define H_SEND_DLG
#include <QDialog>
#include "SendTask.h"
class QHBoxLayout;
class QProgressBar;
class SendDlg : public QDialog
{
Q_OBJECT
public:
SendDlg(const QString& filepath, QWidget *parent = 0);
~SendDlg();
virtual void timerEvent( QTimerEvent * event );
private:
QHBoxLayout *main_layout_;
QProgressBar *schedule_;
SendTask* m_task; //工作线程
int m_timerId;//定时器ID
};
#endif //H_SEND_DLG
SendDlg.cpp文件:
1、创建进度条显示界面;2、调用工作线程;3、设置定时器,定时显示进度。
#include <QtGui>
#include "SendDlg.h"
#include "GBK.h"
#include <QTextCodec>
SendDlg::SendDlg(const QString& filepath, QWidget *parent /*= 0*/ )
{
QTextCodec::setCodecForTr(QTextCodec::codecForName("gbk"));
main_layout_ = new QHBoxLayout;
schedule_ = new QProgressBar;
main_layout_->addWidget(schedule_);
this->setLayout(main_layout_);
this->setWindowTitle( tr("传输进度") );
//把中文转换一下
string gbk_filepath = GBK::FromUnicode(filepath);
//创建工作线程
m_task = new SendTask(NULL);
m_task->Create( gbk_filepath.c_str() );
//
m_timerId = startTimer(500);
}
SendDlg::~SendDlg()
{
}
void SendDlg::timerEvent( QTimerEvent * event )
{
//
if (event->timerId() == m_timerId)
{
//获取工作线程的当前状态和进度,并显示
int status = m_task->GetStatus();
int progress = m_task->GetProgress();
schedule_->setValue(progress);
if (status == 1)
{
//销毁线程
m_task->Destroy();
delete m_task;
killTimer(m_timerId);//关闭定时器
this->accept();//关闭对话框
}
}
}
SendTask.h文件
#ifndef H_SEND_TASK
#define H_SEND_TASK
#include <QThread>
class SendTask : public QThread
{
Q_OBJECT
public:
SendTask(QObject *parent);
~SendTask();
int Create(const char* filename);
void Destroy();
int GetStatus();
int GetProgress();
private:
//线程的入口函数
void run();
private:
char m_filepath[128];
int m_filesize;
int m_bytesread;
int m_status;
};
#endif //H_SEND_TASK
SendTask.cpp文件
1、创建工作线程;2、设置入口函数;3、设置状态函数与进程函数;4、设置销毁线程函数
#include <QtGui>
#include <string.h>
#include <QDebug>
#include "SendTask.h"
SendTask::SendTask( QObject *parent )
{
}
SendTask::~SendTask()
{
}
int SendTask::Create( const char* filename )
{
strcpy(m_filepath,filename);
m_filesize = 0;
m_bytesread = 0;
m_status = 0;
start();//运行线程
return 0;
}
void SendTask::Destroy()
{
wait();
}
int SendTask::GetStatus()
{
return m_status;
}
int SendTask::GetProgress()
{
if (m_filesize <= 0)
return 0;
return m_bytesread * 100 / m_filesize;
}
//线程入口函数
void SendTask::run()
{
FILE* fp = fopen(m_filepath,"rb");
if (!fp)
{
m_status = -1; //状态,发生错误
return;
}
//获取文件大小
fseek(fp,0,SEEK_END);
m_filesize = ftell(fp);
fseek(fp,0,SEEK_SET);
char buf[256];
int part = 0;
while(1)
{
int n = fread(buf, 1, 256, fp);
if (n <= 0)
{
break;
}
m_bytesread += n;
qDebug() << "Read: " << m_bytesread;
//每隔一定时间,sleep一次
part += n;
if (part > 1024 * 32)
{
QThread::msleep(100); //模拟一下
part = 0;
}
}
fclose(fp);
m_status = 1;//状态已完成
qDebug() << "Complete!!!";
}
GBK.h文件:中文设置问题
#ifndef _QT_GBK_H
#define _QT_GBK_H
#include <QString>
#include <QTextCodec>
#include <string>
using std::string;
class GBK
{
public:
// QString(Unicode) -> std::string (GBK)
static string FromUnicode(const QString& qstr)
{
QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
if(!pCodec) return "";
QByteArray arr = pCodec->fromUnicode(qstr);
string cstr = arr.data();
return cstr;
}
// std::string (GBK) -> QString(Unicode)
static QString ToUnicode(const string& cstr)
{
QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
if(!pCodec) return "";
QString qstr = pCodec->toUnicode(cstr.c_str(), cstr.length());
return qstr;
}
};
#endif
注
:本文参考
《C语言/C++学习指南》Qt界面开发篇 课程实现