首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何线程QGLWidget?

如何线程QGLWidget?
EN

Stack Overflow用户
提问于 2014-04-06 04:23:04
回答 3查看 1.5K关注 0票数 2

我有小部件:

代码语言:javascript
复制
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;
}

然后,我创建了以下所有内容:

代码语言:javascript
复制
mOGL = new MyOGLWidget();
mThread = new QThread();
mStart = new QPushButton();
mStop = new QPushButton();
//move to other thread
mOGL->moveToThread(mThread);

我想在mOGL使用动画。对于这个想法,我有以下代码:

代码语言:javascript
复制
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,以及如何停止?

EN

回答 3

Stack Overflow用户

发布于 2014-04-06 05:39:18

除了OpenGL视图之外,UI会冻结,因为您没有返回到事件循环。将从QWidget派生的任何类(包括QGLWidget )移到另一个线程是错误的。

要从另一个线程进行呈现,需要将QGLContext (而不是QGLWidget)移动到呈现线程。遵循QGLWidget在这方面的文档。小部件的画图事件处理程序必须什么都不做,因为否则它将使用错误(GUI)线程中的GL上下文。您可以使用在呈现线程中运行的QObject中的零持续时间计时器来避免对自定义end标志的需求。每当通过调用QThread::quit()完成线程的事件循环时,对象将停止执行。

您还需要使用一个可以安全销毁的线程类。正确设计的C++类始终是可销毁的。QThread是个怪人--我们在这里修好它。

作为一种风格,没有必要在堆中分配小部件子部件。实际上,这是对堆的少量浪费,因为堆块的开销与QObject实例的大小相当。

下面的草图显示了Qt中多线程OpenGL需要解决的问题。

代码语言:javascript
复制
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
}
票数 1
EN

Stack Overflow用户

发布于 2014-04-06 18:14:57

QTimer**,上处理动画的正确方法是使用,而不是使用** QThread

覆盖QGLWidget的初始化方法initializeGL()来启动计时器。您可以使用类似的方法,它每隔33 use调用一次_tick()

代码语言:javascript
复制
 QTimer::singleShot(33, this, SLOT(_tick()));

我只想澄清singleShot()QTimer的一个静态方法,它只触发一次。这意味着当_tick() (私有时隙)被调用时,它应该做一些事情:

  1. 更新所有控制动画的变量(旋转、移动、速度等);
  2. 通过调用paintGL()间接触发updateGL()
  3. 启动一个新的singleShot计时器。 void GLWidget::_tick() { /* 1-更新状态变量*/ /* 2-间接调用paintGL()执行绘图*/ updateGL();/* 3-设置一个新的singleShot计时器再次调用此方法,从现在*/ QTimer::singleShot(33,this,槽(_tick()};

这就是使用QGLWidget处理动画的方法。

票数 1
EN

Stack Overflow用户

发布于 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

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22889963

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档