Qt/C++ による GUI ソフトウェア作成の練習 6:ピッキング
このページは,Qt3D で描画したオブジェクトをピッキング(マウスで描画オブジェクトを選択)する手法を勉強したときのまとめです.
QObjectPicker クラスのインスタンスを QEntity のコンポーネントとして追加するだけで,その QEntity はピッキングできるようになります.
ここでは,QEntity のインスタンスを識別番号で区別するための方法を試してみました.
これを当サイトでは,分子モデルを処理する際に原子を区別するための手段として使います.
インフォメーション
インストールした Debian パッケージ
パッケージ名(バージョン) | 注 |
g++(12.2.0) | GNU C++ コンパイラ |
make (4.3) | コンパイルを制御するユーティリティ |
qt6-base-dev(6.4.2) | Qt6 アプリケーションのビルドに使用されるヘッダ開発ファイル |
qt6-3d-dev(6.4.2) | Qt3D ライブラリを使用した Qt6 アプリケーションのビルドに使用されるヘッダ開発ファイル |
ドキュメント
Qt3D による描画で紹介したリンクに,QObjectPicker クラスを追加しておきます.
Qt 3D Overview | Qt 3D 6.5.2
QEntity
Component
Qt 3D Examples | Qt 3D 6.5.2
QObjectPicker Class
QObjectPicker Class のページに記されている注意事項
- QObjectPicker クラスのインスタンスは共有してはならない.これを守らないと未定義の動作が起こるかもしれない
- カメラの遠方平面の値はピッキングに影響する.およそ 100000 より大きくなると,浮動小数点の精度により不正確な結果となる
ソースコード
Qt3D による描画で作成したコードをわずかに変更して雛形としました.
このページで追加した箇所は赤色で示しています.
draw_obj.h
#include <Qt3DCore>
#include <Qt3DExtras>
#include <Qt3DRender>
class Draw_Obj : public QObject {
private:
Qt3DExtras::Qt3DWindow *DrawWindow;
Qt3DCore::QEntity *RootEntity;
Qt3DCore::QEntity *Sphere;
bool Picked; //ピック済みかどうかを示すフラグ
private slots:
void slot_clicked(int i); //このコードでは球をクリックしたときに呼び出される,引数付きのスロット関数
public:
Draw_Obj();
~Draw_Obj() {}
Qt3DExtras::Qt3DWindow *get_draw_window()
{ return DrawWindow; }
void init();
void add_obj();
};
draw_obj.cc
#include "draw_obj.h"
#include <iostream>
Draw_Obj::Draw_Obj()
: DrawWindow(nullptr),
RootEntity(nullptr),
Sphere(nullptr),
Picked(false) //最初はピックされていない
{
RootEntity = new Qt3DCore::QEntity();
DrawWindow = new Qt3DExtras::Qt3DWindow();
DrawWindow->setRootEntity(RootEntity);
}
void Draw_Obj::init() {
if(Sphere) {
delete Sphere;
Sphere = nullptr;
}
if(RootEntity) {
delete RootEntity;
RootEntity = new Qt3DCore::QEntity();
}
if(DrawWindow) {
delete DrawWindow;
DrawWindow = new Qt3DExtras::Qt3DWindow();
}
DrawWindow->setRootEntity(RootEntity);
Qt3DRender::QCamera *CameraEntity
= DrawWindow->camera();
CameraEntity->lens()->setPerspectiveProjection(
45.0f, 1.0f, 10.f, 30.0f);
CameraEntity->setPosition(QVector3D(0, 0, 20.0f));
CameraEntity->setUpVector(QVector3D(0, 1, 0));
CameraEntity->setViewCenter(QVector3D(0, 0, 0));
Qt3DCore::QEntity *light_entity
= new Qt3DCore::QEntity(RootEntity);
Qt3DRender::QPointLight *light
= new Qt3DRender::QPointLight(light_entity);
light->setColor("white");
light->setIntensity(1);
light_entity->addComponent(light);
Qt3DCore::QTransform *light_pos
= new Qt3DCore::QTransform(light_entity);
light_pos->setTranslation(
CameraEntity->position());
light_entity->addComponent(light_pos);
}
void Draw_Obj::add_obj() {
if(Sphere) return;
Sphere = new Qt3DCore::QEntity(RootEntity);
Qt3DExtras::QSphereMesh *mesh
= new Qt3DExtras::QSphereMesh();
mesh->setRings(20);
mesh->setSlices(20);
mesh->setRadius(2.5);
Sphere->addComponent(mesh);
Qt3DExtras::QPhongMaterial *material
= new Qt3DExtras::QPhongMaterial();
material->setDiffuse(QColor(QRgb(0xFF0000)));
Sphere->addComponent(material);
Qt3DCore::QTransform *transform
= new Qt3DCore::QTransform();
transform->setTranslation(
QVector3D(1.0f, 1.0f, 1.0f));
Sphere->addComponent(transform);
//ピッカーを Sphere のコンポーネントとして,識別番号を付けて作成する
int id = 123; //識別番号.ここでは値はてきとうでよい
Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker(Sphere);
connect(
picker,
&Qt3DRender::QObjectPicker::clicked,
[this, id]() { slot_clicked(id); } ); //引数付きのスロット関数と接続するときは,ラムダ式を使えばよい
Sphere->addComponent(picker);
}
//connect() を使って接続したので, 上で作成した Sphere がクリックされるとこの関数が呼び出される.引数は識別番号
void Draw_Obj::slot_clicked(int i) {
//確認のため,識別番号を出力
std::cout << "id : " << i << std::endl;
//ピックされたら色を変える.これだと,ピックのたびにコンポーネントが増える一方のような気がする.何かうまい方法があるのかもしれない
Qt3DExtras::QPhongMaterial *material
= new Qt3DExtras::QPhongMaterial();
if(!Picked)
material->setDiffuse(QColor(QRgb(0x0000FF)));
else
material->setDiffuse(QColor(QRgb(0xFF0000)));
Sphere->addComponent(material);
//フラグを反転
Picked = !Picked;
}
main_window.h
#include <QMainWindow>
#include <QMenuBar>
#include <QHBoxLayout>
#include "draw_obj.h"
class Main_Window : public QMainWindow {
private:
Q_OBJECT
QMenuBar *MenuBar;
QHBoxLayout *CentralLayout;
QWidget *Container;
Draw_Obj DrawObj;
Qt3DExtras::Qt3DWindow *DrawWindow;
private slots:
void slot_draw();
void slot_clear() { DrawObj.init(); }
public:
Main_Window(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
};
main_window.cc
#include "main_window.h"
Main_Window::Main_Window(QWidget *parent, Qt::WindowFlags f)
: QMainWindow(parent, f),
MenuBar(menuBar()),
CentralLayout(nullptr),
Container(nullptr),
DrawWindow(nullptr)
{
QAction *action_quit = new QAction("終了(&Q)", this);
connect(action_quit, SIGNAL(triggered()), this, SLOT(close()));
QMenu *file_menu = new QMenu("ファイル(&F)", this);
file_menu->addAction(action_quit);
MenuBar->addMenu(file_menu);
QAction *action_draw
= new QAction(tr("描画(&D)"), this);
connect(action_draw, SIGNAL(triggered()), this,
SLOT(slot_draw()));
QAction *action_clear
= new QAction(tr("クリア(&C)"), this);
connect(action_clear, SIGNAL(triggered()), this,
SLOT(slot_clear()));
QMenu *view_menu = new QMenu(tr("表示(&V)"), this);
view_menu->addAction(action_draw);
view_menu->addAction(action_clear);
MenuBar->addMenu(view_menu);
QWidget *central_widget = new QWidget(this);
setCentralWidget(central_widget);
CentralLayout = new QHBoxLayout(central_widget);
DrawWindow = DrawObj.get_draw_window();
Container = createWindowContainer(DrawWindow);
CentralLayout->addWidget(Container);
QSize screenSize = screen()->size();
setMaximumSize(screenSize);
setMinimumWidth(600);
setMinimumHeight(600);
}
void Main_Window::slot_draw() {
DrawObj.init();
CentralLayout->removeWidget(Container);
delete Container;
DrawWindow = DrawObj.get_draw_window();
Container = createWindowContainer(DrawWindow);
CentralLayout->addWidget(Container);
DrawObj.add_obj();
}
main.cc
#include <QMainWindow>
#include <QApplication>
#include "main_window.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Main_Window window(nullptr, Qt::Window);
window.show();
return app.exec();
}
tmp.pro
qmake のプロジェクトファイルです.名前を変更しても問題ありません.
TEMPLATE = app
TARGET = window
QT += widgets
INCLUDEPATH += .
HEADERS += main_window.h draw_obj.h
SOURCES += main.cc main_window.cc draw_obj.cc
QT += 3dcore
QT += 3dextras
#QT += 3drender
#QT += 3dinput
ビルドと実行
![Qt3D で球を描画](./qt_pick_01.png)
まず Makefile を作成.
~/tmp$ qmake6
次いでビルド.実行ファイル window が生成します.
~/tmp$ make
ビルドに成功したら実行.
~/tmp$ ./window &
とするとウィンドウが表示されます.
メニューで [表示(V)]-[描画(D)] とすると球が描画されます.
球をクリックするごと,球の色が赤→青→赤→青と変化します.
画像は,青に変化したときのキャプチャしたものです.
同時に端末に,ソースコード内で設定した識別番号が出力されます.
id : 123
参考書の検索
- Amazon の「コンピュータ・IT」本カテゴリーでの,「Qt」での検索結果です
- Amazon の「コンピュータ・IT」本カテゴリーでの,「C++」での検索結果です