V jazyce C++ si rozsáhlé knihovny sami vytvářejí informace o svých typech (a každá knihovna používá odlišné datové struktury pro typové informace)
Předvedeme si typové informace na příkladu třírozměrných objektů zobrazovaných pomocí knihovny Open Inventor nebo Coin3D
Vytvoříme scénu obsahující kužel, kouli a krychli.
Výsledek je vidět na obrázku na konci kapitoly spolu se stromem objektů.
void MainWindow::simpleScene (SoSeparator * top) { // cone SoSeparator * group1 = new SoSeparator; SoTransform * shift1 = new SoTransform; shift1->translation.setValue (-2.0, 0.0, 0.0); group1->addChild (shift1); SoMaterial * redMaterial = new SoMaterial; redMaterial->diffuseColor.setValue (1.0, 0.0, 0.0); group1->addChild (redMaterial); SoCone * cone = new SoCone (); group1->addChild (cone); top->addChild (group1); // sphere SoSeparator * group2 = new SoSeparator; SoMaterial * greenMaterial = new SoMaterial; greenMaterial->diffuseColor.setValue (0.0, 1.0, 0.0); group2->addChild (greenMaterial); SoSphere * sphere = new SoSphere (); group2->addChild (sphere); top->addChild (group2); // cube SoSeparator * group3 = new SoSeparator; SoTransform * shift3 = new SoTransform (); shift3->translation.setValue (1.6, 0.0, 1.6); shift3->scaleFactor.setValue (0.7, 0.7, 0.7); group3->addChild (shift3); SoMaterial * blueMaterial = new SoMaterial; blueMaterial->diffuseColor.setValue (0.0, 0.0, 1.0); group3->addChild (blueMaterial); SoCube * cube = new SoCube (); group3->addChild (cube); top->addChild (group3); }
Objekty naší grafické scény scény jsou odvozeny ze z třídy SoNode
Pro každou instanci SoNode vytvoříme branch typu TreeItem, která bude zobrazena ve stromy typu QTreeWidget na levé straně okna.
Třída TreeItem je rošířením QTreeWidgetItem o položku node typu SoNode
class TreeItem : public QTreeWidgetItem { public: SoNode * node; };
Z objektu typu SoNode získáme typové informace poskytované knihovnou OpenInventor
SoType type = node->getTypeId ();
Z typových informací získáme jméno skutečného typu objektu
type.getName().getString()
Zajímalo by nás, zda objekt typy SoNode není také odvozeného typu SoGroup, který umí skladovat podřízené prvky typu SoNode
Zda SoNode je také SoGroup můžeme zjistit z typových informací
if ( type.isDerivedFrom ( SoGroup::getClassTypeId() ) )
Také můžeme použít dynamic_cast a otestovat zda výsledek není nulový ukazatel
SoGroup * group = dynamic_cast <SoGroup *> (node);
Pokud je objekt typy SoGroup získáme jednotlivé prvky a opět je zobrazíme v Qt stromu
int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); }
Celá funkce displayBranch rekuzivně zobrazující strom objektů pro zadané node typu SoNode
void MainWindow::displayBranch (QTreeWidgetItem * target, SoNode * node) { TreeItem * branch = new TreeItem; branch->node = node; SoType type = node->getTypeId (); branch->setText (0, type.getName().getString()); if ( type.isDerivedFrom ( SoGroup::getClassTypeId() ) ) { SoGroup * group = dynamic_cast <SoGroup *> (node); int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); } } target->addChild (branch); }
Pokud “klikneme” na uzel ve stromu na levé straně okna, zobrazíme parametry grafického objektu v tabulce na pravé straně.
Pomocí dynamic_cast ověříme, že uzel stromu je našeho typy TreeItem a v něm nalezneme node typu SoNode
void MainWindow::on_tree_itemActivated (QTreeWidgetItem * param, int column) { TreeItem * item = dynamic_cast <TreeItem *> (param); if (item != nullptr) displayNode (item->node); }
Objekt typu SoNode lze přetypovat na nadřazenou třídu SoFieldContainer, která obsahuje SoFieldList se seznamem atributů.
Z jednotlivých atributů SoField získáme jméno a hodnotu, kterou zobrazíme v tabulce
SoField * field = list [i]; SbName name; obj->getFieldName (field, name); SbString value; field->get (value); displayLine (name.getString (), value.getString ());
Celá funkce displayNode zobrazující tabulku atributů
void MainWindow::displayNode (SoNode * node) { ui->prop->clear (); SoFieldContainer * obj = node; SoFieldList list; obj->getFields (list); int len = list.getLength (); // display fields for (int i = 0; i < len; i++) { SoField * field = list [i]; // retrieve field name SbName name; obj->getFieldName (field, name); // retrieve field value SbString value; field->get (value); displayLine (name.getString (), value.getString ()); } }
void MainWindow::displayLine (QString name, QString value) { QTreeWidgetItem * item = new QTreeWidgetItem (ui->prop); item->setText (0, name); item->setText (1, value); }
#ifndef QT_INVENTOR_H #define QT_INVENTOR_H #include <QMainWindow> #include <QTreeWidgetItem> #include <Inventor/nodes/SoSeparator.h> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow ( ); private: void displayBranch (QTreeWidgetItem * target, SoNode *node); void displayNode (SoNode *node); void displayLine (QString name, QString value); void simpleScene (SoSeparator * top); private slots: void on_tree_itemActivated(QTreeWidgetItem *item, int column); private: Ui::MainWindow *ui; }; #endif // QT_INVENTOR_H
#include "qt-inventor.h" #include "ui_qt-inventor.h" #include <Inventor/Qt/SoQt.h> #include <Inventor/Qt/viewers/SoQtExaminerViewer.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoCone.h> #include <Inventor/nodes/SoSphere.h> #include <Inventor/nodes/SoCylinder.h> #include <Inventor/nodes/SoTransform.h> #include <Inventor/nodes/SoMaterial.h> class TreeItem : public QTreeWidgetItem { public: SoNode * node; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->splitter->setStretchFactor (0, 1); ui->splitter->setStretchFactor (1, 5); ui->splitter->setStretchFactor (2, 1); ui->prop->setColumnCount (2); ui->prop->setHeaderLabels (QStringList () << "name" << "value"); SoQtExaminerViewer * examiner = new SoQtExaminerViewer (ui->widget); SoSeparator * root = new SoSeparator; simpleScene (root); // SoCone * cone = new SoCone; // root->addChild(cone); examiner->setSceneGraph(root); displayBranch (ui->tree->invisibleRootItem (), root); } MainWindow::~MainWindow() { delete ui; } void MainWindow::displayBranch (QTreeWidgetItem * target, SoNode * node) { TreeItem * branch = new TreeItem; branch->node = node; SoType type = node->getTypeId (); branch->setText (0, type.getName().getString()); if (type.isDerivedFrom (SoGroup::getClassTypeId ()) ) { SoGroup * group = dynamic_cast <SoGroup *> (node); int count = group->getNumChildren (); for (int i = 0; i < count; i++) { SoNode * sub_node = group->getChild (i); displayBranch (branch, sub_node); } } target->addChild (branch); } void MainWindow::displayLine (QString name, QString value) { QTreeWidgetItem * item = new QTreeWidgetItem (ui->prop); item->setText (0, name); item->setText (1, value); } void MainWindow::displayNode (SoNode * node) { ui->prop->clear (); SoFieldContainer * obj = node; SoFieldList list; obj->getFields (list); int len = list.getLength (); // display fields for (int i = 0; i < len; i++) { SoField * field = list [i]; // retrieve field name SbName name; obj->getFieldName (field, name); // retrieve field value SbString value; field->get (value); displayLine (name.getString (), value.getString ()); } } void MainWindow::on_tree_itemActivated (QTreeWidgetItem * param, int column) { TreeItem * item = dynamic_cast <TreeItem *> (param); if (item != nullptr) displayNode (item->node); } void MainWindow::simpleScene (SoSeparator * top) { // cone SoSeparator * group1 = new SoSeparator; SoTransform * shift1 = new SoTransform; shift1->translation.setValue (-2.0, 0.0, 0.0); group1->addChild (shift1); SoMaterial * redMaterial = new SoMaterial; redMaterial->diffuseColor.setValue (1.0, 0.0, 0.0); group1->addChild (redMaterial); SoCone * cone = new SoCone (); group1->addChild (cone); top->addChild (group1); // sphere SoSeparator * group2 = new SoSeparator; SoMaterial * greenMaterial = new SoMaterial; greenMaterial->diffuseColor.setValue (0.0, 1.0, 0.0); group2->addChild (greenMaterial); SoSphere * sphere = new SoSphere (); group2->addChild (sphere); top->addChild (group2); // cube SoSeparator * group3 = new SoSeparator; SoTransform * shift3 = new SoTransform (); shift3->translation.setValue (1.6, 0.0, 1.6); shift3->scaleFactor.setValue (0.7, 0.7, 0.7); group3->addChild (shift3); SoMaterial * blueMaterial = new SoMaterial; blueMaterial->diffuseColor.setValue (0.0, 0.0, 1.0); group3->addChild (blueMaterial); SoCube * cube = new SoCube (); group3->addChild (cube); top->addChild (group3); } int main (int argc, char ** argv) { QApplication app (argc, argv); SoQt::init ((QWidget*)NULL); MainWindow * win = new MainWindow(); win->show(); QObject::connect (&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); SoQt::mainLoop(); return 0; }
Do .pro doplnit
CONFIG += link_pkgconfig PKGCONFIG += SoQt LIBS += -lSoQt