首页 > 编程语言 > 详细

Qt C++ 子线程访问主线程数据和方法

时间:2020-05-09 22:06:55      阅读:237      评论:0      收藏:0      [点我收藏+]

  因为项目需求,需要将模型中的数据保存为excel保存到电脑上,但是由于拉起excel这个过程需要几秒钟时间,如果在主线程中完成这项工作,那么这几秒钟程序会陷入假死状态,因此需要将其写到子线程中。

主线程:widget.h     widget.cpp

子线程:saveThread.h     saveThread.cpp

遇到的问题和解决方法记录如下

1:用于保存数据的函数 savedata_excel()已经在主线程中完成,而且可以正常调用。

那么我最初的想法就是只要在子线程中声明一个Widget对象就可以调用savedata_excel()函数,事实证明这样是不行的。编译器会提示savedata_excel()是私有成员,无法调用。

于是我想到了友元,经过学习之后成功的把saveThread声明为Widget的友元类,并且成功地调用了一个widget的测试函数(打印一串文字来测试是否成功)。但是在调用savedata_excel()函数时却依然失败了,遇到了下一个问题。

2:经过长时间的测试,找到原因是因为子线程中的widget对象没有分配内存(地址是0xcdcdcdcdcdcdcdcdcdcd),于是我在run()函数中添加语句Widget *=new Widget;给它分配内存地址,但是这次编译器又报错了widgets must be creat in the GUI thread,也就是说子线程是不能访问GUI对象的,这是真的把人憋坏了,又不能手动给它分配地址,有需要它有内存地址才能正确调用一些方法,这不是矛盾吗。

但是在不断的思索下,我终于还是找到了解决问题的办法:用信号和槽来把主线程中widget对象的地址传递给子线程中的widget对象,这样他们不就指向同一个地址了吗!

经验证,此方法确实可行,下面就是相关程序的代码:

 

Widget.h


class SaveThread;//前置声明
class Widget : public QWidget
{
    Q_OBJECT
signals:
    void preper_save(Widget *);
public:
    friend class SaveThread;//将SaveThread类声明为友元
private:
    SaveThread *s_thread;//声明SaveThread对象,s_thread.start()写在onUDPSocketReadyRead()函数中,收到UDP信号后发送信号,并启动线程
   bool savedata_excel();//用于保存数据的函数 private slots: void onUDPSocketReadyRead();//读取socket传入的数据

 

 

 


#include "savethread.h" //包含头文件,不然编译器会报错,没有在.h中声明,因为也会报错
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{
    s_thread=new SaveThread();//给子线程类对象分配地址

connect(this,SIGNAL(preper_save(Widget *)),s_thread,SLOT(getaddress(Widget *)));//传递主线程指针地址 connect(ui->savebtn,SIGNAL(clicked()),this,SLOT(savedata_excel()));//主线程中点击按钮之后保存数据 } void Widget::onUDPSocketReadyRead() { while(udpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(udpSocket->pendingDatagramSize()); QHostAddress peerAddr; quint16 peerPort; udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort); QString str=datagram.data(); QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] "; ui->plainTextEdit->appendPlainText(peer+str); if(str.mid(0,4)=="+PLS") { shotnum=str; emit preper_save(this);//UDP收到信号只有,将主线程地址传递给子线程 s_thread->start();//子线程开始 } else return; } } bool Widget::savedata_excel() { qDebug()<<"这是主线程的保存数据"; QString fileName = QFileDialog::getSaveFileName(this,"保存", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),"Excel 文件(*.xls *.xlsx)"); if(fileName!="") { QAxObject *excel = new QAxObject; excel->setControl("Excel.APPlication"); excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体 excel->setProperty("DisplayAlerts", false);//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示 QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合 workbooks->dynamicCall("Add");//新建一个工作簿 QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿 QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1); if(channel1->on_off) { QVariant var=datatovariant(model1); int i = model1->rowCount(); QString str="A3:C"+QString::number(2+i); QAxObject *range=worksheet->querySubObject("Range(const QString&)",str); range->setProperty("Value",var); range->querySubObject("Interior")->setProperty("Color", QColor(255, 255, 165)); } workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(fileName));//保存至fileName workbook->dynamicCall("Close()");//关闭工作簿 excel->dynamicCall("Quit()");//关闭excel flagnumber=0; delete excel; excel=nullptr; QMessageBox::information(this,tr("提示"),tr("数据已保存")); return true; } else return false; }
SaveThread.h


#include "widget.h"//包含Widget类的头文件
#pragma execution_character_set("utf-8")
class SaveThread : public QThread
{
    Q_OBJECT
protected:
    void  run() Q_DECL_OVERRIDE;  //线程任务
public:
    SaveThread();
private slots:
    void getaddress(Widget *);//槽函数,用于接收指针
private:
    Widget *w;
};
SaveThread.cpp


void SaveThread::run()
{
    qDebug()<<"线程开始";
    QString strpath=w->fileName;
    strpath.append(w->shotnum);
    qDebug()<<strpath;
    if(strpath!="")
    {
        //QMessageBox::information(this,tr("提示"),tr("正在保存数据,请稍等"));
        QAxObject *excel = new QAxObject;
        excel->setControl("Excel.APPlication");

        excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
        excel->setProperty("DisplayAlerts", false);//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
        QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
        workbooks->dynamicCall("Add");//新建一个工作簿
        QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
        QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);


        if(w->channel1->on_off)//每个通道对应的结构体中有一个值on_off来判断通道是否打开
        {
            QVariant var=w->datatovariant(w->model1);
            int i = w->model1->rowCount();
            QString str="A3:C"+QString::number(2+i);
            QAxObject *range=worksheet->querySubObject("Range(const QString&)",str);
            range->setProperty("Value",var);
            range->querySubObject("Interior")->setProperty("Color", QColor(255, 255, 165));
        }

        workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(strpath));//保存至fileName
        workbook->dynamicCall("Close()");//关闭工作簿
        excel->dynamicCall("Quit()");//关闭excel
        w->flagnumber=0;
        delete excel;
        excel=nullptr;
     }
    this->quit(); 
    qDebug()<<"线程结束";
}

void SaveThread::getaddress(Widget *p)
{
    w=p;//此行代码就使得w有了内存,而且和主线程对象地址一样
}

  我们可以注意到savedata_excel()函数与run()函数执行的是一样的功能,但是补分代码确不一样,这是因为主线程中的数据可以直接访问,而子线程想要访问主线程数据则必须通过w->来进行访问和调用。

总结如下:要使得子线程能访问主线程的数据,而且是GUI主线程的数据,第一步需要将子线程声明为主线程的友元类,第二步是将主线程类对象的地址通过信号槽传递给子线程中创建的对象,就大功告成!

由于刚好解决掉这个问题,忙于先将方法记录下来,文中应该还有说的不准确的地方,日后验证之后会对这篇文章进行修改。

Qt C++ 子线程访问主线程数据和方法

原文:https://www.cnblogs.com/leocc325/p/12860176.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!