QT学习之浅谈QThread

为什么使用多线程

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界面开发篇 课程实现



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值