Chapter Overview
| >General >QTDesigner >Compiling >Passive Window with QT / Plain OpenGL Code |
Throughout the tutorials we used a GLUT Window for rendering, but as I mentioned earlier, there are some more options. QT (from Trolltech - www.trolltech.com) is a very common and powerful cross platform tool for rapid GUI development. This appendix will show you how to create a simple application that is capable to render OpenSG content, though this will not (and can not) be an introduction to QT development. If you are new to QT, have a look at their website where you can find some tutorials dealing with QT stuff only. Additionally the last section will deal with a passive window that is integrated in an QT environment.
The application we will create
I will show you step by step what you have to do, but I also assume that you have some basic knowledge of QT. However, don't worry if you have not - this one was my very first QT application ;-)
In addition to that, you need to compile OpenSG with QT support (Windows binaries have it already compiled in). Here is an example of the configure command
./configure --enable-glut --enable-tif --enable-png --enable-jpg --enable-qt --with-qt=/usr/qt/3/
The configure script might be able to find the location of qt itself, if it does not you have to add the last parameter.
Note: Your path to QT might be different. If you compiled it yourself, it is possibly /usr/local/qt/3/
The rest of the process is just the same as usual. You are now ready to create QT powered OpenSG applications... or OpenSG powered QT applications ;-)
Designer window after startup
As we are not going to develop a complex application with multiple windows, we need not to create a project. Just select the "Dialog" icon, as seen above, and click on "OK".
You see now an empty window in the middle of the screen titled with "Form1", which is actually your application. On the right hand side you have two toolbox windows named "Object Explorer" and "Property Editor/Signal Handlers". The latter will be used to set attributes of the selected object, like the size or name of the window. On the left hand side is the "Toolbox" where you can select different GUI elements to place in your window.
Creating a new custom widget
Click on the "New Widget" button and edit the attributes found on the right hand side. Make sure the "Definition" tab is selected. Enter the following
Class : OSGQGLManagedDesignerWidget
Headerfile : OpenSG/OSGQGLManagedDesignerWidget_qt.h
Dropdownfield : Global (instead of local)
All other options should be left as they are. Please make sure that everything is spelled correctly, else your application will not compile correctly.
The QT window
Change the name of the OSGQGLManagedDesignerWidget to "osgWidget". Finally select the window itself and change the name from "Form1" to "MainWindow" as well as the "caption" property from "Form1" to "OpenSG Viewer".
Edit Functions window
Click on "OK" to close that window. We need to connect the parts with each other. Press F3 on your keyboard and click on the load button. In the new window click on "No Signal" below Signal and select clicked(). Set Receiver to "MainWindow" and finally set slot to load() - click OK.
Now click on the load() slot, found beneath the public specifier. That will open a new window, where you find the body of the load() function. Here we can add code to perform the actions we want it to do. Replace the contents of the file with the following code - of course you can leave out the comments...
#include <qfiledialog.h> #include <OpenSG/OSGSceneFileHandler.h> #include <OpenSG/OSGSimpleGeometry.h> void MainWindow::load() { // this command will open a file select dialog, allowing wrl, osg, and bin file types. The selected // filename along with it's path will be stored in 's' QString s(QFileDialog::getOpenFileName(QString::null, "Szenenformate (*.wrl *.osg *.bin)")); if (!s.isEmpty()) { char *filename; //create a correct filename filename = (char*)s.latin1(); NodePtr n = SceneFileHandler::the().read(filename); //load that file - if it fails for some reason, we crate a torus if (n != NullFC) osgWidget->getManager().setRoot(n); else osgWidget->getManager().setRoot(makeTorus(0.5,2,16,16)); } }
Now it is about time to save everything. Select "File > Save All". I advise you to create a new folder - let us name it OpenSGViewer. QT suggests the filename "mainwindow.ui", which is alright for us. Just click "OK" after you have switched to the new folder. If you have a look in that folder you will see that there are two files now: "mainwindow.ui" and "mainwindow.ui.h". The latter contains exactly what we have written in the editor, where as the first one is created automatically by QT and describes the GUI.
#include <qapplication.h> #include <OpenSG/OSGConfig.h> #include <OpenSG/OSGNode.h> #include "mainwindow.h" OSG_USING_NAMESPACE; int main(int argc, char **argv) { osgInit(argc, argv); QApplication app (argc, argv); MainWindow main; app.setMainWidget(&main); main.show(); int result = app.exec(); return result; }
Actually this is quite simple. At the very beginning we initialize OpenSG (as usual) and QT. Afterwards we create an instance of our own widget, set it as the applications main window and execute it.
This will generate the header file
uic -o mainwindow.h mainwindow.uiand this the cpp file
uic -i mainwindow.h -o mainwindow.cpp mainwindow.ui
Now invoke the next command
moc -o moc_mainwindow.cpp mainwindow.hand finally the "usual" compiler command
g++ -D_GNU_SOURCE -DQT_CLEAN_NAMESPACE -DOSG_WITH_GLUT -DOSG_WITH_QT -DOSG_WITH_TIF -DOSG_WITH_PNG -DOSG_WITH_JPG -I$QTDIR/include -I/usr/local/include -ansi -f-template-depth-100 mainwindow.cpp main.cpp moc_mainwindow.cpp -L/usr/local/lib/dbg -L/usr/X11R6/lib -L$QTDIR/lib -lOSGWindowGLUT -lOSGWindowQT -lOSGSystem -lOSGBase -lGLU -lGL -lglut -lqt -lXmu -lXi -lXt -lpthread -ldl -o OpenSGViewer
Yes, really that is one single command! However, you might have to adjust some things. First, if you build the optimized OpenSG library you have to use -L/usr/local/lib/opt instead of -L/usr/local/lib/dbg. Also you have to make sure $QTDIR is defined correctly. If not you can also specify the full path here. I hope your terminal supports copy and paste ;-)
After compiling you can execute the application with
./OpenSGViewer
Click on the load button and select any valid file - if you have no such files on your own, take one from the tutorial/progs/data folder. Maybe you have to move the camera to see anything, depending on the file you loaded.
You can find all source files in the tutorial/progs/qt folder!
Here you go!
If you read the online tutorial right from the beginning, you have seen a lot of different problems and solutions and you have rendered a few million polygons with OpenGL so far - but you have never ever used a single OpenGL command, like Vertex3f(....) or the like. Well, that actually should not bother us, but what if you want or even have to insert plain OpenGL code for some reason? Using the tools you know so far this would not be possible, but as you might expect, this will be possible by using a passive window. This, too, I will show you in the upcoming tutorial.
This time we don't need the designer (although you can, if you want to design a more advanced GUI), but we will have a window class derived from QGLWidget. Please do not mistake this with OSGQGLManagedDesignerWidget we used it the previous application. The QGLWidget has really nothing to do with OpenSG and thus does not support any OpenSG functionality on it's own. As the name already says, it offers support for OpenGL.
Let us have a look at the code first, here is the header file OpenSGWidget.h, found in progs/qt/OpenSGWidget.h
#include <qgl.h> #include <qapplication.h> #include <OpenSG/OSGConfig.h> #include <OpenSG/OSGSceneFileHandler.h> #include <OpenSG/OSGSimpleGeometry.h> #include <OpenSG/OSGPassiveWindow.h> #include <OpenSG/OSGSimpleSceneManager.h> OSG_USING_NAMESPACE // our widget class will be derived from QGLWidget, // a widget offering support for plain OpenGL class OpenSGWidget : public QGLWidget{ public: OpenSGWidget( QGLFormat f, QWidget *parent=0, const char *name=0 ); SimpleSceneManager *getManager(void); protected: // these replace the callback functions // you know from the other tutorials void initializeGL(); void resizeGL(int, int); void paintGL(); void mousePressEvent(QMouseEvent *ev); void mouseMoveEvent(QMouseEvent *ev); void mouseReleaseEvent(QMouseEvent *ev); void wheelEvent(QWheelEvent *ev); SimpleSceneManager *mMgr; // no GLUT window this time, but a passive one PassiveWindowPtr mPwin; };
Next the coressponding .cpp file
#include "OpenSGWidget.h" //constructor OpenSGWidget::OpenSGWidget(QGLFormat f, QWidget *parent, const char *name ) : QGLWidget( f, parent, name ){ mMgr = new SimpleSceneManager; mPwin = PassiveWindow::create(); mMgr->setWindow(mPwin); } SimpleSceneManager *OpenSGWidget::getManager(void){ return mMgr; } void OpenSGWidget::initializeGL(){ mPwin->init(); } void OpenSGWidget::resizeGL(int width, int height){ mMgr->resize(width,height); } // this replaces the display function void OpenSGWidget::paintGL(){ mMgr->redraw(); } void OpenSGWidget::mousePressEvent(QMouseEvent *ev){ UInt32 button; // translate the button value from qt to OpenSG switch (ev->button()){ case LeftButton: button = SimpleSceneManager::MouseLeft; break; case MidButton: button = SimpleSceneManager::MouseMiddle; break; case RightButton: button = SimpleSceneManager::MouseRight; break; default: return; } //passs the event to the manager object mMgr->mouseButtonPress(button, ev->x(), ev->y()); update(); } void OpenSGWidget::mouseReleaseEvent(QMouseEvent *ev){ UInt32 button; switch (ev->button()){ case LeftButton: button = SimpleSceneManager::MouseLeft; break; case MidButton: button = SimpleSceneManager::MouseMiddle; break; case RightButton: button = SimpleSceneManager::MouseRight; break; default: return; } mMgr->mouseButtonRelease(button, ev->x(), ev->y()); update(); } void OpenSGWidget::mouseMoveEvent(QMouseEvent *ev){ mMgr->mouseMove(ev->x(), ev->y()); update(); } void OpenSGWidget::wheelEvent(QWheelEvent *ev){ mMgr->mouseButtonPress(ev->delta() > 0 ? SimpleSceneManager::MouseUp : SimpleSceneManager::MouseDown, ev->x(), ev->y()); ev->accept(); update(); } int main( int argc, char **argv ){ osgInit(argc,argv); //fire up the widget QApplication application(argc, argv); OpenSGWidget widget(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering)); NodePtr scene; //Load stuff from the command line, //if present if(argc > 1){ scene = Node::create(); GroupPtr g = Group::create(); beginEditCP(scene); scene->setCore(g); for(UInt16 i = 1; i < argc; ++i) scene->addChild(SceneFileHandler::the().read(argv[i])); endEditCP(scene); }else{ scene = makeTorus(.5, 3, 16, 16); } widget.getManager()->setRoot(scene); widget.getManager()->showAll(); application.setMainWidget( &widget ); widget.show(); // replaces the glutMainLoop() // giving control to the QT Main Loop return application.exec(); }
This time you won't need to call the 'uic' and 'moc' commands like in the last example, because we have not used the designer to create an interface. However hacking the long OpenSG parameters and other attributes into the terminal is quite time robbing, so I offer you a Makefile which also can be taken for your future projects.
# This Makefile will compile the OpenSGWidget application (2nd tutorial in qt.dox) # The first tutorial has to be compiled by hand (see qt.dox) # define the kind of library you have installed ('dbg' or 'opt') LIBTYPE = dbg C++ = g++ CCFLAGS = -D_GNU_SOURCE -DQT_CLEAN_NAMESPACE -DOSG_WITH_GLUT -DOSG_WITH_QT -DOSG_WITH_JPG -DOSG_WITH_PNG -D_OSG_HAVE_CONFIGURED_H_ -DQT_NO_XINERAMA -DQT_NO_XRENDER -DQT_NO_XFTFREERYPE -D_QT_NO_XKB -DQT_NO_SM_SUPPORT -DQT_NO_IMAGEIO_MNG -DQT_NO_IMAGEIO_JPEG -DQT_NO_STYLE_AQUA -DQT_NO_STYE_MAC -DQT_NO_STYLE_INTERLACE -DQT_NO_STYLE_COMPACT -use_readonly_const -ftemplate-depth-100 -Wno-deprecated -pipe -funroll-loops LDFLAGS = -L/usr/local/lib/$(LIBTYPE) -L/usr/qt/3/lib -L/usr/X11R6/lib -Wall LIBS = -lOSGSystem -lOSGBase -lglut -lGLU -lGL -lXmu -lXi -lXt -lqt -lpthread -ldl DEBUG = -c -O3 INCLUDE = /usr/qt/3/include SOURCES = OpenSGWidget.cpp OBJECTS = $(SOURCES:.cpp=.o) EXECUTABLE = OSGWidget all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(C++) $(LDFLAGS) $(LIBS) $(OBJECTS) -o $@ .cpp.o: $(C++) $(CCFLAGS) -I$(INCLUDE) $(DEBUG) $< -o $@ clean: rm -rf *.o OSGWidget
If you want to use it for your own projects you simply have to add your source files to the line that says "SOURCES = ....". Additionally it might increase performance if you add some platform specific parameters to the CCFLAGS, like "-march=athlon" or the like.
So if you have saved all three file in the same folder (or just use the premade ones) then fire up a terminal cd to the directory and call "make". After make finishes type "./OSGWidget" which should show you a window with a torus (like always ;-) ). You can add any number of files as parameters to the command line call which will then be loaded.
So far so good, know we want to add some plain OpenGL code. Well, if you are using a passive window this is really very easy, as you only need to add it to the paintGL() method. So please replace your painGL() method with the following code
void OpenSGWidget::paintGL(){
mMgr->redraw();
glBegin(GL_QUADS);
glColor3f(1,0,0);
glVertex3f(2.5,2.5,0);
glVertex3f(-2.5,2.5,0);
glVertex3f(-2.5,-2.5,0);
glVertex3f(2.5,-2.5,0);
glEnd();
}
Compile the application and run it again - you now should see the torus (or whichever files you have loaded) and a red quad, which was defined by "real" and "raw" OpenGL commands. In this way it is possible to drive an application with OpenGL graphics that does not use the scenegraph at all - although I can't imagine what that should be good for anyway ;-)
A Passive Window with QT and a Raw OpenGL Drawn Quad
Next Chapter : Appendix C - More Documentation and Tools
1.4.3