Qt项目:文件拷贝项目

基础知识:

在进行桌面应用程序开发的时候,假设应用程序在某些情况下需要处理比较复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作,这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率

在Qt中使用了多线程,有些事项是需要额外注意的:

1、默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者控件数据的更新

2、子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理

3、主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制

//线程类QThread:
//Qt中提供了一个线程类,通过这个类可以创建子线程了
//构造函数
QTread::QTread(QObject * parent = Q_NULLPTR);
//判断线程中的任务是不是处理完毕了
bool QThread::isFinished()const;
//判断子线程是不是在执行任务
bool QThread::isRunning()const;
//在主线程中创建子线程对象,并调用start函数启动线程
void QThread::start(Priority priority = InheritPriority)[slot]
//优先级从低到高是
//QThread::IdlePriority
//QThread::LowestPriority
//QThread::LowPriority
//QThread::NormalPriority
//QThread::HighPriority
//QThread::HighestPriority
//QThread::TimeCriticalPriority
//QThread::InheritPriority
//线程退出,可能马上终止线程,一般不用
void QThread::terminate() [slot]
//退出线程
void QThread::exit(int returnCode = 0);
//等待任务完成,然后退出线程
bool QThread::wait(unsigned long time = ULONG_MAX);
//子线程要处理什么任务,需要写到run()中
void QThread::run() ;[virtual protected]
操作步骤:
//1、需要创建一个线程类的子类,让其继承Qt中的线程类QThread
class MyThread::public QThread
{
}
//2、重写父类的run()方法,在该函数内部编写子线程要处理的具体的业务流程
class MyThread::public QTread
{
protected:
void run()
{
}
}
//3、在主线程中创建子线程对象,new一个就可以了
MyThread * subThread=new MyThread;
//4、启动子线程,调用start()方法
subThread->start();

这个run()是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承QThread,并且子类中重写父类的run()方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数start()启动子线程,当子线程启动,这个run()函数也就在线程内部被调用了

不能在类的外部调用run()方法启动子线程,在外部调用start()相当于让run()开始运行

当子线程创建出来之后,父子进程之间的通信可以通过信号槽的方式,注意事项:

1、在Qt中子线程中不要操作程序中的窗口类对象,不允许,如果操作了,程序就挂了

2、只有主程序才能操作程序中的窗口对象,默认的线程就是主线程,自己创建的就是子线程

文件拷贝代码示例:

头文件代码示例
copythread.h
#ifndef COPYTHREAD_H
#define COPYTHREAD_H

#include 
// 头文件
#include 

class CopyThread : public QThread
{
    Q_OBJECT
public:
    explicit CopyThread(QString,QString,QObject *parent = 0);

protected:
    void run();

private:
    QString readPath;
    QString writePath;

signals:
    // 发射拷贝进度值的信号函数
    void valueSignal(int);

public slots:

};

#endif // COPYTHREAD_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H

#include 
#include 
#include 
#include  // 文件选择对话框
#include  // 文件信息
#include 
#include 
#include "copythread.h" // 拷贝线程

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QButtonGroup *group;
    // 读写的路径
    QString readPath;
    QString writePath;
    // 文件拷贝
    void copy();

private slots:
    // 按钮组点击的槽函数
    void btnsClickedSlot(int);
    // 接收子线程进度值的槽函数
    void valueSlot(int);
};

#endif // DIALOG_H
cpp代码示例:
copythread.cpp
#include "copythread.h"

CopyThread::CopyThread(QString readPath,
                       QString writePath,QObject *parent) :
    QThread(parent)
{
    // 给传入的路径保存到属性中
    this->readPath = readPath;
    this->writePath = writePath;
}

void CopyThread::run()
{
    // 创建两个QFile对象
    QFile readFile(readPath);
    QFile writeFile(writePath);
    // 以只读和只写模式打开文件
    readFile.open(QIODevice::ReadOnly);
    writeFile.open(QIODevice::WriteOnly);
    // 获取文件大小
    qint64 totalSize = readFile.size();
    // 已经读写的大小
    qint64 hasReadSize = 0;
    // 上一次的进度值
    int lastPer = 0;

    // 存储每次读取的数据的字节数组对象
    QByteArray buffer;
    // 循环读取
    while(!readFile.atEnd())
    {
        // 读取最多1kb的数据到数组中
        buffer = readFile.read(2048);
        // 写出数据
        hasReadSize += writeFile.write(buffer);
        // 算百分比
        int per = 100*hasReadSize/totalSize;
        // 跟上一次的进度值比对
        if(per!=lastPer)
        {
            // 发射进度值到主线程
            emit valueSignal(per);
            lastPer = per;
        }
    }
    // 收尾
    writeFile.flush();
    writeFile.close();
    readFile.close();
}
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    group = new QButtonGroup(this);
    group->addButton(ui->pushButtonOpen,1);
    group->addButton(ui->pushButtonSave,2);
    group->addButton(ui->pushButtonCopy,3);
    connect(group,SIGNAL(buttonClicked(int)),
            this,SLOT(btnsClickedSlot(int)));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::copy()
{
    // 是否已经选择读写的文件
    if(readPath == "" || writePath == "")
    {
        QMessageBox::warning(this,"提示","请选择要读写的文件!");
        return;
    }
    // 屏蔽拷贝按钮
    ui->pushButtonCopy->setEnabled(false);
    ui->pushButtonCopy->setText("拷贝中...");
    // 开启子线程拷贝
    CopyThread *ct = new CopyThread(readPath,writePath,this);
    // 连接两个线程通信的信号槽
    connect(ct,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));
    ct->start();
}

void Dialog::btnsClickedSlot(int id)
{
    if(id == 1)
    {
        QString path = QFileDialog::getOpenFileName(this,
                                                    "打开",
                                                    "E:/Image/",
                                                    "头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
        if(readPath == "" && path == "")
        {
            QMessageBox::warning(this,"提示","请选择要读取的文件!");
            return;
        }
        readPath = path;
        // 清空之前信息显示
        ui->textBrowserOpen->clear();
        ui->textBrowserOpen->append(readPath);
        // 显示要读取的文件信息
        QFileInfo info(readPath);
        // 获得创建时间
        QString text = info.created().toString("yyyy-MM-dd hh:mm:ss");
        text.prepend("创建时间:");
        ui->textBrowserOpen->append(text);
        // 判断是否可读
        if(info.isReadable())
            ui->textBrowserOpen->append("可读");
        else
            ui->textBrowserOpen->append("不可读");
        // 获得文件大小
        int count = 0; // 除了多少次
        qint64 size = info.size();
        while(size > 1024)
        {
            size = size/1024;
            count++;
        }
        if(count == 0)
        {
            text = "字节";
        }else if(count == 1)
        {
            text = "kb";
        }else if(count == 2)
        {
            text = "Mb";
        }else if(count == 3)
        {
            text = "Gb";
        }
        // 数字转字符串
        text.prepend(QString::number(size)).prepend("文件大小:");
        ui->textBrowserOpen->append(text);

    }else if(id == 2)
    {
        QString path = QFileDialog::getSaveFileName(this,
                                                    "保存",
                                                    "E:/",
                                                    "头文件(*.h);;源文件(*.cpp *.c);;所有文件(*.*)");
        if(writePath == "" && path == "")
        {
            QMessageBox::warning(this,"提示","请设置要保存的文件!");
            return;
        }
        writePath = path;
        // 清空之前信息显示
        ui->textBrowserSave->clear();
        ui->textBrowserSave->append(writePath);
    }else if(id==3)
    {
        copy();
    }
}

void Dialog::valueSlot(int value)
{
    // 更新进度条显示
    ui->progressBar->setValue(value);
    // 如果进度值=100,说明拷贝完成
    if(value == 100)
    {
        // 回复拷贝按钮
        ui->pushButtonCopy->setEnabled(true);
        ui->pushButtonCopy->setText("开始拷贝");
        QString text = "输出文件路径:";
        text.append(writePath);
        QMessageBox::information(this,"拷贝完成",text);
    }
}

你可能感兴趣的:(qt,c++)