====== Typové informace v knihovně Open Inventor ======
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 [[https://cs.wikipedia.org/wiki/Open_Inventor|Open Inventor]]
nebo [[https://cs.wikipedia.org/wiki/Coin3D|Coin3D]]
===== Instalace a první pokus =====
[[coin|instalace a prvni pokus]]
===== Jednoduchá scéna =====
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ů.
* **group1** je větev stromu zobrazující červený kužel
* pod //group1// je **shift1**, posun o 2 jednotky doleva
* níže je **redMaterial**
* a pod ním je **cone** (který by sám byl šedý)
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);
}
===== Strom zobrazující strukturu grafických objektů =====
Objekty naší grafické scény scény jsou odvozeny ze z třídy [[https://coin3d.github.io/Coin/html/classSoNode.html|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 [[https://coin3d.github.io/Coin/html/classSoGroup.html|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 (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 (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);
}
===== Zobrazení atributů grafického objektu na základě typových informací =====
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 (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);
}
===== Hlavičkový soubor =====
#ifndef QT_INVENTOR_H
#define QT_INVENTOR_H
#include
#include
#include
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
===== Program =====
#include "qt-inventor.h"
#include "ui_qt-inventor.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 (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 (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
{{qtinventor.png}}
[[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/tree/master/QtInventor|gitlab]]
[[http://kmlinux.fjfi.cvut.cz/~culikzde/uop/QtInventor2020.zip|zip]]