Russian Qt Forum
Ноябрь 23, 2024, 16:16 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Использование OpenGL во втором потоке на Qt  (Прочитано 8836 раз)
Tataina
Гость
« : Июль 07, 2011, 09:48 »

 Доброе утро!
 Уже несколько дней бьюсь над тем,чтобы перенести полностью отрисовку изображения из GUI потока в другой. Существует QGLWidget, находящийся в основном потоке, необходимо в другом потоке используя Qt и OpenGL (не QPixmap, QImage и т.д.) сделать картинку и отобразить ее в основном потоке.
Поток создается, сигнал-слотовое соединение работает. Вопрос только в том,что не понимаю как и на чем нарисовать OpenGL-командами в другом потоке, и как передать это изображение в основной!
Возможно, кто-то сталкивался с подобным.
Заранее спасибо!
« Последнее редактирование: Июль 08, 2011, 09:44 от Tataina » Записан
climber
Гость
« Ответ #1 : Июль 08, 2011, 08:20 »

Насколько мне известно гуевый поток может быть только один. Соответсвенно в отдельный поток можно и нужно выносить только обработку данных, а сама отрисовка будет происходить в главном (гуевом) потоке после получения сигнала finished(). Для создания потока обработки данных нужно создать свой класс унаследованный от QThread. Затем переопределяется метод run() под ваши конкретные задачи. Далее запускается созданный поток start(), а в коструктор класса вызвавшего этот дочерний поток вешается
connect(thread, SIGNAL(finished()),this,SLOT(_обработчик конца выполнения потока_));
Таким образом все вычисления и обработки данных в дочернем потоке, а отрисовка в главном потоке. Как то так. Если интересно, подробнее это описано тут http://www.opennet.ru/docs/RUS/qt3_prog/x7810.html
Записан
Tataina
Гость
« Ответ #2 : Июль 08, 2011, 09:44 »

на данный момент я обнаружила статью, в которой якобы приводится пример работы многопоточного приложения.
вот она: http://doc.qt.nokia.com/qq/qq06-glimpsing.html#generaltipshttp://doc.qt.nokia.com/qq/qq06-glimpsing.html#generaltips
так вот при перекладывании этой проги на Qt4 у меня даже виджет в качестве окошка не оказывается. просто черный экран в углу  mainwindow. и я совершенно определенно не нашла в этой проге изменения контекста, и самого рисования пирамидки..
может кто-то сможет помочь? могу выслать исходники программы,написанной под  Qt 4.7..
Записан
twp
Гость
« Ответ #3 : Июль 08, 2011, 14:50 »

да, было бы неплохо выложить исходники. А в одном потоке все работает ок?
Записан
Tataina
Гость
« Ответ #4 : Июль 08, 2011, 15:25 »

Класс наследника QGLWidget
Код:
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include "glthread.h"
class GLWidget : public QGLWidget
{

public:
    GLWidget(QWidget *parent);
    void startRendering();
    void stopRendering();

protected:
    void resizeEvent(QResizeEvent *evt);
    void paintEvent(QPaintEvent *);
    void closeEvent(QCloseEvent *evt);

    GLThread glt;
};
реализация GLWidget:
Код:
#include "glwidget.h"
#include <QResizeEvent>
GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(parent),
      glt(this)
{
    setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
    qglClearColor(Qt::white);
    setAutoBufferSwap(false);
    resize(320, 240);
}

void GLWidget::startRendering()
{
     glt.start();
}

void GLWidget::stopRendering()
{
    glt.stop();
    glt.wait();
}

void GLWidget::resizeEvent(QResizeEvent *evt)
{
    glt.resizeViewport(evt->size());
}

void GLWidget::paintEvent(QPaintEvent *)
{
    // Handled by the GLThread.
}

void GLWidget::closeEvent(QCloseEvent *evt)
{
    stopRendering();
    QGLWidget::closeEvent(evt);
}
Наследник QThread:
Код:
#ifndef GLTHREAD_H
#define GLTHREAD_H

#include <QThread>
#include <QSize>

class GLWidget;
class GLThread : public QThread
{
public:
    GLThread(GLWidget *glWidget);
    void resizeViewport(const QSize &size);
    void run();
    void stop();

private:
    bool doRendering;
    bool doResize;
    int w;
    int h;
    int rotAngle;
public:
    GLWidget *glw;
};
#endif // GLTHREAD_H

Реализация методов:
Код:
#include "glthread.h"
#include <QtGui>
#include <QtOpenGL>
#include "glwidget.h"
GLThread::GLThread(GLWidget *gl)
    : QThread(), glw(gl)
{
    doRendering = true;
    doResize = false;
   // glw->qglClearColor(Qt::white);
}

void GLThread::stop()
{
    doRendering = false;
}

void GLThread::resizeViewport(const QSize &size)
{
    glMatrixMode(GL_PROJECTION);
    w = size.width();
    h = size.height();
    doResize = true;
}

void GLThread::run()
{
    srand(QTime::currentTime().msec());
    rotAngle = rand() % 360;

    glw->makeCurrent();
   // glw->qglClearColor(Qt::white); // qglClearColor(Qt::white);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-5.0, 5.0, -5.0, 5.0, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glViewport(0, 0, 200, 200);
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);

//    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 // test of painting
//    glBegin(GL_LINES);
//    glVertex2f(-0.6,-0.4);
//    glVertex2f(0.6,-0.4);
//    glVertex2f(-0.6,-0.6);
//    glVertex2f(0.6,-0.6);
//    glVertex2f(-0.6,-0.8);
//    glVertex2f(0.6,-0.8);
//    glEnd();
    while (doRendering) {
        if (doResize) {
            glViewport(0, 0, w, h);
            doResize = false;
        }

        glw->swapBuffers();
        msleep(40);
    }
}
соединение этих классов происходит в еще одном классе- наследнике QMainWindow. в таком формате происходит добавление нескольких новых потоков,которые будут показывать вращение картинки.. если есть возможность помочь исправить и оставить только 2 потока, буду рада. т.е. в одном потоке создается виджет, а во втором он отрисовывается.. это необходимо по рабочим соображениям..
класс AppWindow:
Код:
#ifndef APPWINDOW_H
#define APPWINDOW_H
#include <QMainWindow>
#include <QWorkspace>
class AppWindow: public QMainWindow
{
    Q_OBJECT
public:
    AppWindow();

protected:
    void closeEvent(QCloseEvent *evt);

private slots:
    void newThread();
  //  void killThread();

private:
    QWorkspace *ws;
};

#endif // APPWINDOW_H
реализация:
Код:
#include "appwindow.h"
#include "glwidget.h"
#include "glthread.h"
#include<QtGui>
#include <QMenu>
#include<QMenuBar>
#include <QAction>
AppWindow::AppWindow()
    : QMainWindow(0)
{
    QMenu *menu = new QMenu(this);
    menuBar()->addMenu(menu);
    menu=menuBar()->addMenu("&Thread");

    menu->addAction("&New thread", this, SLOT(newThread()), Qt::CTRL+Qt::Key_N);
   // menu->addAction("&End current thread", this, SLOT(killThread()), Qt::CTRL+Qt::Key_K);
    menu->addSeparator();
    menu->addAction("&Exit", qApp, SLOT(quit()), Qt::CTRL+Qt::Key_Q);

    menuBar()->show();
    ws = new QWorkspace(this);
    setCentralWidget(ws);
}

void AppWindow::closeEvent(QCloseEvent *evt)
{
    QWidgetList windows = ws->windowList();
    for (int i = 0; i < int(windows.count()); ++i) {
        GLWidget *window = (GLWidget *)windows.at(i);
        window->stopRendering();
    }
    QMainWindow::closeEvent(evt);
}

void AppWindow::newThread()
{
    QWidgetList windows = ws->windowList();
    GLWidget *widget = new GLWidget(ws);
    widget->setWindowTitle("Thread #" + QString::number(windows.count() + 1));
   // widget->resize(200,200);
    widget->show();
    widget->startRendering();
}

//void AppWindow::killThread()
//{
//    GLWidget *widget = (GLWidget *)ws->activeWindow();
//    if (widget) {
//    widget->stopRendering();
//        delete widget;
//    }
//}
ну и сам мейн
Код:
#include <QApplication>
#include "appwindow.h"
#include "glwidget.h"
#include "glthread.h"

int main(int argc, char *argv[])
{
        QApplication app(argc, argv);
        AppWindow aw;
       // app.setMainWidget(&aw);
        aw.show();
        return app.exec();
}
реализация в одном потоке работает отлично, потому что там не надо запариваться с контекстами и т.д.
Записан
twp
Гость
« Ответ #5 : Июль 08, 2011, 15:35 »

а можно сам архив проекта приаттачить к теме? Просто создавать весь проект влом  Улыбающийся
Записан
Tataina
Гость
« Ответ #6 : Июль 08, 2011, 16:16 »

под Qt creator 2.2.1
Записан
twp
Гость
« Ответ #7 : Июль 08, 2011, 18:03 »

ок, попробую на досуге. вообще есть  рабочий пример Но там используются мютексы что имхо не есть хорошо, возможно есть возможность обойтись без них
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Июль 09, 2011, 11:25 »

Мои познания в OpenGL допотопны, тоже интересно узнать как это делается. По смыслу вроде нет помех вызывать все ф-ции OpenGL из др. нитки (ведь нужно просто "не в главной" а не "параллельно"). А вот SwapBuffers наверняка завалит систему. Я бы спробовал так

- отключить SwapBuffers и убедиться что все живет
- вместо SwapBuffers извлечь пиксели из внутреннего буфера (glReadPixels) и в главной нитке его уже рисовать

Не исключено этот примитивный вариант будет работать
Записан
Tataina
Гость
« Ответ #9 : Июль 09, 2011, 16:13 »

swap действительно это заваливает =) подготовка данных в другом потоке- это очень популярная тема, и на многих форумах это обсуждается, и много где, предлагаются решения, но это не совсем то,что нужно лично для моей задачи. Это поможет улучшить,но все также будет грузить основной поток, который и так нагружен по самое не балуйся..

Пример с немецкого форума еще не проверяла, пыталась решить проблему со своим виджетом, который совсем не хочет отображаться..
Записан
twp
Гость
« Ответ #10 : Июль 11, 2011, 15:01 »

доработал я пример и все работает.
Я просто взял пример из книги по OpenGL и адаптировал его к этому приложению.
То я и спрашивал, работает ли приложение без использования потока.
Это приложение врядли подойдет ибо здесь происходит постоянная перерисовка через каждые 40 мс
Лучше за основу взять приложение с немецкого форума. Там отрисовка происходит только когда это действительно необходимо
и больше подходит для статических сцен.
Насчет SwapBuffers. Он используется в потоке в обоих демках и не валит приложение. Насколько я понял, главное это сделать QGLWidget::makeCurrent() перед операциями отрисовки и SwapBuffers, а после этого - QGLWidget::doneCurrent(). Вообще исходя из этой статьи , Qt 4.8 и выше будет поддерживать больше возможностей использования потоков в OpenGL
« Последнее редактирование: Июль 11, 2011, 15:18 от twp » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.049 секунд. Запросов: 22.