Qt/C++ による GUI ソフト 4:3D レンダリングウィジェットの作成
ここで使っている Qt3DExtras::Qt3DWindow は非推奨なので,代替法が分かり次第改訂します
このページでは,3D レンダリングが可能なメインウィンドウを作成します.
まず,Qt3DExtras::Qt3DWindow という Qt 3D レンダリング用クラスのインスタンスを作成して表示するところから始めます.
ちょっと紛らわしいのですが,Qt3DWindow は,QWidget やその派生クラスである QMainWindow などとは別物で,ウィンドウシステムを表現するクラスです.
次に,Qt3DExtras::Qt3DWindow から QWidget を作成して表示します.これには,QWidget::createWindowContainer() という関数を使います.
作成されたウィジェットは Qt3DWindow の機能を有するので,3D レンダリング可能なウィジェットということになります.
レイアウト管理の機能を利用すれば,このウィジェットを配置したアプリケーションお雛形が作成できます.
インフォメーション
Qt 3D に関する公式ドキュメント
- Qt 3D:Qt 3D に関するポータル的なページ
- Qt 3D Overview
- Qt 3D C++ Classes
- Qt 3D Examples
- QWindow Class:Qt3DExtras::Qt3DWindow の基底クラス(ドキュメントを示せないので不確か)
レイアウト管理に関する公式ドキュメント
"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
ビルドと実行
手順は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())
解説から引用すると,
- QWidget が作成されるので,QWidget ベースのアプリケーションに QWindow を埋め込むことが可能になる
- ウィンドウコンテナは,親ウィジェットやウィンドウフラグを設定することも可能
- ウィンドウがコンテナに埋め込まれると,コンテナがウィンドウのジオメトリと可視性を制御する
- 埋め込まれたウィンドウに対して QWindow::setGeometry(),QWindow::show(),または QWindow::hide() を明示的に呼び出すことは推奨されない
- コンテナはウィンドウの所有権を引き継ぐ
- ウィンドウは,QWindow::setParent() を呼び出すことでウィンドウコンテナから削除できる
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
上と同じなので省略します.
ビルドと実行
手順は上と同じです.
~/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
ビルドと実行
手順は上と同じです.
~/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 またはその派生クラスです.
レイアウト管理はアプリケーションのルック・アンド・フィールを決定する要因です.冒頭に挙げた解説ぺージを再掲します.
ここでは,下のように実装しています.
- セントラルウィジェットとするべき QWidget *central_widget をインスタンス化
- QMainWindow::setCentralWidget() でセントラルウィジェットとする
- central_widget を親として QHBoxLayout *central_layout インスタンス化.後で QWidget::setLayout() を使って設定することも可能
- 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 の次期バージョンに利用する構想です.