Интеграция Qt в предварительно написанное приложение/фреймворк

У меня есть небольшая структура визуализации, написанная на С++, и я хочу использовать Qt, чтобы иметь правильный графический интерфейс и контролировать свои визуализации для взаимодействия с ними. в настоящее время я использую GLUT для создания окна и рисования в нем представления. поэтому все, что я делаю, это инициализирую объект класса Visualization, который делает все за меня: хранит модель и представления. само представление содержит контроллер для обработки пользовательского ввода и управления моделью. мой основной цикл выглядит так:

Visualization* vis = new Visualization();
vis->createModel("some_file.txt");
vis->createView("unknown");

// ...

void demo_app::display()
{
  // clear the framebuffer
  glClearColor(0.3, 0.3, 0.3, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  vis.update(); // looks for dirty scenes to update
  vis.render(); // draws all views which are stored in vis

  glutSwapBuffers();
}

с GLUT у меня есть одно окно, в котором я упорядочиваю несколько представлений. когда я заказываю представление для рисования во время основного цикла, я могу выполнить glCommands, и все в порядке.

так что теперь моя проблема в том, что я хочу использовать Qt с несколькими окнами (QDialogs+QGLWidgets). Я знаком с Qt и уже портировал фреймворк на Qt. но проблема в том, что мне нужно слишком много интегрировать Qt в фреймворк, а это не то, чего я хочу. Я хочу управлять визуализацией с помощью Qt и ее элементов графического интерфейса, но вызов отрисовки для представления должен выполняться моим классом визуализации, как показано в примере GLUT. Таким образом, должен быть способ дать унаследованному QGLWidget указатель на объект представления, и всякий раз, когда представление должно быть отрисовано, виджет должен вызвать makeCurrent(), чтобы glCommands мог рисовать в контексте. Я также могу использовать текстуру, в которой мое представление рисует текущую сцену, а затем рисовать текстуру в функции paintGL или glDraw() функции QGLWidget, но как я могу сообщить виджету update(), когда представление ничего об этом не знает. Я буду эмулировать основной цикл с QTimer, установленным на 0. Все, что я хочу сделать, это иметь возможность запускать рендеринг QGLWidget внутри моего фреймворка. любое предложение? ссылки? Примеры?


person iam_peter    schedule 08.08.2013    source источник


Ответы (1)


Таким образом, должен быть способ дать унаследованному QGLWidget указатель на объект представления и всякий раз, когда представление должно быть отрисовано.

Если вы вызовете demo_app::display() из QGLWidget::paintGL, это сработает и отрисует сцену в QGLWidget контексте. Однако при этом будет использоваться контекст QGLWidget, поэтому вам нужно будет переместить все функции инициализации в QGLWidget. Я не помню, можно ли совместно использовать объекты OpenGL в контексте, но не удивлюсь, если это невозможно сделать.

но как я могу сообщить виджету об обновлении(), когда представление ничего об этом не знает.

Что ж, добавьте указатель на QWidget в представление и вызовите QWidget->update(). Должно быть очевидно.

Если вы хотите рисовать виджеты qt из окна на основе GLUT...

т.е. если вы хотите «встроить» виджеты Qt в окно GLUT.

Каждый виджет Qt имеет метод render. Вы можете использовать этот метод для рисования виджета на любом QPaintDevice или с помощью любого QPainter.

Таким образом, чтобы отрисовать виджет из фреймворка, вам нужно предоставить один из них виджету и вызвать рендеринг вручную.

Вам также придется перенаправлять события мыши и клавиатуры из фреймворка и преобразовывать их в события Qt.

Это должно быть выполнимо, но это будет большая боль для реализации.

См. также демонстрацию EmbeddedDialogs и QGraphicsProxyWidget.

Если вы просто хотите использовать графические виджеты в дополнение к окну переполнения, не встраивая их в окно переполнения...

Оберните все, что связано с вашим фреймворком и сообщениями о переполнении, в QObject, и поместите обработку сообщений о переполнении в слот этого объекта. Подключите этот слот к таймеру, работающему с тайм-аутом 0.

Пример из моего кода (использует SDL):

class Game: public QObject{
Q_OBJECT
protected:
    void processSdlEvents();
...
protected slots:
    void onGameEnd();
    void timerSlot();
...
};

Game::Game(/*arguments*/ ...){
...
    gameTimer = new QTimer(this);
    gameTimer->setSingleShot(false);
    gameTimer->setInterval(0);
    connect(gameTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
...
}

void Game::timerSlot(){
    processSdlEvents();
    update();
    render();
}

void Game::processSdlEvents(){
    SDL_Event event;
    QList<GameEventHandler*> eventHandlers = findChildren<GameEventHandler*>();
    /*WARNING: This is NOT an infinite loop. */
    while (SDL_PollEvent(&event) != 0){
        if (event.type == SDL_QUIT){
            emit gameEnded();
            continue;
        }
        bool processed = false;
        for (int i = 0; i < eventHandlers.size(); i++){
            if (eventHandlers[i]->processEvent(&event)){
                processed = true;
                break;
            }
        }
        if (processed)
            continue;
        for (int i = 0; i < joysticks.size(); i++){
            if (joysticks[i]->processEvent(&event)){
                processed = true;
                break;
            }
        }
        /*if (processed)
            continue;*/
    }
}


int main(int argc, char** argv){
    QApplication app(argc, argv);
    int result = 0;
    try{
        Game game;
        QObject::connect(&game, SIGNAL(gameEnded()), &app, SLOT(quit()));
        game.start();
        result = app.exec();
    }
    catch(const QString& s){
        reportException(s);
    }
    catch(std::exception& e){
        reportException(e);
    }
    catch(...){
        reportException();
    }
    return result;
}

Обратите внимание, что processSdlEvents НЕ является бесконечным циклом. Он вызывается время от времени и обрабатывает все события, полученные с момента последнего вызова, а затем завершается.

В этом сценарии функции, специфичные для вашего фреймворка, вызываются из основного цикла Qt.

Возможно, вы также могли бы сделать это наоборот и вызвать функции, специфичные для Qt, из основного цикла в вашей собственной структуре (используя класс QEventLoop или QApplication::processEvents()), но это может быть менее надежным, и я сам этого не пробовал.

person SigTerm    schedule 08.08.2013
comment
я не хочу вызывать свой demo_app::display() из QGLWidget. это должно быть выполнено в основном цикле, а затем представления отрисовываются. я не хочу, чтобы paintGL() запускала мою основную функцию отображения, потому что должно быть несколько QGLWidgets с разными представлениями. - person iam_peter; 08.08.2013
comment
@iam_peter: это должно быть выполнено в основном цикле Эм, нет, это неправильно, и нет реальной причины вызывать отображение вручную из основного цикла (возможно, если только вы не используете полноэкранное приложение). Qt так не работает. Виджет вызывает paint/paintGL при необходимости. Если вы хотите непрерывно перерисовывать, вызывайте update непрерывно. Непрерывное перерисовывание нескольких виджетов из основного цикла просто приведет к пустой трате ресурсов процессора и не принесет ничего полезного. - person SigTerm; 08.08.2013
comment
функция отображения определяет, какой вид следует перерисовать из-за некоторых изменений модели. этот механизм не должен быть направлен на Qt, это должен делать мой класс визуализации, поэтому я вызываю display/update в основном цикле. во время отображения вызова представления перерисовываются соответственно, это не должно запускаться Qt. Все, что должен делать Qt, — это информировать мое представление о взаимодействии с пользователем, поэтому оно становится грязным. тогда мой планировщик (= визуализация) должен запускать все остальное. - person iam_peter; 08.08.2013
comment
@iam_peter: я не согласен. На мой взгляд, вы ставите телегу впереди лошади. Нет причин придерживаться этого дизайна, особенно если используемому вами фреймворку это не нравится. Просто обновите соответствующий вид при изменении модели, этого должно быть достаточно. - person SigTerm; 08.08.2013