Panda 3D and Qt with C++

Hi everyone,

I’ve search for a way to use Panda3D with Qt in C++ but I didn’t find much. Almost everything I found is used with PyQt and is develop in Python. So do you know how to integrate Panda3d in Qt4 developing in C++?

Thank you,
Quentin.

1 Like

No one can help me ? Does anyone have even just a clus of how I could do it ?

Thank’s

Quentin.

I guess no one else has tried this yet. But why is it a problem? What, in particular, are you trying to do? “Integrating” with a window library means different things to different people.

David

Hi

I was thinking about the same thing for my SceneViewer/Editor. I’ve been just using the standard Panda EventHandler addHook for keyboard and mouse along with tasks. I have Qt4 and use to program with it so I tried it out with Panda; I found some things on the forum to help, this is what I found…

In my case I used Qt Creator to make a simple gui, but basically you just need to get the winId of the QWidget and set that in a Panda WindowProperties object, in my case my QWidget is a QMainWindow

Here’s parts of my code:

#include <QtGui/QApplication>
#include <QtCore/QtCore>
#include "ui_mygraphics.h"

#include <pandaFramework.h>
#include <pandaSystem.h>

PandaFramework framework;
WindowFramework *window;
.
.
int main(int argc, char *argv[]) {

  printf("=== SceneViewer ===\n");

  QApplication qApp(argc, argv);

  QMainWindow *qtMainWin = new QMainWindow;
  Ui::MyGraphics *ui = new Ui::MyGraphics; // generated from Qt Creator
  ui->setupUi(qtMainWin);

  qtMainWin->show();
.
.
  framework.open_framework(argc, argv);

  WindowProperties prop;
  framework.get_default_window_props(prop);

  QWidget *qw = qtMainWin->centralWidget();
  prop.set_parent_window((size_t)qw->winId());

  window = framework.open_window(prop,0);
.
.
// this is the main loop
  Thread *current_thread = Thread::get_current_thread();
  while (framework.do_frame(current_thread)) {
    qApp.processEvents();
  }

  framework.close_framework();

More work needs to be done to get Panda display to resize with the MainWindow and also to exit when MainWindow exits; but I hope this helps.

All my Panda keyboard and mouse controls still work too.

I’m also thinking I might just keep Panda window and have it popup a Qt GUI when I need it, that way I can have a full screen Panda window that I can move around in the scene and use my Panda keyboard and mouse controls, and just pop the Qt GUI when I want to save or load files or select models, etc…

tim

Hi

First of all, thank you very much for taking the time to answer me and sorry for taking so much time to answer you.

I just started to work with the code you show me and I have a stupid question : how do you compile this ? Because, I know how to compile Panda and how to compile Qt project, but how can I compile both at the same time ?
Could you show me your .pro file please ?

Thank you again for helping me.

Quentin.

Oh, sorry I didn’t mention that.
I just compile it with Visual Studios 2008;
put all the Qt4 Creator generated files in the C++ project ( ClassName.h ClassName.cpp moc_ClassName.cpp ) If changes are made to the GUI you may need to first compile the creator project to update the ui_ClassName.h file.
I have a problem with the moc file if I compile the base Qt program (it has it’s own stand alone exe that I don’t need, since I set it up as its own Qt Creator project); so I generate the moc file thus:
moc ClassName.h > moc_ClassName.cpp
and then compile everything in the Panda Visual Studios project. Remember to add the .h file to your Panda main program.

I reworked my code a little so that it calls the class set up in Creator now, and changed the name from mygraphics to QtGUI, so now it looks like this:

#include <QtGui/QApplication>
#include <QtCore/QtCore>
#include "QtGUI.h"

#include <pandaFramework.h>
#include <pandaSystem.h>
.
.
PandaFramework framework;
WindowFramework *window;
.
.
int main(int argc, char *argv[]) {

  printf("=== SceneViewer ===\n");

  QApplication a(argc, argv);

  QtGUI qtGUI;
  qtGUI.show();
.
.
    Thread *current_thread = Thread::get_current_thread();
    while(framework.do_frame(current_thread)) {
      a.processEvents();
    }

    framework.close_framework();
.
.

A bit cleaner, but I still have to work in resize for the Panda window, and my MainWindows File->Exit option needs to let the main while loop now that it should shut down, for now I just use my Panda exit event when user presses Exit key.

here is what the .pro looks like, but again I don’t really use this, it does generate a separate exe for the gui itself but is not needed; maybe I should have created my Qt project as a lib or plugin…

#-------------------------------------------------
#
# Project created by QtCreator 2011-03-11T12:31:02
#
#-------------------------------------------------

TARGET = QtGUI
TEMPLATE = app


SOURCES += main.cpp\
        qtgui.cpp

HEADERS  += qtgui.h

FORMS    += qtgui.ui

Hi, thank you again for your answer.
I managed to compile easily just by adding this two lines in the .pro :

LIBS += -L /usr/lib/panda3d  -lp3framework -lpanda -lpandafx -lpandaexpress -lp3dtoolconfig -lp3dtool -lp3pystub -lp3direct
INCLUDEPATH += /usr/local/include /usr/include/python2.6 /usr/include/panda3d

But now I’m stuck with the integration of the panda Window into the QWidget. This is the piece of code that doesn’t work :

framework.open_framework(argc, argv);

     WindowProperties prop;
     framework.get_default_window_props(prop);

     QWidget *qw = qtGUI.centralWidget();

     prop.set_parent_window((size_t)qw->winId());

     cout<<qw->winId()<<endl;

     window = framework.open_window(prop,0);
     window->setup_trackball();

This is what I have in the shell :

&"warning: GDB: Failed to set controlling terminal: Argument invalide\n"
=== SceneViewer ===
85983253
Known pipe types:
  glxGraphicsPipe
(all display modules loaded.)
:display:x11display
(
error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadWindow (invalid Window parameter)
:display:x11display(error): BadDrawable (invalid Pixmap or Window parameter)
:display:x11display(error): BadDrawable (invalid Pixmap or Window parameter)
:display:x11display(error): BadDrawable (invalid Pixmap or Window parameter)
:display:x11display
(
error
): 
BadDrawable (invalid Pixmap or Window parameter)

The errors start with this line :
prop.set_parent_window((size_t)qw->winId());

And I don’t get why. It might be because I’m working on linux but I’m not sure. I’ll try on Windows later.

Thank you again.
Quentin.

I think it needs to be a window, not a generic widget. So you might want to get the window handle of the main window itself (or a child window or so.)

Thank you for the answer.

But why does it work on Tah example and not with me ? The only different that I see is that he is working with windows.

When I try it directly on a QMainWindow I got this :

&"warning: GDB: Failed to set controlling terminal: Argument invalide\n"
=== SceneViewer ===
85983234
Known pipe types:
  glxGraphicsPipe
(all display modules loaded.)
:display:x11display
(
error
): 
BadMatch (invalid parameter attributes)

Thank’s for helping me.
Quentin.

Hi Zabaki

okay, linux, not sure about that one, I’m compiling on Windows XP.

From the forums I’ve seen the way to get a Qt window handle is to call the winId() member function of the QWidget, and so:
prop.set_parent_window((size_t)qtGUI.centralWidget()->winId());

prop being defined:
WindowProperties prop;

There is actually two ways to set the window handle in the WindowProperties; try the other way too

thus:

WindowProperties prop;
framework.get_default_window_props(prop);
PT (WindowHandle) winH = NativeWindowHandle::make_int((int)qtGUI.centralWidget()->winId());
prop.set_parent_window(winH);

(note: I changed (size_t) to (int) in the cast, that works too, so that’s something you can try too)

Hope something works for you, I don’t have a Linux machine to test it on.

tim

Hello everyone,

I’m back.

Sorry for not answering for a while but I’ve been quite busy. I recently start working on that and I’ve found how to include my Panda3D window into a QtWidget by using effectiveWinId() instead of WinId() :
prop.set_parent_window((unsigned long)this->effectiveWinId());

But my problem is now that I would like to handle all the event (mouse and keyboard) with Qt and not Panda. But the Panda window seems to have the priority and Qt can’t catch any event when the mouse is over the Panda Window. I’ve try to force Qt to take the priority with grabMouse() and grabKeyboard() but it doesn’t work very much. In the end, I will use Panda only to render the scene, I don’t want it to interfere with the mouse and keyboard events.

Does anyone have any idea of how I could do that ?

Thank you.
Quentin.

Hmm, does anyone know if there is an Xlib call we should be making to make the window transparent to mouse events and such? Otherwise, I don’t think it’s in our hands–the mouse events come from the operating system.

David

Hi

don’t know any xlib call, but I tried a few things in Panda and Qt with no success, seems the Panda window will always be on top of any sibling widgets, I can’t even do a raise() function to make a sibling QWidget be on top of Panda; but you can have popup widgets on top and make them always be on top.

If letting Panda handle the mouse isn’t a show stopper, then what you could do is make a member function in your Qt gui class and call that function from inside the Panda function that catches the mouse press, and release, etc. You can pass the button number and mouse x and y position, then in your Qt gui function do something like popup a menu etc, you can even have it position itself to where the mouse cursor is if you passed in the position, you’ll have to add the upper left corner of the Qt gui though.

example:

in my QtGUI:

void QtGUI::pandaMouseEvent(int button, int mx, int my){
  printf("=== QtGUI::pandaMouseEvent button:%d pos:%d %d ===\n",button,mx,my);
  ui->modelEditPopUp->move(x()+mx,y()+my);
  ui->modelEditPopUp->show();
}

in Panda:

//////////////////////////////////////////////////////////////////////////////////////////////
void Viewer::mouse1Event(CPT_Event ev, void *data) {
//////////////////////////////////////////////////////////////////////////////////////////////

  Viewer *v = (Viewer*)data;

  if(v->mMouseWatcher->has_mouse()){

  const Event *e = ev.p();
  const char *key = e->get_name().c_str();
  printf("Viewer::mouse1Event:%s\n",key);

  if(strstr(key,"-up")){
    // handle mouse release
  }else{

    v->mMouseX = v->window->get_graphics_window()->get_pointer(0).get_x();
    v->mMouseY = v->window->get_graphics_window()->get_pointer(0).get_y();

    v->mGuiManager->pandaMouseEvent(1,v->mMouseX,v->mMouseY);
.
.

In my program I don’t need the Qt to get mouse when over the Panda window, I’m using all Panda there to move camera and select terrain pos and objects with the mouse; the Qt mouse events function fine for all the QMainMenu options on the the menu bar.

tim

Hi,

sorry again for taking so long to answer you but I had some other stuff to work on. From now on, I’m about to work on this until it works. So I’ve a problem, I don’t get everything you did. There are several things I don’t get : How do you catch the mouse Event ? Is your Viewer class the main class in your program and what does it contains ? How did you linked your mouseWatcher with your framework ? And did you start the main_loop (I didn’t start it because I intended to handle all the event with Qt) ?

Thank you again,
Quentin.

I’m still working on it and my problem is that I just can’t call the get_mouse function on my framework. I tried

framework->get_mouse(window->get_graphics_output())

where framework is my pandaFramework and

window->get_mouse();

where window is my windowFramework. And everytime, the program just stop here.
Does anyone have any idea ?

Thank’s,
Quentin.

Hi

Yes, I have a Panda main loop since I want Panda events too.

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

  printf("=== SceneViewer ===\n"); 

  QApplication qApp(argc, argv); 
.
.
// this is the main loop 
  Thread *current_thread = Thread::get_current_thread(); 
  while (framework.do_frame(current_thread)) { 
    qApp.processEvents(); 
  } 

The qApp.processEvents lets you get Qt events too, except I had no luck for Qt to get any mouse events when over the Panda window, but I do get Panda mouse events. In order to use panda mouse watcher and events you need to have framework.do_frame(current_thread)) being called in a loop.

So, do you have a Qt main loop (not using .exec()) but a loop of your making ? You would need to put framework.do_frame(current_thread)) in your loop.

The Viewer class is one of many classes in my SceneViewer program, it handles the controling of the Camera using mouse and key events. The member function Viewer::mouse1Event(CPT_Event ev, void *data)
is a function that is passed to the Panda EventHandler add_hook function and assigns mouse1 as its “trigger”; everytime the left mouse is pressed then Viewer::mouse1Event gets called

Hi,

Thank you for your answer. I now have a problem with the hook and the event. It doesn’t seems to be working. This is what I have :

void mouse1Event(const Event* ev, void *data) {
    printf("itworks\n");
}

CPT_Event raise an error during compilation. So I changed it to “const Event*”. Is it allright ?
And after the initialisation of my window :

EventHandler evHandler = framework->get_event_handler();
    evHandler.add_hook("mouse1",&mouse1Event,NULL);

I also tried :

window->enable_keyboard();
        framework->define_key("mouse1","mouse event",mouse1Event,0);

But it doesn’t work either.

Do you have any idea of where is my problem ?

Here is the initialisation part of my code :

testPanda::testPanda(QWidget *parent) :
    QWidget(parent)
{
    framework= NULL;
    window=NULL;

    setAttribute(Qt::WA_KeyCompression, true);

    setMinimumSize(600, 600);
}

testPanda::~testPanda()
{
}

void mouse1Event(const Event* ev, void *data) {
    printf("itworks\n");
}

void testPanda::initializeFramework(){
    if (framework == NULL){
        printf("initialisation\n");

        framework = new PandaFramework();
        int num = 1;
        char* pandatest = "pandatest";
        char** pandatest2 = &pandatest;
        framework->open_framework(num,pandatest2);
    }
}

void testPanda::initializeWindow(){
    if (window == NULL){
        WindowProperties prop;
        framework->get_default_window_props(prop);
        prop.set_parent_window((unsigned long)this->effectiveWinId());
        prop.set_size(width(),height());
        prop.set_origin(x()+20,y());
        //prop.set_undecorated(true);

        window = framework->open_window(prop,0);
        window->enable_keyboard();
        framework->define_key("mouse1","oi",mouse1Event,0);
        initializeEvent();

        createScene();
    }
}
void testPanda::initializeEvent(){
    printf("mdk,\n");
    mouse_watcher = (MouseWatcher*)window->get_mouse().node();
    EventHandler evHandler = framework->get_event_handler();
    evHandler.add_hook("mouse1",&mouse1Event,NULL);

}

Thank’s again,
Quentin.

This line:

EventHandler evHandler = framework->get_event_handler(); 

creates a copy of the EventHandler returned by framework->get_event_handler(). Try this line instead:

EventHandler &evHandler = framework->get_event_handler(); 

David

here’s how I set up the mouse1event

in my Viewer.h

class Viewer
{
.
.
  private:
.
.
    PT(MouseWatcher) mMouseWatcher;
.
.
    static void mouse1Event(CPT_Event ev, void *data);
.
.

in my Viewer.cpp

Viewer::Viewer(PandaFramework *fw, QtGUI *gui){

  window = framework->get_window(0);
.
.
  mMouseWatcher = DCAST(MouseWatcher, window->get_mouse().node()); 
.
.
}


void Viewer::addMouseEvents(){

  EventHandler &eventHandler = framework->get_event_handler();

  eventHandler.add_hook("mouse1", ( EventHandler::EventCallbackFunction* )mouse1Event, this);
.
.
}

void Viewer::mouse1Event(CPT_Event ev, void *data) {

  Viewer *v = (Viewer*)data;

  if(v->mMouseWatcher->has_mouse()){

    const Event *e = ev.p();
    const char *key = e->get_name().c_str();
.
.

Well, and I guess it’s still failing? In what way does it fail–does it simply never execute the callback function? Are you sure that you’re calling addMouseEvents()? Have you tried putting a cerr statement in the callback function to prove that it’s not getting called?

You shouldn’t have to cast your mouse1Event function to the appropriate type in the add_hook() call. It should work without casting. If it doesn’t, then something is probably wrong with the way you have declared your function.

Note in particular that EventHandler::EventCallbackFunction is defined with the prototype (const Event *, void *), and not (CPT_Event, void). So your function is indeed declared incorrectly.

David