Boost.Scopeにも実装されているらしい。
https://www.boost.org/doc/libs/1_85_0/libs/scope/doc/html/index.html
ただBoostの導入は面倒なので、今回は有志がGithubで公開しているものを使う。
https://github.com/okdshin/unique_resource
例えば、C++にはstd::unique_ptrがあり、メモリリークを防げる。この時、カスタムデリータを指定すれば、普通のnew/deleteとは異なった破棄処理を行うこともできる。
// ファイルをクローズするデリータ struct MyFileCloser { void operator()(FILE* ptr) const { fclose(ptr); } };
int main() { { // ファイルを開く std::unique_ptr<FILE, MyFileCloser> pfile(fopen("test", "r"), MyFileCloser()); char buffer[1024]; fgets(buffer, sizeof(buffer), pfile.get()); std::cout << buffer << std::endl; // ファイルはスコープを抜けると自動的にクローズされる } }
問題は管理したい参照がポインタでない場合で、unique_ptrではエラーになる。
#include <memory> #include <unordered_map> // リソースはハンドルでアクセス using HANDLE = int; // リソース管理用のコンテナ std::unordered_map<HANDLE, int*> myresource;
// リソースの確保 HANDLE MyNew(size_t size) { HANDLE handle = 0; myresource.insert({ handle,new int[size] }); return handle++; }
// リソースの解放 void MyDelete(HANDLE handle) { delete[] myresource[handle]; myresource.erase(handle); }
struct My_HANDLE_Release { void operator()(HANDLE handle) const { MyDelete(handle); } };
int main() { // エラー MyNew が返す HANDLE はポインタでない std::unique_ptr<HANDLE, My_HANDLE_Release> uptr(MyNew(100), My_HANDLE_Release()); }
以下から、unique_resource.hpp をダウンロード。
https://github.com/okdshin/unique_resource
#include "unique_resource.hpp"
int main() { HANDLE test; { auto unique_handle = std_experimental::make_unique_resource<HANDLE, My_HANDLE_Release>(MyNew(100), My_HANDLE_Release()); test = unique_handle.get(); // リソースを使う int* p = myresource.at(unique_handle.get()); std::cout << "access " << p << std::endl; for (size_t i = 0; i < 100; ++i) { p[i] = i; }
// リソースが解放される
}
// リソースが解放されているかチェック if( myresource.find(test) == myresource.end() ) { std::cout << "リソースは解放されています" << std::endl; } else { std::cout << "リソースは解放されていません" << std::endl; } }
window.addEventListener("DOMContentLoaded", () => { document.getElementById("MyNewButton").addEventListener("click",async (e) => { invoke("my_rust_messagebox"); }); });
Rust側では、tauriクレートのfeaturesでdialogを指定する。
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[tauri::command] fn my_rust_messagebox(window:tauri::Window){ tauri::api::dialog::message(Some(&window),"タイトル","メッセージ "); }
fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![my_rust_messagebox]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
以下の3ファイルが、GUIの編集とイベント処理に必要になる
index.htmlに、buttonを追加。IDをMyNewButtonとする。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="styles.css" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Tauri App</title> <script type="module" src="/main.js" defer></script> <style> .logo.vanilla:hover { filter: drop-shadow(0 0 2em #ffe21c); } </style> </head> <body> <div class="container"> <h1>Welcome to Tauri!</h1> <div class="row"> <a href="https://tauri.app" target="_blank"> <img src="/assets/tauri.svg" class="logo tauri" alt="Tauri logo" /> </a> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" > <img src="/assets/javascript.svg" class="logo vanilla" alt="JavaScript logo" /> </a> </div> <p>Click on the Tauri logo to learn more about the framework</p> <form class="row" id="greet-form"> <input id="greet-input" placeholder="Enter a name..." /> <button type="submit">Greet</button> </form> <p id="greet-msg"></p> <p></p> <button id="MyNewButton" type="button">新しいボタン</button> </div> </body> </html>
Rust側の関数 my_rust_function を呼び出すJavascriptを記述。myvalueは引数。
const { invoke } = window.__TAURI__.tauri; let greetInputEl; let greetMsgEl; async function greet() { // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command greetMsgEl.textContent = await invoke("greet", { name: greetInputEl.value }); } window.addEventListener("DOMContentLoaded", () => { greetInputEl = document.querySelector("#greet-input"); greetMsgEl = document.querySelector("#greet-msg"); document.querySelector("#greet-form").addEventListener("submit", (e) => { e.preventDefault(); greet(); });
document.getElementById("MyNewButton").addEventListener("click",async (e) => { invoke("my_rust_function", { myvalue: 55555 }); });
});
// Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) }
#[tauri::command] fn my_rust_function(myvalue: i32){ // カレントディレクトリへテキストファイルを作成 std::fs::write("my_rust_function.txt", format!("myvalue: {}", myvalue)).unwrap(); }
fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet]) .invoke_handler(tauri::generate_handler![my_rust_function]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
my_rust_function.txtが生成される。
RustでGUIを扱うよい方法を探している。
Node.jsは、クライアントPCで動くJavaScript実行環境。Node.jsの公式サイトからダウンロードする。LTS版とCurrent版があるがそれぞれ安全版と最新版程度の違いでしかないので好きなほうでいい。
例のごとく、私はmsiによるインストールを好まないので、Other Downloadsからzip版をダウンロードして導入して自分でパスを通した。
Tauri Command Line Interface。Tauri開発に必要なコマンドラインツール。
Rust環境は構築済みであることが前提なので、以下のコマンドでtauri-cliを導入する。
最初に、プロジェクトを生成するディレクトリへCDする。
以下のコマンドを叩くと、対話形式でのtauriプロジェクトの生成が始まる。
Project name プロジェクト名を入力 例:my1st_tauri
Choose which language to use for your frontend カーソル上下でRustを選択してEnter
Choose your UI template 今回はVanillaを選択。選択肢によってプロジェクト構成が違う。
まず作成したプロジェクトのディレクトリへ入る。
続いて、プロジェクトをビルド・実行
デバッグビルド
リリースビルド
ただし、これを実行すると、以下のラーが出る
このエラーはBundle.identifierという設定項目が初期値のままの時に発生する。
バンドルIDは、アプリケーション固有のIDで、何でもいいが他のTauriアプリケーションと重複しないIDを与える。一般的にはドメインを逆にしたものを与えるらしい。
src-tauri\tauri.conf.jsonを以下のように変更し、再度cargo tauri buildする。
Before
{ "build": { "devPath": "../src", "distDir": "../src", "withGlobalTauri": true }, "package": { "productName": "tauri-app", "version": "0.0.0" }, "tauri": { "allowlist": { "all": false, "shell": { "all": false, "open": true } }, "windows": [ { "title": "tauri-app", "width": 800, "height": 600 } ], "security": { "csp": null }, "bundle": { "active": true, "targets": "all", "identifier": "com.tauri.dev", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ] } } }
After
{ "build": { "devPath": "../src", "distDir": "../src", "withGlobalTauri": true }, "package": { "productName": "tauri-app", "version": "0.0.0" }, "tauri": { "allowlist": { "all": false, "shell": { "all": false, "open": true } }, "windows": [ { "title": "tauri-app", "width": 800, "height": 600 } ], "security": { "csp": null }, "bundle": { "active": true, "targets": "all", "identifier": "my.unique.identify", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ] } } }
なお、cargo tauri devでデバッグビルドと同時に実行した実行ファイルは、src-tauri\target\debug\ 内にある。しかしこの.exeを直接実行しようとすると、ウィンドウこそ開くが
127.0.0.1 により、接続が拒否されました。
プログラマーの魔法の使い方 Tauri デスクトップアプリ開発のはじめ方。
https://programwiz.org/2022/03/27/how-to-develop-desktop-app-with-tauri-framework/
VC++にGithub Copilotが統合され、Copilot Chatも使えるようになった。
自分の場合、元々Github Copilotの拡張機能を使っていたせいか、Copilotが動かなくなる現象に遭遇した。
既に使っていた場合は、拡張機能マネージャからGithub Copilotを削除する
再起動すれば標準機能でソースコード補完が動く。
Copilot Chatは [表示] → [GitHub Copilot チャット]で起動できる。
FLTKを使うと、設置したコントロール類がウィンドウサイズの変更と同時に移動・リサイズしてしまう。生成時の比率を維持するらしく使い勝手が悪いが、デフォルトでその機能を無効にする手段が見当たらなかったので、Event::Resize時にコントロールサイズを強制的に書き換えることで対処する。
use fltk::{app, prelude::*, window::Window, button::Button, enums::Event};
// 最初に指定したサイズ情報をボタンと一緒に扱うための構造体 struct WidgetWrap<T>{ x:i32, y:i32, w:i32, h:i32, btn:T, }
// WidgetWrapにボタンにメソッドを追加 impl<T:WidgetExt> WidgetWrap<T>{ /// @brief ウィジェットのサイズを含めてボタン管理 /// @param x_ x座標 /// @param y_ y座標 /// @param w_ 幅 /// @param h_ 高さ /// @param widget ウィジェットオブジェクト /// @return WidgetWrap<T> ウィジェット管理オブジェクト fn new(x_:i32,y_:i32,w_:i32,h_:i32,widget:T)->Self{ let mut _widget:WidgetWrap<T> = WidgetWrap{ x: x_, y: y_, w: w_, h: h_, btn:widget }; return _widget; } /// @brief ウィンドウサイズが変更されるときに呼び出し、ボタンの位置とサイズを強制的に維持する fn size_keep(&mut self){ self.btn.resize(self.x, self.y, self.w, self.h); } }
fn main() { let app = app::App::default(); let mut wind = Window::new(100, 100, 400, 300, "Resizable Window"); let mut _btn = Button::new( 160, 200, 80, 40, "The Button");// ボタン生成 let mut _btnwrap= WidgetWrap::new(// ボタンをサイズと一緒に管理する構造体に管理委譲 160, 200, 80, 40, _btn ); wind.end(); wind.resizable(&wind); wind.show(); wind.handle(move |_, ev| match ev { // ここでリサイズイベント時の処理を行う Event::Resize => { // ボタンの位置とサイズを保持 _btnwrap.size_keep(); true } _ => false, } ); app.run().unwrap(); }
RustでGUIを使いたい。軽量で、導入もかなり簡単なfltk-rs。しかしウィンドウサイズを変更すると中のコントロールまでサイズが変わってしまうのはかなりいただけない。
参考: https://docs.rs/fltk/latest/fltk/
gitからダウンロードが必要なので、ビルド時にgitへのパスが通っていることが必要。
use fltk::{ app, prelude::*, window::Window, button::Button }; fn main() { let app = app::App::default(); // ウィンドウを作成 let mut wind = Window::default() .with_size(400, 300) .with_label("Rust + FLTK"); // ウィンドウサイズを可変にする wind.resizable(&wind); // ボタンを作成 let mut mybutton = Button::new(160,200,80,40,"ボタン"); wind.end(); wind.show(); // ボタンが押された時の処理 mybutton.set_callback( move |_| { // メッセージボックスを表示 fltk::dialog::message(200, 200, "こんにちは"); }); app.run().unwrap(); }
Kaspersky Virus Removal Tool for Linux。無料のアンチウィルスソフトウェア。
・常駐しない。都度手動で起動する。
・自分でアップデートしない。使いたいときに都度手動でダウンロードする。
https://www.kaspersky.com/downloads/free-virus-removal-tool
あるいはDownloadのリンク先が以下だったのでwgetできる。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib,"wxbase32ud.lib") #pragma comment(lib,"wxbase32ud_net.lib") #pragma comment(lib,"wxbase32ud_xml.lib") #pragma comment(lib,"wxmsw32ud_adv.lib") #pragma comment(lib,"wxmsw32ud_aui.lib") #pragma comment(lib,"wxmsw32ud_core.lib") #pragma comment(lib,"wxmsw32ud_gl.lib") #pragma comment(lib,"wxmsw32ud_html.lib") #pragma comment(lib,"wxmsw32ud_media.lib") #pragma comment(lib,"wxmsw32ud_propgrid.lib") #pragma comment(lib,"wxmsw32ud_qa.lib") #pragma comment(lib,"wxmsw32ud_ribbon.lib") #pragma comment(lib,"wxmsw32ud_richtext.lib") #pragma comment(lib,"wxmsw32ud_stc.lib") #pragma comment(lib,"wxmsw32ud_webview.lib") #pragma comment(lib,"wxmsw32ud_xrc.lib") #else #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") #endif #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> #include <wx/wx.h> #include <wx/window.h> #include <vtkRenderWindow.h> #include <vtkRenderer.h> #include <vtkRenderWindowInteractor.h> #include <vtkAutoInit.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib, "crypt32") #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "Psapi.Lib") #pragma comment(lib, "winmm.lib") #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "dbghelp.lib") VTK_MODULE_INIT(vtkRenderingOpenGL2); // VTK 8 以降で必要 VTK_MODULE_INIT(vtkInteractionStyle);
// VTKを表示するwxPanelを表示する class MyVTKPanel : public wxPanel { vtkSmartPointer<vtkRenderWindow> renderWindow; vtkSmartPointer<vtkRenderer> renderer; public: vtkSmartPointer<vtkRenderer> getRenderer() {return renderer;} vtkSmartPointer<vtkRenderWindow> getRenderWindow(){return renderWindow;} MyVTKPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) : wxPanel(parent, id, pos, size) { renderer = vtkSmartPointer<vtkRenderer>::New(); renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetParentId(reinterpret_cast<void*>(this->GetHandle()));// PanelにVTKを設定 renderWindow->SetSize(this->GetSize().GetWidth(), this->GetSize().GetHeight()); // 背景を黒に設定 renderer->SetBackground(0.0, 0.0, 0.0); // wxWidgets ウィンドウの背景色を設定 // これは表示されないので黄色が出てきたらVTK表示が失敗している証拠 this->SetBackgroundColour(*wxYELLOW); // ペイントイベントのハンドラを設定 this->Bind(wxEVT_PAINT, &MyVTKPanel::OnPaint, this); // サイズ変更イベントのハンドラを設定 this->Bind(wxEVT_SIZE, &MyVTKPanel::OnSize, this); this->Layout(); } // 描画イベントのハンドラ void OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); renderWindow->Render(); } // サイズ変更イベントのハンドラ void OnSize(wxSizeEvent& event) { renderWindow->SetSize(this->GetSize().GetWidth(), this->GetSize().GetHeight()); renderWindow->Render(); } };
// マウスで回転・拡大縮小・平行移動を行う設定 void interactor(vtkSmartPointer<vtkRenderWindow> rendererwindow) { vtkSmartPointer<vtkRenderWindowInteractor>renderWindowInteractor; renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); renderWindowInteractor->SetRenderWindow(rendererwindow); }
// 表示するアイテムを追加 void setitem(vtkSmartPointer<vtkRenderer> renderer) { // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); renderer->AddActor(actor); }
///////////////////////////////////// class MyFrame : public wxFrame { public: MyFrame() : wxFrame(NULL, wxID_ANY, "VTK wx サンプル", wxDefaultPosition, wxSize(400, 300)) { Centre();// ウィンドウを画面中央に表示 // VTKを張り付け MyVTKPanel* vtkpanel = new MyVTKPanel(this, wxID_ANY, wxDefaultPosition, wxSize(300, 300)); // マウスイベントを有効にする interactor(vtkpanel->getRenderWindow()); // アイテムを追加 setitem(vtkpanel->getRenderer()); // ウィンドウをセンタリング wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(0, 0, 1, wxEXPAND, 5); sizer->Add(vtkpanel, 0, wxALIGN_CENTER | wxALL, 5); sizer->Add(0, 0, 1, wxEXPAND, 5); this->SetSizer(sizer); } };
class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame(); // ウィンドウサイズを600x600 frame->SetSize(600, 600); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
・vtkTextActorを使って表示する文字列を作成。日本語は使えない(?)ので注意
・Actorの三次元座標を画面上のピクセル座標に変換するにはvtkCoordinateを使用する。
・再描画が行われるたびにテキストの位置の再計算が必要。AddObserverでイベントリスナを追加する
//VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkConeSource.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #include <vtkTextActor.h> #include <vtkTextProperty.h> #include <vtkCamera.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); VTK_MODULE_INIT(vtkRenderingFreeType); // テキスト表示に必要
// テキストの位置をアクタの中心に合わせる void UpdateTextActorPosition(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, vtkSmartPointer<vtkTextActor> textActor) { double* c = actor->GetPosition(); // アクターの中心座標を取得 // cの三次元座標をクライアント領域のピクセル座標に変換 vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New(); coordinate->SetCoordinateSystemToWorld(); coordinate->SetValue(c); int* xy = coordinate->GetComputedDisplayValue(renderer); int x = static_cast<int>(xy[0]); int y = static_cast<int>(xy[1]); // テキストの位置を設定 textActor->SetPosition(x, y); }
vtkSmartPointer<vtkTextActor> CreateTextActorFromActor(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, const std::string& title) { auto textActor = vtkSmartPointer<vtkTextActor>::New(); textActor->SetInput(title.c_str()); // テキストプロパティを取得 vtkTextProperty* textProperty2 = textActor->GetTextProperty(); // テキストサイズ(フォントサイズ)を設定 textProperty2->SetFontSize(24); // テキストの色を設定(オプション、例えば白色) textProperty2->SetColor(1.0, 1.0, 1.0); UpdateTextActorPosition(renderer, actor, textActor); return textActor; }
////////////////////////////////////////////////////////////// // VTK内のアクションに応じて呼び出すためのコールバッククラス ////////////////////////////////////////////////////////////// class CameraModifiedCallback : public vtkCommand { vtkSmartPointer<vtkActor> _actor; vtkSmartPointer<vtkTextActor> _textActor; vtkSmartPointer<vtkRenderer> _renderer; public: static CameraModifiedCallback* New() { return new CameraModifiedCallback; } void SetRenderer(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, vtkSmartPointer<vtkTextActor> textActor) { _renderer = renderer; _actor = actor; _textActor = textActor; } void Execute(vtkObject* caller, unsigned long eventId, void* callData) override { // イベントに応じて処理を分岐 if (eventId == vtkCommand::ModifiedEvent) { vtkCamera* camera = dynamic_cast<vtkCamera*>(caller); if (!camera) return; // callerがvtkCameraでない場合は何もしない UpdateTextActorPosition(_renderer, _actor, _textActor); } else if (eventId == vtkCommand::ConfigureEvent) { // ウィンドウサイズ変更イベントの処理 UpdateTextActorPosition(_renderer, _actor, _textActor); } } };
int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->SetInteractor(interactor); renderWindow->Render(); ////////////////////////////////////// ////////////////////////////////////// auto renderer_1st = vtkSmartPointer<vtkRenderer>::New(); auto renderer_2nd = vtkSmartPointer<vtkRenderer>::New(); // レイヤー番号の指定 // 番号が若いほど背面に描画される renderer_1st->SetLayer(0); renderer_2nd->SetLayer(1); renderWindow->SetNumberOfLayers(2);// レイヤー数を指定しておいたほうが行儀がいいらしい renderWindow->AddRenderer(renderer_1st); renderWindow->AddRenderer(renderer_2nd); renderer_1st->ResetCamera(); // カメラの共有 renderer_2nd->SetActiveCamera(renderer_1st->GetActiveCamera()); ////////////////////////////////////// ////////////////////////////////////// ////////////////////////////////////// // レイヤー1にCylinderを表示 vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); { vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); actor->SetMapper(mapper); actor->GetProperty()->SetColor(1.0, 0.0, 0.0); // レイヤー1にアクタを追加 renderer_1st->AddActor(actor); } // 表示用の文字列オブジェクトを作成 vtkSmartPointer<vtkTextActor> text = CreateTextActorFromActor(renderer_2nd, actor, "The Cylinder"); renderer_2nd->AddActor(text);
// カメラの変更時に呼び出すコールバックを作成 vtkSmartPointer<CameraModifiedCallback> camera_callback = CameraModifiedCallback::New(); camera_callback->SetRenderer(renderer_2nd,actor,text); // カメラの変更を検知したときにcamera_callbackを呼び出す renderer_2nd->GetActiveCamera()->AddObserver(vtkCommand::ModifiedEvent, camera_callback); // ウィンドウサイズが変更されたときにもcamera_callbackを呼び出したい interactor->AddObserver(vtkCommand::ConfigureEvent, camera_callback);
// イベントループ interactor->Start(); return 0; }