我创建了一个应用程序,我想在应用程序打开一个项目时添加一个加载屏幕,因为项目的加载时间很长,有时gui阻塞,所以用户可以认为发生了崩溃。 / p>
所以我尝试了QThread,阅读doc和"解决了#34;这个论坛上的例子,但无事可做,我无法使其发挥作用。
我有一个处理GUI的MainWindow类,这个类是我在main函数中创建的类:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
然后我有: mainwindow.h
class MyThread;
class MainWindow : public QMainWindow
{
Q_OBJECT
...
private :
Controller *controller;//inherits from QObject and loads the project
QList<MyThread*> threads;
public slots :
void animateLoadingScreen(int inValue);
}
mainwindow.cpp
MainWindow::MainWindow(...)
{
controller=new Controller(...);
threads.append(new MyThread(30, this));
connect(threads[0], SIGNAL(valueChanged(int)), this, SLOT(animateLoadingScreen(int)));
}
void MainWindow::animateLoadingScreen(int inValue)
{
cout<<"MainWindow::animateLoadingScreen"<<endl;
widgetLoadingScreen->updateValue(inValue);//to update the animation
}
void MainWindow::openProject()
{
widgetLoadingScreen->show()://a widget containing a spinner for example
threads[0]->start();//I want to launch the other thread here
controller->openProject();
threads[0]->exit(0);//end of thread so end of loading screen
}
MyThread.h
class MyThread : public QThread
{
Q_OBJECT;
public:
explicit MyThread(int interval, QObject* parent = 0);
~MyThread();
signals:
void valueChanged(int);
private slots:
void count(void);
protected:
void run(void);
private:
int i;
int inc;
int intvl;
QTimer* timer;
};
MyThread.cpp
MyThread::MyThread(int interval, QObject* parent): QThread(parent), i(0), inc(-1), intvl(interval), timer(0)
{
}
void MyThread::run(void)
{
if(timer == 0)
{
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(count()));
}
timer->start(intvl);
exec();
}
void MyThread::count(void)
{
if(i >= 100 || i <= 0)
inc = -inc;
i += inc;
emit valueChanged(i);
}
当我执行应用程序,然后单击启动MainWindow :: openProject()的打开按钮时,我得到:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is MyThread(0x5463930), parent's thread is QThread(0x3cd1f80), current thread is MyThread(0x5463930)
MainWindow::animateLoadingScreen
MainWindow::animateLoadingScreen
....
MainWindow::animateLoadingScreen
MainWindow::animateLoadingScreen
(and here the controller outputs. and no MainWindow::animateLoadingScreen anymore so the widget loading screen is never animated during the opening of the project)
那么我该怎么做,我需要在MyThread类中添加什么,如何将其信号链接到MainWindow以更新加载屏幕。我认为在MainWindow中创建的widgetLoadingScreen可能存在问题,因此如果MainWindow因打开而被阻止,则widgetLoadingScreen不能更新,因为它位于处理GUI的MainWindow的线程中? / p>
我看了: http://www.qtcentre.org/wiki/index.php?title=Updating_GUI_from_QThread 但是有了那个,我在运行时收到了错误信息,它是我在上面给出的代码中使用的那个 QObject:无法为位于不同线程中的父级创建子级。 (Parent是MyThread(0x41938e0),父线程是QThread(0x1221f80),当前线程是MyThread(0x41938e0)
我也试过了: How to emit cross-thread signal in Qt? 但是,即使我没有在运行时收到错误消息,对于未更新的动画也是如此。
我完全迷失了,在主线程打开项目时,我不认为在线程中加载屏幕很难做到这一点?!
答案 0 :(得分:0)
Qt有一个QSplashScreen课程,您可以将其用于加载屏幕。
除此之外,您遇到的一个问题是由于线程关联(运行对象的线程)以及您决定从QThread继承的事实。
在我看来,QThread有一个误导性的名字。它更像是一个线程控制器,除非你想改变Qt处理线程的方式,你真的不应该继承它,许多人会告诉你“You're doing it Wrong!”
通过继承QThread,您的MyThread实例正在主线程上运行。但是,当调用exec()时,它会为新线程及其所有子节点(包括QTimer)启动事件循环,并尝试将其移动到新线程。
解决这个问题的正确方法不是继承QThread,而是创建一个派生自QObject的工作对象并将其移动到新线程。您可以阅读有关如何“Really Truly Use QThread' here。
的信息答案 1 :(得分:0)
第一个问题 - QTimer的运行时错误
问题出在void MyThread::run(void)
行timer = new QTimer(this);
。线程实例是在(并因此拥有)不同(主)线程中创建的,然后它表示。因此,您不能将线程用作创建内部线程的对象的父级。在您的情况下,解决方案很简单 - 在堆栈上创建计时器。
示例:强>
void MyThread::run(void)
{
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(count()));
timer.start(intvl);
// Following method start event loop which makes sure that timer
// will exists. When this method ends, timer will be automatically
// destroyed and thread ends.
exec();
}
第二个问题 - GUI未更新
Qt要求从主应用程序线程创建和显示GUI。这意味着用大操作阻塞主线程整个GUI。只有两种可能的解决方案:
QCoreApplication::processEvents()
- 每次调用此事件时,都会处理事件,包括调用插槽,键和鼠标事件,GUI重绘等。您调用的频率越高,越多响应式GUI将是。这个实现起来比较简单(只需在代码中放一行),但它非常难看,如果操作阻止加载文件,GUI会冻结一段时间。这意味着GUI的响应性取决于调用之间的间隔。 (但如果你经常调用它,你会减慢加载进度。) 不鼓励使用这种技术,最好避免使用它...... 答案 2 :(得分:0)
我将根据default example on Qthread页面
执行此操作controller->openProject();
以便更新变量statusChanged(int)
)提供对该变量的访问权。代码看起来像
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
private:
int status; //
bool hasworktoDo();
void doSmallWork();
public slots:
void doWork() {
while(hasworktoDo())
{
doSmallWork(); // fragments of controller->openProject();
++status; //previously set to 0
emit statusChanged(status);
}
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
void statusChanged(int value);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(statusChanged(int)), this, SLOT(updateStatus(int)));
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate()), worker, SLOT(doWork()));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
void updateStatus(int value)
{
int status = value;
//now you have what to use to update the ui
}
signals:
void operate(); //
};
如果您在小部件中打破openProject
操作时遇到问题,那就意味着在用户界面上说你所在的50%
毫无意义...只需说出Loading...
和使代码更简单。请注意,在我的情况下,您不需要计时器,因为工作人员会定期发送更新