[Qt] Delivering Embedded Panda3D in Qt (X11 / Windows)

Good morning,

This used to be a question, but it turns out I solved everything : the forum already has clues about how to do this, but no piece of code doing everything for you :slight_smile: !

So this is what I’m delivering to you : the simplest way to include Panda in Qt, with everything handled (as far as I know).
It works with X11 and Windows too (thanks to Tah for Windows compatibility) :wink: !

It is composed of two classes :

  • QPandaApplication : reimplementing QApplication and including PandaFramework.
    Note: you will need to explicitly terminate your application using the slot method “Close()”.
    To get the instance of PandaFramework, use the static method Framework.

  • QPandaWidget : a QWidget displaying a Panda3D window.
    Note: everything is handled transparently by the widget. All you would need to draw is the WindowFramework embedded in the widget. You can get it with the method “Window()”.
    You can’t use the WindowFramework before the widget has been initialized : wait for the Initialized signal to know when you can start drawing meshes all over the place.
    This QWidget also supports the mousepress and mouserelease events : you can handle them like in any other QWidget by reimplementing mousePressEvent / mouseReleaseEvent. All the details of the click (except keyboard modifiers) are specified in the QMouseEvent.

qpandaapplication.h

#ifndef QPANDAAPPLICATION_H
# define QPANDAAPPLICATION_H

# include <QApplication>
# include <panda3d/pandaFramework.h>
# include <panda3d/pandaSystem.h>

class QPandaApplication : public QApplication
{
    Q_OBJECT
public:
    QPandaApplication(int argc, char **argv);
    ~QPandaApplication();

    int                    exec(void);

    static PandaFramework& Framework(void) { return (_framework); }
    static void            Close(void);

public slots:
    void                   Terminate(void) { Close(); }

private:
    static bool           _continue;
    static PandaFramework _framework;
};

#endif // QPANDAAPPLICATION_H

qpandaapplication.cpp

#include "qpandaapplication.h"

PandaFramework QPandaApplication::_framework;
bool           QPandaApplication::_continue;

QPandaApplication::QPandaApplication(int argc, char **argv) : QApplication(argc, argv)
{
  _framework.open_framework(argc, argv);
  _continue = true;
}

QPandaApplication::~QPandaApplication()
{
  _framework.close_framework();
}

int            QPandaApplication::exec(void)
{
    Thread*    currentThread = Thread::get_current_thread();

    do
    {
        processEvents();
    } while (_continue && _framework.do_frame(currentThread));
    return (0);
}

void           QPandaApplication::Close(void)
{
    _continue = false;
}

qpandawidget.hpp

#ifndef QPANDAWIDGET_H
# define QPANDAWIDGET_H

# include <QWidget>
# include <panda3d/pandaFramework.h>
# include <panda3d/pandaSystem.h>

class QPandaWidget : public QWidget
{
    Q_OBJECT
public:
    explicit QPandaWidget(QWidget *parent = 0);
    virtual ~QPandaWidget();

    virtual void     resizeEvent(QResizeEvent*);
    virtual void     showEvent(QShowEvent*);

    QPaintEngine*    paintEngine() const { return (0); }
    void             paintEvent(QPaintEvent*) {}
    virtual void     closeEvent(QCloseEvent*);

    WindowFramework* Window(void) const { return (_window); }

signals:
    void             Initialized(void);

private:
    void             UpdateSize(void);
    static void      CallbackMouse(const Event* event, void* ptr);

    WindowFramework* _window;
    QSize            _size;
};

#endif // QPANDAWIDGET_H

qpandawidget.cpp

#include "qpandawidget.h"
#include <QResizeEvent>
#ifdef Q_WS_X11
# include <Qt/qx11info_x11.h>
# include <X11/Xlib.h>
#endif
#include <iostream>
#include "qpandaapplication.h"

QPandaWidget::QPandaWidget(QWidget *parent) : QWidget(parent)
{
    EventHandler& events = QPandaApplication::Framework().get_event_handler();

    _window = 0;
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_NoSystemBackground);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setFocusPolicy(Qt::StrongFocus);
    events.add_hook("mouse1",    QPandaWidget::CallbackMouse, this);
    events.add_hook("mouse2",    QPandaWidget::CallbackMouse, this);
    events.add_hook("mouse3",    QPandaWidget::CallbackMouse, this);
    events.add_hook("mouse1-up", QPandaWidget::CallbackMouse, this);
    events.add_hook("mouse2-up", QPandaWidget::CallbackMouse, this);
    events.add_hook("mouse3-up", QPandaWidget::CallbackMouse, this);
}

QPandaWidget::~QPandaWidget()
{
    EventHandler& events = QPandaApplication::Framework().get_event_handler();

    events.remove_hooks_with(this);
    if (_window)
      QPandaApplication::Framework().close_window(_window);
}

void QPandaWidget::UpdateSize(void)
{
    if (_window)
    {
        WindowProperties wp;

        _window->get_graphics_window()->request_properties(wp);
        wp.set_size(_size.width(), _size.height());
        wp.set_origin(0, 0);
        _window->get_graphics_window()->set_properties_now(wp);
    }
}

void QPandaWidget::resizeEvent(QResizeEvent* event)
{
    _size = event->size();
    UpdateSize();
    QWidget::resizeEvent(event);
}

void QPandaWidget::showEvent(QShowEvent* event)
{
    if (_window == 0)
    {
        WindowProperties wp;

#ifdef Q_WS_X11
        XFlush(QX11Info::display());
#endif
        QPandaApplication::Framework().get_default_window_props(wp);
        wp.set_parent_window((size_t)this->winId());
        _window = QPandaApplication::Framework().open_window(wp, 0);
        UpdateSize();
        Initialized();
    }
    QWidget::showEvent(event);
}

void QPandaWidget::closeEvent(QCloseEvent*)
{
    QPandaApplication::Close();
}

// MOUSE MANAGEMENT
void QPandaWidget::CallbackMouse(const Event* event, void* ptr)
{
    QPandaWidget*     instance = reinterpret_cast<QPandaWidget*>(ptr);
    MouseData         data     = instance->_window->get_graphics_window()->get_pointer(0);

    if (data.get_in_window())
    {
        QPoint            pos(data.get_x(), data.get_y());
        std::string       name     = event->get_name();
        Qt::MouseButton   button;
        QMouseEvent::Type type;
        char              buttonN;

        if (name.substr(name.size() - 2) == "up")
        {
            type = QMouseEvent::MouseButtonRelease;
            name = name.substr(0, name.size() - 3);
        }
        else
            type = QMouseEvent::MouseButtonPress;

        buttonN  = name[name.size() - 1];
        name = name.substr(0, name.size() - 1);

        switch (buttonN)
        {
        default:
        case '1':
            button = Qt::LeftButton;
            break ;
        case '2':
            button = Qt::RightButton;
            break ;
        case '3':
            button = Qt::MiddleButton;
            break ;
        }

        if (type == QMouseEvent::MouseButtonPress)
        {
          QMouseEvent event(type, pos, button, 0, Qt::NoModifier);

          instance->mousePressEvent(&event);
        }
        else
        {
          QMouseEvent event(type, pos, button, 0, Qt::NoModifier);

          instance->mouseReleaseEvent(&event);
        }
    }
}

Here you go folks ! I really hope this will help !

Hi

Hey, good work !
I got it running on windows, just a few little mods to get it to fill the window.
I replaced the wp.set_fullscreen(true); with wp.set_origin(0,0);

Added a QPandaWidget::closeEvent so when the user clicks on the close “x” on upper right of window to shuts everything down.
And had to make _continue a static bool and void Close(void); a static void (in qpandaapplication.h )

here is the QPandaWidget::closeEvent

void QPandaWidget::closeEvent(QCloseEvent *e)
{

    QPandaApplication::Close();

}

just add void closeEvent(QCloseEvent *ev); to the .h file in private part.

here is a basic main:

#include "qpandaapplication.h"
#include "qpandawidget.h"

int main(int argc, char *argv[])
{

  QPandaApplication a(argc, argv);

  // get the PandaFramework from the QPandaApplication !!!!
  PandaFramework framework = a.Framework();

  // create a QPandaWidget
  QPandaWidget *panda =  new QPandaWidget;
  panda->show();

  // get the WindowFramework from the QPandaWidget !!!!
  WindowFramework* window = panda->Window();

  //set the window title to my Panda3D window
  framework.set_window_title("My Panda3D Window");

  // here is room for your own code
  // load the environment model,  basic Panda land demo
  NodePath environ=window->load_model(framework.get_models(),"models/environment");

  // reparent the model to render
  environ.reparent_to(window->get_render());

  //apply scale and position transforms to the model
  environ.set_scale(0.25, 0.25, 0.25);
  environ.set_pos(-8, 42, 0);

  window->setup_trackball();

  return a.exec();

}

This can help make new Panda tools
Thanks again

\o/ ! Thanks !
I added those changes to the next version. Which will also include support for QMouseEvents (mousePressEvent and mouseReleaseEvent from QWidget will be called properly).

Well, that is, if I manage to find out why the EventHandler doesn’t call my hooks. I’ll update as soon as this problem is solved.

I had some problems with QEvents in the Panda part of the window, the Panda seems to be very territorial…ha
I’ve been working on a type of scene viewer/editor, but I do things in another way than you do. I make a QMainWindow and set the Panda WindowProperties parent to the QMainWindow centralWidget, then I have the Panda main loop that calls qApp.processEvents();

I’m okay with this since I wanted to use the Panda events in the Panda window.

There is a post, started by someone else who wanted to use Panda with Qt and I made some comments there about how I was doing things and problems.

perhaps you have seen it already…
[url]Panda 3D and Qt with C++]

edit
Ha… I just looked over the all of that post again and I see you had already found it and replied there