Qt/C++ による GUI ソフト 4:3D レンダリングウィジェットの作成

ここで使っている Qt3DExtras::Qt3DWindow は非推奨なので,代替法が分かり次第改訂します

このページでは,3D レンダリングが可能なメインウィンドウを作成します.

まず,Qt3DExtras::Qt3DWindow という Qt 3D レンダリング用クラスのインスタンスを作成して表示するところから始めます.
ちょっと紛らわしいのですが,Qt3DWindow は,QWidget やその派生クラスである QMainWindow などとは別物で,ウィンドウシステムを表現するクラスです.

次に,Qt3DExtras::Qt3DWindow から QWidget を作成して表示します.これには,QWidget::createWindowContainer() という関数を使います.
作成されたウィジェットは Qt3DWindow の機能を有するので,3D レンダリング可能なウィジェットということになります.
レイアウト管理の機能を利用すれば,このウィジェットを配置したアプリケーションお雛形が作成できます.

Before : 3:シグナルとスロット Next : 5:イベント処理

インフォメーション

Qt 3D に関する公式ドキュメント

レイアウト管理に関する公式ドキュメント

"Layout Management" には,"Intruduction" の次に "Qt's Layout Classes" というパラグラフがあって,そこには基底クラスである QLayout とその継承クラスである各種レイアウト計 16 種が列挙されています.次のパラグラフが "Horizontal, Vertical, Grid, and Form Layouts" です.orizontal,Vertical … と学んでいくべきなのでしょう.

インストールした Debian パッケージ

このページは Debian 13 trixie で作成しています.

パッケージ名(バージョン)
g++(14.2.0)GNU C++ コンパイラ
make(4.4.1)コンパイルを制御するユーティリティ
qt6-base-dev(6.8.2)Qt6 アプリケーションのビルドに使用されるヘッダ開発ファイル
qt6-3d-dev(6.8.2)Qt 3D ライブラリを使用した Qt6 アプリケーションのビルドに使用されるヘッダ開発ファイル

目次(ページ内リンク)


Qt3DWindow の作成

Qt3DExtras::Qt3DWindow は QWindow を継承したクラスです.QWindow はウィンドウシステム上のウィンドウそのものを表現するクラスなので,Qt 3D のレンダリングターゲットは,ウィンドウシステム上のウィンドウと解釈できそうです.
まず 3D レンダリングの場となる,Qt3DWindow を作成して表示する,ごく簡素なプログラムを作成します.

ソースコード

Qt 3D Examples に 2 個ある C++ のコードのうち,Qt 3D: Simple C++ Example から抜粋して作成しました.
メタタグのタイトルは "Qt 3D: Simple C++ Example | Qt 3D | Qt 6.10.1" となっているので,最新の Qt でも推奨されるコーディングなのでしょう.
このコードはまた,1:メインウィンドウで最初に作成した QWidget を作成して表示するプログラムの,QWidget を Qt3DWindow に置き換えただけともみなせます.

main.cc

Qt 3D Extras を使う場合のインクルード文は,Qt3DExtras Namespace の最初に書いてあります.


#include <QApplication>
#include <Qt3DExtras>

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

 Qt3DExtras::Qt3DWindow view;
 view.resize(320, 240);  //ウィンドウサイズの設定.QWindow の関数
 view.show();

 return app.exec();
}

tmp.pro

qmake でビルドするために 1 行追加しています.これも,Qt3DExtras Namespace の最初に書いてあります.


TEMPLATE = app
TARGET = my_window
INCLUDEPATH += .
QT += widgets
QT += 3dextras
SOURCES += main.cc

ビルドと実行

Qt3DExtras::Qt3DWindow インスタンス

手順は1:メインウィンドウと同じです.ソースファイルが tmp というディテクトリにあるとします.
~/tmp$ qmake6
~/tmp$ make
~/tmp$ ./my_window &

画像に示すウィンドウが表示されました.


Qt3DWindow から QWidget の作成

Qt3DWindow はウィジェットではありません.QLayout を使ったレイアウト管理ができないので,そのままではメインウィンドウに配置したりできません.
QWidget には,QWindow から QWidget(ウィンドウコンテナ)を生成する createWindowContainer() という関数があるので,これを利用します.
関数宣言を書き写せば,
QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())
解説から引用すると,

createWindowContainer() は QWindow の継承クラスであっても QWidget を作成できるようです.

ソースコード

Qt3DWindow を埋め込んだコンテナウィジェットを生成するプログラムを作成してみました.
Qt3DWindow と QWidget はポインタにしています.


#include <QApplication>
#include <Qt3DExtras>
#include <QWidget>

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

 Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow();
 QWidget *widget = QWidget::createWindowContainer(view);
 widget->resize(320, 240);  //ウィンドウサイズの設定.QWindow の関数
 widget->setWindowTitle("My Widget");  //ウィンドウタイトルの設定.QWidget の関数
 widget->show();

 return app.exec();
}

tmp.pro

上と同じなので省略します.

ビルドと実行

Qt3DExtras::Qt3DWindow を埋め込んだ QWidget

手順は上と同じです.

~/tmp$ qmake6
~/tmp$ make
~/tmp$ ./my_window &

画像に示すウィンドウが表示されました.
ウィンドウタイトルが変更されているのが見て取れます.


Qt3DWindow から 作成した QWidget の埋め込み

上のセクションでは Qt3DExtras::Qt3DWindow を埋め込んだ QWidget を main() 関数で生成していました.
これを Main_Window クラスのコンストラクタに移動し,生成した QWidget を QMainWindow に埋め込みます.
3:シグナルとスロットで作成したコードを改変して作成しました.

ソースコード

main_window.h


#include <QMainWindow>
#include <QMenuBar>

class Main_Window : public QMainWindow {
 private:
  QMenuBar *menu_bar;
  QMenu *file_menu;
  QMenu *view_menu;  QAction *action_quit;

 public:
  Main_Window(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
};

main_window.cc

作成したレンダリングウィジェットをメインウィンドウに配置するのに,QMainWindow::setCentralWidget() という関数を使っています.

QMainWindow Class の Qt Main Window Framework にセントラルウィジェットの説明があります.「QMainWindow には独自のレイアウトがあり,QToolBars,QDockWidgets,QMenuBar,QStatusBarを追加できます.レイアウトには中央領域があり,任意の種類のウィジェットを配置できます」.この中央領域に配置されるウィジェットをセントラルうジェットと称します.


#include "main_window.h"
#include <Qt3DExtras>

Main_Window::Main_Window(QWidget *parent, Qt::WindowFlags f)
 : QMainWindow(parent, f),
   menu_bar(menuBar())
{
 resize(320, 240);

 file_menu = new QMenu("ファイル(&F)", this);
 menu_bar->addMenu(file_menu);

 action_quit = new QAction("終了(&Q)", this);
 file_menu->addAction(action_quit);

 connect(action_quit, SIGNAL(triggered()), this, SLOT(close()));

 Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow();
 QWidget *widget = QWidget::createWindowContainer(view);
 setCentralWidget(widget);
}

main.cc


#include "main_window.h"
#include <QApplication>

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

 Main_Window window(nullptr, Qt::Window);
 window.show();

 return app.exec();
}

tmp.pro


TEMPLATE = app
TARGET = my_window
INCLUDEPATH += .
QT += widgets
QT += 3dextras
SOURCES += main.cc main_window.cc
HEADERS += main_window.h

ビルドと実行

Qt3DExtras::Qt3DWindow を埋め込んだ Main_Window

手順は上と同じです.
~/tmp$ qmake6
~/tmp$ make
~/tmp$ ./my_window &

画像は実行時のイメージです.かなり,アプリケーションらしい見た目になりました.
ただこのコードでは,生成したウィジェットをそのままセントラルウィジェットとしているので,レイアウト管理ができないといった課題が残っています.


ウィンドウアプリケーションの雛形

ソースコード

これまでに,メインウィンドウメニューバーグナルとスロット,レンダリングウィジェットの作成を練習しました.
イアウト管理を利用してレンダリングウィジェットをメインウィンドウに配置すれば,アプリケーションの雛形が作成できそうです.

draw_window.h

Qt3DExtras::Qt3DWindow の継承クラスを作成します.このクラスで,マウスイベントの処理やレンダリングの機能などを追加していく構想です.


#ifndef ___DRAW_WINDOW
#define ___DRAW_WINDOW

#include <Qt3DExtras>

class Draw_Window : public Qt3DExtras::Qt3DWindow {
 public:
  Draw_Window() {}
};

#endif

draw_window.cc

まだ何も機能がないので,インクルード文のみです.


#include "draw_window.h"

main_window.h


#include "draw_window.h"
#include <QMainWindow>
#include <QMenuBar>

class Main_Window : public QMainWindow {
 private:
  QMenuBar *menu_bar;
  QMenu *file_menu;
  QMenu *view_menu;  QAction *action_quit;

  Draw_Window *DrawWindow;  //draw_window.h で宣言した Qt3DExtras::Qt3DWindow の継承クラス
  QWidget *DrawWidget;  //*DrawWindow 埋め込んだウィジェット.ここで宣言する必要があるかどうか未定

 public:
  Main_Window(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
};

main_window.cc

QMainWindow Class の Qt Main Window Framework にあるセントラルウィジェットの説明を再び引用します.「QMainWindow には独自のレイアウトがあり,QToolBars,QDockWidgets,QMenuBar,QStatusBarを追加できます.レイアウトには中央領域があり,任意の種類のウィジェットを配置できます」.この中央領域に配置されるウィジェットをセントラルうジェットと称します.

セントラルウィジェット上に色々なウィジェットを配置する場合,レイアウト管理の機能を使います.オブジェクトとしては,QLayout Class またはその派生クラスです.
レイアウト管理はアプリケーションのルック・アンド・フィールを決定する要因です.冒頭に挙げた解説ぺージを再掲します.

ここでは,下のように実装しています.

  1. セントラルウィジェットとするべき QWidget *central_widget をインスタンス化
  2. QMainWindow::setCentralWidget() でセントラルウィジェットとする
  3. central_widget を親として QHBoxLayout *central_layout インスタンス化.後で QWidget::setLayout() を使って設定することも可能
  4. QHBokLayout::addWidget() でウィジェットを配置する.QHBokLayout を使うとウィジェットは横並びに配置される

#include "main_window.h"
#include "draw_window.h"
#include <QHBoxLayout>

Main_Window::Main_Window(QWidget *parent, Qt::WindowFlags f)
 : QMainWindow(parent, f),
   menu_bar(menuBar())
{
 resize(320, 240);

 file_menu = new QMenu("ファイル(&F)", this);
 menu_bar->addMenu(file_menu);

 action_quit = new QAction("終了(&Q)", this);
 file_menu->addAction(action_quit);

 connect(action_quit, SIGNAL(triggered()), this, SLOT(close()));

 //セントラルウィジェットを作成し,QHBoxLayout の使用を設定している
 QWidget *central_widget = new QWidget(this);
 setCentralWidget(central_widget);
 QHBoxLayout *central_layout = new QHBoxLayout(central_widget); 

 //レンダリング用のオブジェクトを生成し,レイアウトに追加
 DrawWindow = new Draw_Window;  //draw_window.h で宣言した Qt3DExtras::Qt3DWindow の継承クラスを生成
 DrawWidget = QWidget::createWindowContainer(DrawWindow);  //DrawWindow から QWidget を生成
 central_layout->addWidget(DrawWidget); 
}

main.cc


#include "main_window.h"
#include <QApplication>

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

 Main_Window window(nullptr, Qt::Window);
 window.show();

 return app.exec();
}

tmp.pro


TEMPLATE = app
TARGET = my_window
INCLUDEPATH += .
QT += widgets
QT += 3dextras
SOURCES += main.cc main_window.cc draw_window.cc
HEADERS += main_window.h draw_window.h

ビルドと実行

アプリケーションの雛形

手順は上と同じです.
~/tmp$ qmake6
~/tmp$ make
~/tmp$ ./my_window &

レイアウト管理を追加したら,レンダリングウィンドウ(白い領域)が少し小さくなりました.

課題として,イベントの処理とか 3D レンダリングなどが残っています.それらのページを作成しつつ,修正を加えていきます.
出来上がった雛形は,分子モデリングソフト Builcule の次期バージョンに利用する構想です.