2014-04-06 6 views
2

나는 위젯이 있습니다
QGLWidget을 쓰는 방법?

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을 정지시키지 않고 중지하는 방법은 무엇입니까?

+0

덩어리가 아닌 전체 코드를 표시하십시오. – lpapp

+0

지금까지 문제가 무엇인지 분명합니다. +1 실제로 문제와 관련된 코드를 보여줍니다. –

+0

@KubaOber : OP는 문제를 재현 할 수있는 자체 포함 된 예제를 보여줍니다. 품질 요구 사항은 결코 적지 않았으며, 절대로 그렇게되지 않을 것입니다. 엄밀히 말하자면, 모든 새로운 라인은 클래스 멤버가 아닌 다른 것을 위해 수행 될 수 있습니다. 모든 것을 [SSCCE] (http://sscce.org) 호환 테스트 케이스로 보는 것이 중요합니다. 대답은 추측 할 수 있지만 틀릴 수도 있습니다. – lpapp

답변

0

모든 GUI 작업은 하나의 스레드에서 실행해야합니다.

http://qt-project.org/doc/qt-4.8/thread-basics.html#gui-thread-and-worker-thread

GUI 스레드 및 작업자 스레드 바와 같이

,이 시작될 때마다 프로그램이 하나 개의 쓰레드를 갖는다. 이 스레드는 "주 스레드"( Qt 응용 프로그램의 "GUI 스레드"라고도 함)라고합니다. Qt GUI는이 스레드에서 실행되어야합니다. 모든 위젯과 관련 클래스 (예 : QPixmap)는 보조 스레드 에서 작동하지 않습니다. 보조 스레드는 일반적으로 스레드의 처리 작업을 오프로드하는 데 사용되므로 "worker 스레드"라고합니다.

이 당신이 이벤트 루프에 반환하지 않기 때문에

http://qt-project.org/doc/qt-5/thread-basics.html#gui-thread-and-worker-thread

+0

사실, OpenGL 컨텍스트는 한 번에 하나의 스레드에서만 액세스 할 수 있기 때문에 유용하지 않습니다. 그것은 질문자의 문제에 대한 해결책을 제공하지 않습니다. –

+0

@KubaOber : 답변이 유용 할뿐만 아니라 해당 Qt 문서의 견적이기도합니다. 도와 주려고하는 사람들에 대해 더 존중하고 감사하십시오. – lpapp

+0

문서에 나와 있기 때문에 도움이된다는 의미는 아닙니다. 사실, OpenGL은 GUI 용이므로 문서는 약간 기만적이지만 별도의 스레드에서 실행할 수 있습니다. 또한 작업자 스레드의 QImage에 렌더링을 수행 한 다음 해당 이미지를 GUI 스레드에 그릴 수도 있습니다. 현대의 플랫폼에서'drawImage'는 다른 복잡한 그림보다 훨씬 빠를 수 있습니다. 예를 들어, 다중 스레드 렌더링을 수행하는 유효한 방법입니다. 그래서 저는이 대답이 도움이되지 않는다고 제 견해를 유지합니다. –

0

UI를, OpenGL의보기가 아닌, 정지, 또한 Qt는 5 사실이다. 파생되는 클래스를 QWidget, 을 포함하여QGLWidget 등 다른 클래스로 옮기는 것은 오류입니다.

다른 스레드에서 렌더링하려면 QGLContext (아니 QGLWidget)을 렌더링 스레드로 이동해야합니다. 이 점에 관해서는 QGLWidget의 문서를 따르십시오. 위젯의 페인트 이벤트 핸들러는 아무 것도하지 않아야합니다. 그렇지 않으면 틀린 (GUI) 스레드의 GL 컨텍스트가 사용되기 때문입니다. 사용자 지정 end 플래그의 필요성을 없애기 위해 렌더 스레드에서 실행중인 QObject에서 0 지속 시간 타이머를 사용할 수 있습니다. 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 
} 
+0

왜 QThread :: run을 사용하는지 잘 모르겠다. // 최종 줄이 필요합니다. – lpapp

+0

@LaszloPapp'run'을 다시 구현하면 이벤트 루프를 실행하지 않고'quit' 메소드가 no-op가되고 소멸자가 실행중인 작업을 종료 할 수 없기 때문에 클래스는 실제로 더 이상 작동하지 않습니다. 그것은'run'이 최종적인 것만으로도 의미가 있습니다.이 클래스는 파생되지 않습니다. 다른'QThread' 재 구현에는 물론 일을 그만두는 방법이 있지만,'Thread'에서 파생해서는 안됩니다. –

+0

1) Q_DECL_FINAL이 있습니다. 2) 더 중요한 것은이 질문의 맥락에서 왜 당신은 그것으로부터 파생 될 것인가? 그것은 그것없이 더 포괄적 일 것입니다. – lpapp

1

에있다가 QTimer 아닌 QThread을 사용하십시오 QGLWidget에 애니메이션을 처리하는 적절한 방법!

타이머를 시작하려면 QGLWidget의 초기화 메소드 initializeGL()을 덮어 씁니다.

QTimer::singleShot(33, this, SLOT(_tick())); 

난 그냥 singleShot() 한 번만 발사 QTimer의 정적 방법이라고 명확히하려면 : 당신은 _tick() 33ms마다 호출하는이 같은 것을 사용할 수 있습니다. 즉 _tick() (개인 슬롯)가 호출 될 때, 그것은 몇 가지해야한다는 것을 의미 : 애니메이션 (회전, 이동, 속도 등)를 제어

  1. 업데이트 모든 변수를;
  2. paintGL() 트리거는 간접적으로 updateGL()을 호출하여 트리거합니다.
  3. 새로운 singleShot 타이머를 시작하십시오.

    void GLWidget::_tick() 
    { 
        /* 1- Update state variables */ 
    
        /* 2- Call paintGL() indirectly to do the drawing */ 
        updateGL(); 
    
        /* 3- Set a new singleShot timer to invoke this method again, 33ms from now */ 
        QTimer::singleShot(33, this, SLOT(_tick())); 
    } 
    

은 당신이 QGLWidget와 애니메이션을 처리하는 방법입니다.