我有小部件:
class Main_Widget : public QWidget
{
Q_OBJECT
public:
explicit Main_Widget(QWidget *parent = 0);
~Main_Widget();
private:
MyOGLWidget *mOGL;
QThread *mThread;
QPushButton *mStart;
QPushButton *mStop;
}然后,我创建了以下所有内容:
mOGL = new MyOGLWidget();
mThread = new QThread();
mStart = new QPushButton();
mStop = new QPushButton();
//move to other thread
mOGL->moveToThread(mThread);我想在mOGL使用动画。对于这个想法,我有以下代码:
class MyOGLWindow : public QGLWidget
{
private:
bool mEnd; //default false
//...
public:
void doLoop()
{
while(mEnd)
{
//animation
updateGL();
}
}
public slots:
void slotStart()
{
mEnd = true;
}
void slotStop()
{
mEnd = false;
}
}我把我的两个按钮连接到slotStart(),slotStop()。但是如果我使用“开始”按钮(这会导致slotStart()),我的Main_Widget是冻结的,但是我的动画是成功的。如何启动无穷大循环而不冻结Main_Window,以及如何停止?
发布于 2014-04-06 05:39:18
除了OpenGL视图之外,UI会冻结,因为您没有返回到事件循环。将从QWidget派生的任何类(包括QGLWidget )移到另一个线程是错误的。
要从另一个线程进行呈现,需要将QGLContext (而不是QGLWidget)移动到呈现线程。遵循QGLWidget在这方面的文档。小部件的画图事件处理程序必须什么都不做,因为否则它将使用错误(GUI)线程中的GL上下文。您可以使用在呈现线程中运行的QObject中的零持续时间计时器来避免对自定义end标志的需求。每当通过调用QThread::quit()完成线程的事件循环时,对象将停止执行。
您还需要使用一个可以安全销毁的线程类。正确设计的C++类始终是可销毁的。QThread是个怪人--我们在这里修好它。
作为一种风格,没有必要在堆中分配小部件子部件。实际上,这是对堆的少量浪费,因为堆块的开销与QObject实例的大小相当。
下面的草图显示了Qt中多线程OpenGL需要解决的问题。
class Thread : public QThread {
using QThread::run; // final
public:
~QThread() { quit(); wait(); }
};
class ThreadGLWidget : public QGLWidget
{
void paintEvent(QPaintEvent *) {}
void resizeEvent(QResizeEvent *) { emit resized(size()); }
public:
explicit ThreadGLWidget(QWidget * parent = 0) : QGLWidget(parent) {
// Release the context in this thread.
doneCurrent();
}
Q_SIGNAL void resized(QSize);
};
class Animator : public QObject
{
Q_OBJECT
QBasicTimer mTimer;
QSize mWidgetSize;
QPointer<QGLContext> mGLContext;
void nextFrame() {
mGLContext.makeCurrent();
...
updateGL();
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == mTimer.timerId()) nextFrame();
}
public:
explicit Animator(QGLContext * ctx, QObject * parent = 0) :
QObject(parent), mGLContext(ctx)
{
// The use of the timer obviates the custom stop flag. Our
// thread's event loop is not blocked and is quittable.
mTimer.start(0, this);
}
Q_SLOT void setSize(QSize size) { mWidgetSize = size; }
};
class Main_Widget : public QWidget
{
Q_OBJECT
public:
explicit Main_Widget(QWidget *parent = 0) : QWidget(parent),
mLayout(this), mStart("Start"), mStop("Stop"),
mAnimator(mOGL.context())
{
mLayout.addWidget(&mOGL, 0, 0, 1, 2);
mLayout.addWidget(&mOGL, 1, 0);
mLayout.addWidget(&mOGL, 1, 1);
mAnimator.setSize(mOGL.size());
mOGL.context()->moveToThread(&mThread);
mAnimator.moveToThread(&mThread);
mAnimator.connect(&mOGL, SIGNAL(resized(QSize)), SLOT(setSize(QSize)));
mThread.start();
}
private:
QGridLayout mLayout;
ThreadGLWidget mOGL;
QPushButton mStart;
QPushButton mStop;
Animator mAnimator; // must be declared before its thread and after the GL widget
QThread mThread;
// Destruction order of GL-related objects:
// 1. mThread - stops the animation, makes the animator and context threadless
// 2. mAnimator - now threadless, can be destructed from our thread
// 3. mOGL - its context is threadless and can be destructed from our thread
}发布于 2014-04-06 18:14:57
在QTimer**,上处理动画的正确方法是使用,而不是使用** QThread!
覆盖QGLWidget的初始化方法initializeGL()来启动计时器。您可以使用类似的方法,它每隔33 use调用一次_tick():
QTimer::singleShot(33, this, SLOT(_tick()));我只想澄清singleShot()是QTimer的一个静态方法,它只触发一次。这意味着当_tick() (私有时隙)被调用时,它应该做一些事情:
paintGL()间接触发updateGL();这就是使用QGLWidget处理动画的方法。
发布于 2014-04-06 05:34:19
所有GUI操作必须在一个线程中运行。
http://qt-project.org/doc/qt-4.8/thread-basics.html#gui-thread-and-worker-thread
GUI线程和工作线程 如前所述,每个程序在启动时都有一个线程。这个线程被称为“主线程”(在Qt应用程序中也称为"GUI线程“)。Qt必须在这个线程中运行。所有的小部件和几个相关的类,例如QPixmap,都不能在辅助线程中工作。辅助线程通常被称为“工作线程”,因为它用于从主线程卸载处理工作。
在Qt 5中也是如此,
http://qt-project.org/doc/qt-5/thread-basics.html#gui-thread-and-worker-thread
https://stackoverflow.com/questions/22889963
复制相似问题