• 11690阅读
  • 18回复

[提问]线程不能操作界面,那么线程还有什么用呢? [复制链接]

上一主题 下一主题
离线maidisula
 

只看楼主 倒序阅读 楼主  发表于: 2011-04-18
我想在线程里更新进度条,但是qt线程不能在线程里操作界面,所以只能使用信号通知界面层去更新,
结果程序跑起来之后,发现根本达不到预期的效果,进度条不怎么动,只有主线程的操作做完了,进度条才能动,
我觉得只是因为进度条的更新都在主线程里做的,所以还是在和主线程当前的操作有冲突,
按照我的这个程序来说的话,是不是使用线程就没什么用了,因为线程里只是执行了i++,然后发送信号这两个操作。

那么怎样才能在线程里跟新进度条呢?
离线yaotong195

只看该作者 1楼 发表于: 2011-04-18
QApplication::processEvents()
离线dbzhang800

只看该作者 2楼 发表于: 2011-04-18
引用楼主maidisula于2011-04-18 16:18发表的 线程不能操作界面,那么线程还有什么用呢? :
我想在线程里更新进度条,但是qt线程不能在线程里操作界面,所以只能使用信号通知界面层去更新,
结果程序跑起来之后,发现根本达不到预期的效果,进度条不怎么动,只有主线程的操作做完了,进度条才能动,
我觉得只是因为进度条的更新都在主线程里做的,所以还是在和主线程当前的操作有冲突,
按照我的这个程序来说的话,是不是使用线程就没什么用了,因为线程里只是执行了i++,然后发送信号这两个操作。
.......

没有代码,不好说什么。但你的线程用法应该有问题。你可以贴出你的完整的测试程序(应该不超过100行吧)。
离线jdwx

只看该作者 3楼 发表于: 2011-04-18
引用第1楼yaotong195于2011-04-18 16:49发表的  :
QApplication::processEvents()

同意
发帖时要说明:操作系统、Qt版本、编译器,这样能更快的得到回复。
离线maidisula

只看该作者 4楼 发表于: 2011-04-19
看了dbzhang800的博客,按照第四种方式重新写了线程,但是还是会有问题,贴下代码
//ThreadWorker.h

#include <QObject>
class ThreadWorker : public QObject
{
    Q_OBJECT
public:
    ThreadWorker() {}
    ~ThreadWorker() {}

public slots:
    void onProgressBarRun()
    {
        qDebug()<<"onProgressBarRun: "<<QThread::currentThreadId();
        for(int i = 0; i < 2000; i++)
        {
            for(int j = 0; j < 100000; j++) {}
            emit updateProgressBar(i);
        }
    }
signals:
    void updateProgressBar(int);
};

//Widget.h

#include <QWidget>
#include <QApplication>
#include <QGridLayout>
#include <QProgressBar>
#include <QPushButton>
#include "ThreadWorker.h"
class Widget:public QWidget
{
    Q_OBJECT
public:
    Widget();
    ~Widget();
signals:
    void startProgressBar();

private slots:
    void setProgressBar(int i);
    void onButtonClicked();
private:
    QGridLayout *m_mainLayout;    
    QProgressBar *m_progressBar;
    ThreadWorker *m_threadWorker;
    QThread *m_thread;
    QPushButton *m_button;
};

Widget.cpp

#include <Widget.h>

Widget::Widget()
{
    m_mainLayout = new QGridLayout;

    m_progressBar = new QProgressBar;
    m_progressBar->setRange(0, 2000 - 1);
    m_progressBar->setValue(0);

    m_mainLayout->addWidget(m_progressBar, 0, 0);

    m_button = new QPushButton("button");
    m_mainLayout->addWidget(m_progressBar, 1, 0);

    setLayout(m_mainLayout);

    m_threadWorker = new ThreadWorker;
    m_thread = new QThread(this);
    m_threadWorker->moveToThread(m_thread);

    connect(m_button, SIGNAL(clicked()), SLOT(onButtonClicked()));
    connect(this, SIGNAL(startProgressBar()), m_threadWorker, SLOT(onProgressBarRun()));
    connect(m_threadWorker, SIGNAL(updateProgressBar(int)), this, SLOT(setProgressBar(int)));

    m_thread->start();
}

Widget::~Widget()
{
    if(m_mainLayout != NULL)
    {
        delete m_mainLayout;
        m_mainLayout = NULL;
    }
    
    if(m_threadWorker != NULL)
    {
        delete m_threadWorker;
        m_threadWorker = NULL;
    }

    if(m_thread != NULL)
    {
        //m_thread->quit(); 问题1: 我想在析构函数先结束线程,再删除指针,但是还是提示:
                                                 QTread:Destroyed while thread is still running,不知怎样才能正确
                                                 结束线程?
        delete m_thread;    
        m_thread = NULL;
    }
}

void Widget::onButtonClicked()
{
    emit startProgressBar();//发送信号,执行线程onProgressBarRun槽,更新进度条
    
    QTime t;
    t.start();
    while(t.elapsed() < 3000)
    {    
        //QCoreApplication::processEvents();问题2:如果不注释此行进度条可以正常跟新,当注释后要先等3秒
                才能看到进度条更新,貌似问题解决了,但是实际我要在这里执行一个比较费时的搜索操作,没有循环,
                所有也不能像上边一样一直执行QCoreApplication::processEvents()函数,那么就要等我的这个搜索操作
                完成以后才能跟新进度条,不知这个问题怎样解决呢?
    }
}

void Widget::setProgressBar(int i)
{    
    m_progressBar->setValue(i);
    QApplication::processEvents(); 问题3:为了解决问题2我在这里添加了QApplication::processEvents();
                                                          但是实际根本没有效果,这是为什么呢?难道非要在onButtonClicked函数里
                                                           才管用吗?
}
谢谢大家
离线dbzhang800

只看该作者 5楼 发表于: 2011-04-19
引用第4楼maidisula于2011-04-19 14:04发表的  :
void Widget::onButtonClicked()
{
    emit startProgressBar();//发送信号,执行线程onProgressBarRun槽,更新进度条
    
    QTime t;
    t.start();
    while(t.elapsed() < 3000)
    {    
        //QCoreApplication::processEvents();问题2:如果不注释此行进度条可以正常跟新,当注释后要先等3秒
                才能看到进度条更新,貌似问题解决了,但是实际我要在这里执行一个比较费时的搜索操作,没有循环,
                所有也不能像上边一样一直执行QCoreApplication::processEvents()函数,那么就要等我的这个搜索操作
                完成以后才能跟新进度条,不知这个问题怎样解决呢?
    }
}

方向错了,你这儿的耗时的操作放到次线程中去做。
离线maidisula

只看该作者 6楼 发表于: 2011-04-19
多谢dbzhang800的解答,这确实是个好方法,但是如果这个搜索操作需要操作界面或者,这部分代码要和主线程有交互那该怎么办呢?

还有为什么我在槽
void Widget::setProgressBar(int i)
{    
    m_progressBar->setValue(i);
    QApplication::processEvents(); 问题3:为了解决问题2我在这里添加了QApplication::processEvents();
                                                          但是实际根本没有效果,这是为什么呢?难道非要在onButtonClicked函数里
                                                           才管用吗?
}
直接更新界面却不起作用呢?
谢谢了
离线dbzhang800

只看该作者 7楼 发表于: 2011-04-19
引用第6楼maidisula于2011-04-19 14:46发表的  :
多谢dbzhang800的解答,这确实是个好方法,但是如果这个搜索操作需要操作界面或者,这部分代码要和主线程有交互那该怎么办呢?
还有为什么我在槽
void Widget::setProgressBar(int i)
{    
.......

一两句很难说清楚。
0. 确保主线程的事件循环始终正常运转(QCoreApplication.exe()进行事件派发),如果操作耗时不能及时回到事件循环,需要操作的间隙不停地手动派发事件(也就是你用的命令)。
1. 跨线程的connect,默认是 queued 方式连接的。如果主线程的事件派发停止了,你的槽不可能被触发。
2. ..
离线maidisula

只看该作者 8楼 发表于: 2011-04-19
引用第7楼dbzhang800于2011-04-19 15:09发表的  :
一两句很难说清楚。
0. 确保主线程的事件循环始终正常运转(QCoreApplication.exe()进行事件派发),如果操作耗时不能及时回到事件循环,需要操作的间隙不停地手动派发事件(也就是你用的命令)。
1. 跨线程的connect,默认是 queued 方式连接的。如果主线程的事件派发停止了,你的槽不可能被触发。
2. ..

多谢,为什么第二条没写呢?呵呵
我试了一下第一条,确实是这样,我在
emit startProgressBar();
之后添加了QApplication::processEvents(); 就可以了,不知这种方式好吗?和这个比是不是把耗时操作放大次线程里要好一些呢?

谢谢
[ 此帖被maidisula在2011-04-20 10:28重新编辑 ]
离线maidisula

只看该作者 9楼 发表于: 2011-04-19
引用第8楼maidisula于2011-04-19 15:48发表的  :
多谢,为什么第二条没写呢?呵呵
我试了一下第一条,确实是这样,我在
emit startProgressBar();
之后添加了QApplication::processEvents(); 就可以了,不知这种方式好吗?和这个比是不是把耗时操作放大次线程里要好一些呢?
.......


emit startProgressBar();
之后添加了QApplication::processEvents();会有问题,
这样次线程执行完了,才会执行主线程,看来还是不能用这种方式
[ 此帖被maidisula在2011-04-20 10:47重新编辑 ]
离线maidisula

只看该作者 10楼 发表于: 2011-04-19
引用第5楼dbzhang800于2011-04-19 14:23发表的  :
方向错了,你这儿的耗时的操作放到次线程中去做。

那么更新进度条是放到主线程还是放到同一个次线程还是再开一个线程跟新进度条呢?

您的意思是不是更新进度条在主线程,而把耗时的操作放大次线程里作,是这样吗?

谢谢
离线maidisula

只看该作者 11楼 发表于: 2011-04-20
dbzhang800在吗?麻烦了,谢谢
离线dbzhang800

只看该作者 12楼 发表于: 2011-04-20
引用第11楼maidisula于2011-04-20 09:01发表的  :
dbzhang800在吗?麻烦了,谢谢

1. 与界面有关的操作都放到主线程
2. 耗时操作放次线程
3. 在1、2基础上,线程之间的通讯应该解决其他问题
离线maidisula

只看该作者 13楼 发表于: 2011-04-20
谢谢指导
我改了代码,您看看是否有问题
主要是把耗时操作放到次线程里,把更新进度条放到主线程里,同时在更新后运行 QApplication::processEvents();

我觉得这两个操作对cpu谁会占用多一些呢?还是一样多,能达到那种平滑的效果吗?
//ThreadWorker.h

#include <QObject>
class ThreadWorker : public QObject
{
    Q_OBJECT
public:
    ThreadWorker() {}
    ~ThreadWorker() {}

public slots:
    void onProgressBarRun()
    {
        qDebug()<<"onProgressBarRun: "<<QThread::currentThreadId();
        
     QTime t;
        t.start();
        while(t.elapsed() < 3000)
        {    
        
        }
    }
signals:
    void updateProgressBar(int);
};

//Widget.h

#include <QWidget>
#include <QApplication>
#include <QGridLayout>
#include <QProgressBar>
#include <QPushButton>
#include "ThreadWorker.h"
class Widget:public QWidget
{
    Q_OBJECT
public:
    Widget();
    ~Widget();
signals:
    void startProgressBar();

private slots:
    void setProgressBar(int i);
    void onButtonClicked();
private:
    QGridLayout *m_mainLayout;    
    QProgressBar *m_progressBar;
    ThreadWorker *m_threadWorker;
    QThread *m_thread;
    QPushButton *m_button;
};

Widget.cpp

#include <Widget.h>

Widget::Widget()
{
    m_mainLayout = new QGridLayout;

    m_progressBar = new QProgressBar;
    m_progressBar->setRange(0, 2000 - 1);
    m_progressBar->setValue(0);

    m_mainLayout->addWidget(m_progressBar, 0, 0);

    m_button = new QPushButton("button");
    m_mainLayout->addWidget(m_progressBar, 1, 0);

    setLayout(m_mainLayout);

    m_threadWorker = new ThreadWorker;
    m_thread = new QThread(this);
    m_threadWorker->moveToThread(m_thread);

    connect(m_button, SIGNAL(clicked()), SLOT(onButtonClicked()));
    connect(this, SIGNAL(startProgressBar()), m_threadWorker, SLOT(onProgressBarRun()));
    connect(m_threadWorker, SIGNAL(updateProgressBar(int)), this, SLOT(setProgressBar(int)));

    m_thread->start();
}

Widget::~Widget()
{
    if(m_mainLayout != NULL)
    {
        delete m_mainLayout;
        m_mainLayout = NULL;
    }
    
    if(m_threadWorker != NULL)
    {
        delete m_threadWorker;
        m_threadWorker = NULL;
    }

    if(m_thread != NULL)
    {
        //m_thread->quit();
        delete m_thread;    
        m_thread = NULL;
    }
}

void Widget::onButtonClicked()
{
    emit startProgressBar();//发送信号,执行线程onProgressBarRun槽,运行耗时操作
    
    for(int i = 0; i < 2000; i++) //更新进度条
    {
       for(int j = 0; j < 2000; j++) {}
    m_progressBar->setValue(i);
       QApplication::processEvents();
    }
}

void Widget::setProgressBar(int i)
{    
    m_progressBar->setValue(i);
    QApplication::processEvents();
}
[ 此帖被maidisula在2011-04-20 11:29重新编辑 ]
离线dbzhang800

只看该作者 14楼 发表于: 2011-04-20
    for(int i = 0; i < 2000; i++) //更新进度条
    {
       for(int j = 0; j < 2000; j++) {}
    m_progressBar->setValue(i);
       QApplication::processEvents();
    }


还以为这也是你的耗时代码呢。你是用它来更新进度条的?去掉这些东西和其他地方的processEvents,用 QTimer 去更新你的进度条。
离线maidisula

只看该作者 15楼 发表于: 2011-04-20
引用第14楼dbzhang800于2011-04-20 11:30发表的  :
还以为这也是你的耗时代码呢。你是用它来更新进度条的?去掉这些东西和其他地方的processEvents,用 QTimer 去更新你的进度条。

呵呵,是的,这种方法不好吗?
那我再看看QTimer怎样更新进度条
非常感谢
离线maidisula

只看该作者 16楼 发表于: 2011-04-20
又改了一下代码,使用QTimer更新进度条,您看一下还有什么问题

//ThreadWorker.h

#include <QObject>
class ThreadWorker : public QObject
{
    Q_OBJECT
public:
    ThreadWorker() {}
    ~ThreadWorker() {}

public slots:
    void onProgressBarRun()
    {
        qDebug()<<"onProgressBarRun: "<<QThread::currentThreadId();
        
     QTime t;
        t.start();
        while(t.elapsed() < 3000)
        {    
        
        }
    }
signals:
    void updateProgressBar(int);
};

//Widget.h

#include <QWidget>
#include <QApplication>
#include <QGridLayout>
#include <QProgressBar>
#include <QPushButton>
#include <QTimer>
#include "ThreadWorker.h"
class Widget:public QWidget
{
    Q_OBJECT
public:
    Widget();
    ~Widget();
signals:
    void startProgressBar();

private slots:
    void setProgressBar(int i);
    void setProgressBar();
    void onButtonClicked();
private:
    QGridLayout *m_mainLayout;    
    QProgressBar *m_progressBar;
    ThreadWorker *m_threadWorker;
    QThread *m_thread;
    QPushButton *m_button;
    QTimer *m_timer;
    int m_nProgressValue;
};

Widget.cpp

#include <Widget.h>

Widget::Widget()
{
    m_nProgressValue = 0;
    m_mainLayout = new QGridLayout;

    m_progressBar = new QProgressBar;
    m_progressBar->setRange(0, 100 - 1);
    m_progressBar->setValue(m_nProgressValue);

    m_mainLayout->addWidget(m_progressBar, 0, 0);

    m_button = new QPushButton("button");
    m_mainLayout->addWidget(m_progressBar, 1, 0);

    setLayout(m_mainLayout);

    m_threadWorker = new ThreadWorker;
    m_thread = new QThread(this);
    m_threadWorker->moveToThread(m_thread);
    m_timer = new QTimer;
    m_timer->setInterval(100);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(setProgressBar()));

    connect(m_button, SIGNAL(clicked()), SLOT(onButtonClicked()));
    connect(this, SIGNAL(startProgressBar()), m_threadWorker, SLOT(onProgressBarRun()));
    connect(m_threadWorker, SIGNAL(updateProgressBar(int)), this, SLOT(setProgressBar(int)));

    m_thread->start();
}

Widget::~Widget()
{
    if(m_mainLayout != NULL)
    {
        delete m_mainLayout;
        m_mainLayout = NULL;
    }
    
    if(m_threadWorker != NULL)
    {
        delete m_threadWorker;
        m_threadWorker = NULL;
    }

    if(m_thread != NULL)
    {
        //m_thread->quit();
        delete m_thread;    
        m_thread = NULL;
    }
}

void Widget::onButtonClicked()
{
    m_timer->start();//开始更新进度条
    emit startProgressBar();//发送信号,执行线程onProgressBarRun槽,运行耗时操作
}

void Widget::setProgressBar()
{    
    m_progressBar->setValue(m_nProgressValue);
    m_nProgressValue++;
}

void Widget::setProgressBar(int i)
{    
    m_progressBar->setValue(i);
    QApplication::processEvents();
}
离线maidisula

只看该作者 17楼 发表于: 2011-04-21
这样应该可以了吧
离线maidisula

只看该作者 18楼 发表于: 2011-04-21
我来顶一下
快速回复
限100 字节
 
上一个 下一个