しばらく使っていなかったらGeometry nodeでオブジェクト周辺に点群を生成する機能の使い方が随分と変わっていたので更新しておきたい。初期のころはPoint Distributeとか呼ばれていたもののこと。
なお各ノードの追加はすべて[Shift+A]から検索している。
過去記事:
変数形式で入力しなければいけなかったところがノード接続になって好感が持てる。
検索して手間取ったのが生成するインスタンスを指定する方法が、Point InstanceからObject Info->Geometry to Instance とより抽象化されたところ。
(誤解を恐れずに言うなら)conceptはテンプレートで渡す際のclass TのTに条件を付けるための機能。
以下のように、
という形で定義する。
型Tで与えられた変数valの使い方を定義すると考えると読みやすくなる。
#include <iostream>
// T型の変数valは、print()と++を持たなければならない
template <class T> concept CallableFunction = requires (T val) { val.print(); // print()関数があることを要求 val.operator++(); // ++演算子があることを要求 };
// CallableFunctionの制約がついている所で使用できるクラス class MyValidClass { int val; public: MyValidClass() { val = 5; } void print() {// print()関数を定義 std::cout << "val == " << val << std::endl; } MyValidClass& operator++() {// ++演算子を要求 ++val; return *this; } };
// CallableFunctionの制約がある場合は指定できない。 // operator++() がない。 class MyInvalid { int val; public: MyInvalid() { val = 5; } void print() { std::cout << "val == " << val << std::endl; } };
// print()関数と++演算子を持つクラスを引数に取る関数 template <CallableFunction Type> void func(Type val) { val.print(); }
int main() { MyValidClass my; func(my); }
{ } 内に式を書くと、その式を書くことができるような型だけを要求することができる。
#include <iostream> #include <algorithm> #include <vector>
template <class T> concept Sortable = requires (T& val) { // { } 内の式が有効であることを要求する書き方ができる。 // 例えば、以下は T 型の val はソート可能であることを要求する { std::sort(val.begin(), val.end()) }; };
// print()関数と++演算子を持つクラスを引数に取る関数 template <Sortable Type> void func(Type& val) { std::sort(val.begin(), val.end()); }
int main() { std::vector<int> my{ 5,3,8 }; func(my); for (auto& i : my) { std::cout << i << std::endl; } }
{ 式 } で、式が有効であることを要求できるが、式の結果のデータ型を要求するには
とする。
この仕組みを応用すれば、関数の戻り値を要求できる。
#include <iostream> #include <algorithm> #include <vector>
template <class T> concept Sizable = requires (T & val) { { val.size() // size()関数を持つ }->std::same_as<size_t>; // size()関数の戻り値はsize_t型 // std::same_asはconcept。式の戻り値の型を指定する。 };
class MyArray { std::vector<int>vec; public: void push_back(int i) { vec.push_back(i); } int size() { return vec.size(); } };
// この関数は、戻り値がsize_t型のsize()関数を持つ型しか受け付けない template <Sizable Type> void func(Type& val) { std::cout<<val.size()<<std::endl; }
int main() { std::vector<int> my; // MyArray my; my.push_back(3); my.push_back(1); my.push_back(8); func(my); }
wxWidgetsを使用した場合の問題の一つが、配布時に同梱するDLLの数が増えることで、これを解決するためにwxWidgetsをStatic Link Libraryとしてビルドしてexeとくっつけてしまうことで、DLLの同梱を不要にする。
まずソースコードをダウンロードするが、zipでは依存関係が多くて面倒なので、git clone --recursiveで依存関係も含め一括でダウンロードする。
CMakeでは、wxBUILD_SHAREDを検索し、このチェックを外す
ALL_BUILDをビルドし、INSTALLする。
基本的な使い方はShared Libraryの時と同じだが、何点か注意点がある。
VC++でwxWidgetsを試す(1)
・WXUSINGDLL を指定しない
プリプロセッサにWXUSINGDLLを指定しない。指定してしまうとLNK2001が大量発生する
・windowsのlibファイルのリンクを追加
comctl32.lib , rpcrt4.lib をリンクする。
// プリプロセッサに以下を追加 // __WXMSW__ // static link library の場合、以下は指定しない // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 // Win32 API #pragma comment(lib, "comctl32.lib") #pragma comment(lib, "rpcrt4.lib") // wxWidgets #pragma comment(lib,"wxmsw33u_core.lib") #pragma comment(lib,"wxbase33u.lib") #pragma comment(lib,"wxbase33u_net.lib") #pragma comment(lib,"wxbase33u_xml.lib") #pragma comment(lib,"wxexpat.lib") #pragma comment(lib,"wxjpeg.lib") #pragma comment(lib,"wxlexilla.lib") #pragma comment(lib,"wxmsw33u_adv.lib") #pragma comment(lib,"wxmsw33u_aui.lib") #pragma comment(lib,"wxmsw33u_gl.lib") #pragma comment(lib,"wxmsw33u_html.lib") #pragma comment(lib,"wxmsw33u_media.lib") #pragma comment(lib,"wxmsw33u_propgrid.lib") #pragma comment(lib,"wxmsw33u_qa.lib") #pragma comment(lib,"wxmsw33u_ribbon.lib") #pragma comment(lib,"wxmsw33u_richtext.lib") #pragma comment(lib,"wxmsw33u_stc.lib") #pragma comment(lib,"wxmsw33u_webview.lib") #pragma comment(lib,"wxmsw33u_xrc.lib") #pragma comment(lib,"wxpng.lib") #pragma comment(lib,"wxregexu.lib") #pragma comment(lib,"wxscintilla.lib") #pragma comment(lib,"wxtiff.lib") #pragma comment(lib,"wxzlib.lib") ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { } private: // イベント処理しないときはこれを入れない // wxDECLARE_EVENT_TABLE(); }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
なんでもタスクトレイという言い方は正しくなく、通知領域、Notification Areaが正しいらしい。知ったことではない。
タスクトレイにアイコンを表示する方法を以前調べた。
タスクトレイにアイコンを表示する
クリックしたときにメニューを表示する場合、GetCursorPosでマウスカーソルの位置を取得して、TrackPopupMenuでメニューを表示する。
#pragma warning(disable:4996) #include <windows.h> #include <shellapi.h> static const int TasktrayIconID = 100; static const int WM_MY_NOTIFYICON = WM_USER + TasktrayIconID; const int ID_MENU_MSG = 100; const int ID_MENU_EXIT = 101; HMENU myCreateMenu(); void intoTasktray(HWND hwnd, const int IconID); void fromTasktray(HWND hwnd, const int IconID); void menuPopup(HWND hwnd, HMENU menu); ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// /////////////////////////////////
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { static HMENU hMenu = nullptr; switch (msg) { case WM_CREATE: hMenu = myCreateMenu();//メニュー作成 break; case WM_LBUTTONDOWN://ウィンドウをクリックしたらアイコン表示開始 { intoTasktray(hwnd, TasktrayIconID); break; } case WM_MY_NOTIFYICON: if (lp == WM_LBUTTONDOWN && wp == TasktrayIconID) { // アイコンを左クリックしたらウィンドウを表示 fromTasktray(hwnd, TasktrayIconID); } else if (lp == WM_RBUTTONUP) { // アイコンを右クリックしたらポップアップメニューを表示 menuPopup(hwnd, hMenu); } break; case WM_COMMAND: switch (wp) { case ID_MENU_MSG: // "Open"がクリックされた場合 MessageBoxA(hwnd, "MessageBox", "Info", MB_OK); break; case ID_MENU_EXIT: // "Exit"がクリックされた場合 DestroyWindow(hwnd); break; } break; case WM_DESTROY: DestroyWindow(hwnd); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WINDOW"); if (!RegisterClass(&winc)) return -1; hwnd = CreateWindow( TEXT("SZL-WINDOW"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return -1; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
// メニューの作成 HMENU myCreateMenu() { HMENU menu = CreatePopupMenu(); AppendMenuA(menu, MF_STRING, ID_MENU_MSG, "Open"); AppendMenuA(menu, MF_STRING, ID_MENU_EXIT, "Exit"); return menu; }
// ウィンドウを消し、アイコンをタスクトレイへ格納 void intoTasktray(HWND hwnd, const int IconID) { NOTIFYICONDATA nid; nid.cbSize = sizeof(nid); nid.hWnd = hwnd; nid.uID = IconID;//複数のアイコンを表示したときの識別ID。なんでもいい。 nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uCallbackMessage = WM_MY_NOTIFYICON; //WM_USER以降の定数。 nid.hIcon = (HICON)LoadIcon(NULL, IDI_APPLICATION); wcscpy(nid.szTip, L"タスクトレイで表示する文字列"); int ret = (int)Shell_NotifyIcon(NIM_ADD, &nid); ShowWindow(hwnd, SW_HIDE); }
// タスクトレイからアイコンを削除し、ウィンドウを表示 void fromTasktray(HWND hwnd, const int IconID) { ShowWindow(hwnd, SW_SHOW); //タスクトレイアイコンを削除 NOTIFYICONDATA nid; nid.cbSize = sizeof(nid); nid.hWnd = hwnd; nid.uID = IconID; nid.uFlags = 0; Shell_NotifyIcon(NIM_DELETE, &nid); }
// ポップアップメニューを表示 void menuPopup(HWND hwnd, HMENU menu) { POINT pt; GetCursorPos(&pt); SetForegroundWindow(hwnd); TrackPopupMenu(menu, TPM_RIGHTALIGN, pt.x, pt.y, 0, hwnd, NULL); }
CreateDIBSectionを使ってDIBを作り、CreateCompatibleDCを使って描画してみる。
各関数・データ型・定数がwindows-rsのどこにあるかさえわかれば、あとはそこまで難しくはない。
一番厄介なのはwinmain.rs内でmydibのインスタンスであるグローバル変数で、これはstatic + Lazy + Mutexで実現しないといけない。
use windows::Win32::Graphics::Gdi::*; use windows::core::*; use windows::Win32::Foundation::*; // ■ BITMAPINFO ... windows::Win32::Graphics::Gdi::BITMAPINFO // ■ BITMAPINFOHEADER ... windows::Win32::Graphics::Gdi::BITMAPINFOHEADER // ■ BI_RGB ... windows::Win32::Graphics::Gdi::BI_RGB // ■ CreatedHDC ... windows::Win32::Graphics::Gdi::CreatedHDC // ■ CreateDCA ... windows::Win32::Graphics::Gdi::CreateDCA // ■ CreateCompatibleDC ... windows::Win32::Graphics::Gdi::CreateCompatibleDC // ■ CreateDIBSection ... windows::Win32::Graphics::Gdi::CreateDIBSection // ■ DIB_RGB_COLORS ... windows::Win32::Graphics::Gdi::DIB_RGB_COLORS // ■ DeleteDC ... windows::Win32::Graphics::Gdi::DeleteDC // ■ HBITMAP ... windows::Win32::Graphics::Gdi::HBITMAP // ■ HBITMAP ... windows::Win32::Graphics::Gdi::HBITMAP // ■ SelectObject ... windows::Win32::Graphics::Gdi::SelectObject // ■ HANDLE ... windows::Win32::Foundation::HANDLE // ■ PCSTR ... windows::core::PCSTR pub struct Win32DIBCanvas{ pub bitmap :HBITMAP, pub bitmapInfo :BITMAPINFO, pub ppixel :*mut std::ffi::c_void, pub hMemDC : CreatedHDC, } // ppixel:*mut std::ffi::c_void, があるので、Send,Syncを実装しなければmyDIBをLazy<Mutex<myDIB>>に入れられない unsafe impl Send for Win32DIBCanvas {} unsafe impl Sync for Win32DIBCanvas {} impl Win32DIBCanvas{ pub fn new()->Win32DIBCanvas{ Win32DIBCanvas{ bitmap :HBITMAP(0), bitmapInfo:Default::default(), ppixel :std::ptr::null_mut(), hMemDC :CreatedHDC(0), } } pub fn create(&mut self,width:i32,height:i32){ unsafe{ let mut hdca = CreateDCA( PCSTR::from_raw("DISPLAY\0".as_ptr()), None, None, None ); self.bitmapInfo.bmiHeader.biSize = std::mem::size_of::<BITMAPINFOHEADER>() as u32; self.bitmapInfo.bmiHeader.biWidth = width; self.bitmapInfo.bmiHeader.biHeight = -height; self.bitmapInfo.bmiHeader.biPlanes = 1; self.bitmapInfo.bmiHeader.biBitCount = 3*8; self.bitmapInfo.bmiHeader.biCompression = BI_RGB; let hBitmap = CreateDIBSection( hdca, &self.bitmapInfo, DIB_RGB_COLORS, &mut self.ppixel, HANDLE(0), 0 ); self.hMemDC = CreateCompatibleDC(hdca); if let Ok(hbmp) = hBitmap{ SelectObject(self.hMemDC,hbmp); } DeleteDC(hdca); } } }
main関数。CではWinMain開始だが、Rustではwindowsプログラムもmainから始める。
気分的にWinMainが欲しいので、main関数からWinMainを呼ぶようにしている。
mod winmain; use winmain::WinMain; use windows::Win32::Foundation::*; use windows::Win32::System::LibraryLoader::*; use windows::Win32::System::Threading::*; // ■ HINSTANCE ... windows::Win32::Foundation::HINSTANCE // ■ GetModuleHandleA ... windows::Win32::System::LibraryLoader::GetModuleHandleA // ■ STARTUPINFOW ... windows::Win32::System::Threading::STARTUPINFOW // ■ GetStartupInfoW ... windows::Win32::System::Threading::GetStartupInfoW // Rustのwindows-rsからウィンドウズアプリケーションをビルドする時には、 // WinWain ではなくmainをエントリポイントとする。 fn main()-> windows::core::Result<()> { let hInstance:HINSTANCE; unsafe{ // https://github.com/microsoft/windows-samples-rs/blob/master/create_window/src/main.rs hInstance = GetModuleHandleA(None).unwrap(); } // https://doc.rust-jp.rs/book-ja/ch12-01-accepting-command-line-arguments.html let args: Vec<String> = std::env::args().collect(); // https://stackoverflow.com/questions/68322072/how-to-get-args-from-winmain-or-wwinmain-in-rust let mut si = STARTUPINFOW { cb: std::mem::size_of::<STARTUPINFOW>() as u32, ..Default::default() }; unsafe { GetStartupInfoW(&mut si) }; let cmd_show = si.wShowWindow as i32; //////////////////////////////////////////////////////////////////////////////////// // 自作WinMainを呼び出す WinMain( hInstance, HINSTANCE(0), args, cmd_show ); Ok(()) }
#[path="./win32dib.rs"] mod win32dib; use win32dib::Win32DIBCanvas; use windows::{ core::*, Win32::Foundation::*, Win32::Graphics::Gdi::*, Win32::UI::WindowsAndMessaging::*, }; use once_cell::sync::Lazy; use std::sync::Mutex; // ■ Mutex ... std::sync::Mutex // ■ Lazy ... once_cell::sync::Lazy // DIBを管理する構造体のグローバル変数 static mydib:Lazy<Mutex<Win32DIBCanvas>> = Lazy::new(|| Mutex::new( Win32DIBCanvas::new() ) ); // ■ COLORREF ... windows::Win32::Foundation::COLORREF // ■ HINSTANCE ... windows::Win32::Foundation::HINSTANCE // ■ HWND ... windows::Win32::Foundation::HWND // ■ LPARAM ... windows::Win32::Foundation::LPARAM // ■ LRESULT ... windows::Win32::Foundation::LRESULT // ■ POINT ... windows::Win32::Foundation::POINT // ■ RECT ... windows::Win32::Foundation::RECT // ■ WPARAM ... windows::Win32::Foundation::WPARAM // ■ BeginPaint ... windows::Win32::Graphics::Gdi::BeginPaint // ■ BitBlt ... windows::Win32::Graphics::Gdi::BitBlt // ■ CreatePen ... windows::Win32::Graphics::Gdi::CreatePen // ■ EndPaint ... windows::Win32::Graphics::Gdi::EndPaint // ■ LineTo ... windows::Win32::Graphics::Gdi::LineTo // ■ MoveToEx ... windows::Win32::Graphics::Gdi::MoveToEx // ■ PS_SOLID ... windows::Win32::Graphics::Gdi::PS_SOLID // ■ PAINTSTRUCT ... windows::Win32::Graphics::Gdi::PAINTSTRUCT // ■ SelectObject ... windows::Win32::Graphics::Gdi::SelectObject // ■ SRCCOPY ... windows::Win32::Graphics::Gdi::SRCCOPY // ■ CreateWindowExA ... windows::Win32::UI::WindowsAndMessaging::CreateWindowExA // ■ CS_HREDRAW ... windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW // ■ CS_VREDRAW ... windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW // ■ CW_USEDEFAULT ... windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT // ■ DefWindowProcA ... windows::Win32::UI::WindowsAndMessaging::DefWindowProcA // ■ DispatchMessageA ... windows::Win32::UI::WindowsAndMessaging::DispatchMessageA // ■ GetMessageA ... windows::Win32::UI::WindowsAndMessaging::GetMessageA // ■ LoadCursorW ... windows::Win32::UI::WindowsAndMessaging::LoadCursorW // ■ IDC_ARROW ... windows::Win32::UI::WindowsAndMessaging::IDC_ARROW // ■ MSG ... windows::Win32::UI::WindowsAndMessaging::MSG // ■ PostQuitMessage ... windows::Win32::UI::WindowsAndMessaging::PostQuitMessage // ■ RegisterClassA ... windows::Win32::UI::WindowsAndMessaging::RegisterClassA // ■ WNDCLASSA ... windows::Win32::UI::WindowsAndMessaging::WNDCLASSA // ■ WS_OVERLAPPEDWINDOW ... windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW // ■ WS_VISIBLE ... windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE // ■ PCSTR ... windows::core::PCSTR extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { unsafe { let rectnullptr: Option<*const RECT> = None; match message as u32 { WM_CREATE => { let refmydib = &mut mydib.lock().unwrap(); refmydib.create(640,480); let hPen = CreatePen( PS_SOLID, 1, COLORREF(0x000000FF) ); // 赤いペンを作成 if hPen.is_invalid() == false{ // ペンが作成できていたら線を引く let oldPen = SelectObject( refmydib.hMemDC, hPen ); // refmydib.hMemDC の 50,50 ~ 100,100 に線を引く MoveToEx( refmydib.hMemDC, 50, 50, None as Option<*mut POINT> ); LineTo( refmydib.hMemDC, 100, 100 ); SelectObject( refmydib.hMemDC, oldPen ); // ペンを削除 DeleteObject(hPen); } LRESULT(0) } WM_PAINT => { let mut ps = PAINTSTRUCT::default(); let hdc = BeginPaint(window, &mut ps); let refmydib = &mut mydib.lock().unwrap(); BitBlt( hdc, 0, 0, 640, 480, refmydib.hMemDC, 0, 0, SRCCOPY ); EndPaint(window, &ps); LRESULT(0) } WM_DESTROY => { PostQuitMessage(0); LRESULT(0) } _ => DefWindowProcA(window, message, wparam, lparam), } } } /// 自作 WinMain pub fn WinMain( hInstance : HINSTANCE, hPrevInstance : HINSTANCE, lpCmdLine:Vec<String>, nCmdShow:i32 )->i32{ unsafe{ let _hCursor = LoadCursorW(None,IDC_ARROW).unwrap(); let _hInstance = hInstance; let _lpszClassName = PCSTR::from_raw("MY_NEW_WINDOW\0".as_ptr()); let _style = CS_HREDRAW | CS_VREDRAW; // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/struct.WNDCLASSA.html let wc=WNDCLASSA{ hCursor :_hCursor, hInstance: _hInstance, lpszClassName: _lpszClassName, style: _style, lpfnWndProc: Some(wndproc), ..Default::default() }; let atom = RegisterClassA(&wc); debug_assert!(atom != 0); let nullptr: Option<*const ::core::ffi::c_void> = None; CreateWindowExA( Default::default(), _lpszClassName, PCSTR::from_raw("This is a sample window\0".as_ptr()), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, None, None, _hInstance, nullptr, ); let mut message = MSG::default(); while GetMessageA(&mut message, HWND(0), 0, 0).into() { DispatchMessageA(&mut message); } } return 0; }
Rustのグローバル変数(可変)について調べた話。正直自信がない。
use once_cell::sync::Lazy; // 1.3.1 use std::sync::Mutex;
struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } }
// グローバル変数 static myGlobal: Lazy<Mutex<Foo>> = Lazy::new(|| Mutex::new( Foo::new() ) );
fn call_1(){ let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; }
fn call_2(){ let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; }
fn main() { call_1(); call_2(); // myGlobal のミューテックスガードを取得 let mut myg: std::sync::MutexGuard<'_, Foo> = myGlobal.lock().unwrap(); myg.a += 1; println!("myg.a= {} , myg.b= {} ", myg.a,myg.b ); }
[package]
name = "rs-static"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
once_cell = "1.3.1"
グローバル変数はstaticをつけなければならない。
しかし、staticで定義できるのは定数のみ。従ってstaticだけではかなり限定的な使い方しかできない。
Mutexをつけると、
1.スレッドセーフになるのでunsafeをつけなくてもよくなる
2.内部可変性パターンにより、変数をmutableにできる
これにより、staticをつけながら可変な変数を定義できる。
つまり、可変なグローバル変数はMutexが必要。
Lazyは変数を遅延初期化するための機能。遅延初期化は、変数へ最初にアクセスしたときに初期化が発生するようにする。
初期化のタイミングをコントロールするためには、Lazyが必要。
正直、なくても動かすことはできる。
少なくとも、今回の小規模なプログラムに関しては。
ただ、初期化のタイミングがコンパイル時になる。さらにMutex::new()に与える引数が定数でなければならなくなるため、わざわざconst fnをimplしないといけなくなる。
lazyの || は無名関数を定義するためのもので、以下のように使用する。引数、戻り値型は省略可。処理を括る{}も省略できる。
fn main() { // let func = | 引数 | -> 戻り値型{処理}; let func = | x:i32 , y:i32 | -> f32 { ( x + y ) as f32 }; let a = func(1,2); println!("a = {} ", a ); }
// use once_cell::sync::Lazy; //Lazyは必須ではない use std::sync::Mutex; struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } const fn new_const() -> Foo {// constをつけて定数のFooを返せる Foo { a: 1, b: 1, } } } // グローバル変数 // Lazyがないときは new_constにしなければいけない static myGlobal: Mutex<Foo> = Mutex::new( Foo::new_const() ); // 以下同じ
use once_cell::sync::Lazy; use std::sync::Mutex; // グローバル変数 static myGlobal: Lazy<Mutex<Vec<i32> > >= Lazy::new(|| Mutex::new( vec![] )); fn call_1(){ let mut myg:std::sync::MutexGuard<'_, Vec<i32>> = myGlobal.lock().unwrap(); myg.push(1); }
use once_cell::sync::Lazy; use std::sync::Mutex;
#[derive(Debug)] struct Foo { a: i32, b: i32, } impl Foo{ fn new() -> Foo { Foo { a: 1, b: 1, } } }
// グローバル変数 static myGlobal: Lazy<Mutex<Box<Foo> > >= Lazy::new(|| Mutex::new( Box::new(Foo::new()) ));
fn call_1(){ let mut myg:std::sync::MutexGuard<'_, Box<Foo>> = myGlobal.lock().unwrap(); myg.a += 1; }
wxWidgetsでアイコンを表示したい。
HICONを wxIcon::CreateFromHICON関数でwxWidgetsのwxIconへ変換。これをさらにwxBitmapに変換する。
// 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/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 <commctrl.h> #pragma comment(lib, "comctl32.lib") // IID_IImageList に必要 #include <commoncontrols.h>
// アイコンを取得 wxBitmap my_get_icon(const wchar_t* path) { wxBitmap ret; // システムイメージリストの取得 HIMAGELIST hImageList; HRESULT hr = SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&hImageList); // SHIL_JUMBO 256x256 if (SUCCEEDED(hr)) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); // path からアイコンの「イメージリスト上のインデクス」を取得 // ※ アイコンハンドルではない SHGetFileInfo(path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX); HICON hicon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_NORMAL); // wxWidgetsのwxIconに変換 wxIcon computerIcon; computerIcon.CreateFromHICON((WXHICON)hicon); ret = wxBitmap(computerIcon); // wxBitmapに変換してからDestryIconする DestroyIcon(hicon); } return ret; }
///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { wxBitmap mybmp_drive; wxBitmap mybmp_file; wxBitmap mybmp_doc; public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { SetBackgroundColour(*wxWHITE); mybmp_drive = my_get_icon( L"C:\\"); mybmp_file = my_get_icon(L"C:\\Windows\\System32\\notepad.exe"); mybmp_doc = my_get_icon(L"C:\\Users\\Public\\Documents\\desktop.ini"); // PAINTイベントをバインド Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); } // PAINTイベントのハンドラ void OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); if (mybmp_drive.IsOk()) { dc.DrawBitmap(mybmp_drive, 5+0, 5, false); } if (mybmp_file.IsOk()) { dc.DrawBitmap(mybmp_file, 5 + 256*1, 5, false); } if (mybmp_doc.IsOk()) { dc.DrawBitmap(mybmp_doc, 5 + 256*2, 5, false); } } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(1000, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon #include <commctrl.h> #pragma comment(lib, "comctl32.lib") // IID_IImageList に必要 #include <commoncontrols.h> #include <cstdio> #pragma warning(disable:4996) // アイコンを取得 HICON my_get_icon(const wchar_t* path) { // システムイメージリストの取得 HIMAGELIST hImageList; HRESULT hr = SHGetImageList(SHIL_JUMBO , IID_IImageList, (void**)&hImageList); // SHIL_JUMBO 256x256 // SHIL_EXTRALARGE 48x48 // SHIL_LARGE 32x32 // SHIL_SMALL 16x16 // hrからファイルのアイコンを取得 if (SUCCEEDED(hr)) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); // path からアイコンの「イメージリスト上のインデクス」を取得 // ※ アイコンハンドルではない SHGetFileInfo(path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX); // SHGetImageListで取得したイメージリストから // SHGetFileInfoで取得したインデクスを指定してアイコンハンドルを取得 HICON hicon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_NORMAL); // ※ hiconは使わなくなったら DestroyIcon で破棄すること return hicon; } }
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon // 表示用に ImageList_Draw も使える #include <commctrl.h> #pragma comment(lib, "comctl32.lib") // SHGetSpecialFolderLocation // SHGetFolderLocation #include <ShlObj.h> #include <cstdio> #pragma warning(disable:4996)
// アイコンを取得 HICON my_get_special_icon() { #if 0 // 注: SHGetSpecialFolderLocation は非推奨 // https://learn.microsoft.com/ja-jp/windows/win32/api/shlobj_core/nf-shlobj_core-shgetspecialfolderlocation PIDLIST_ABSOLUTE pidl; HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_FONTS, &pidl); #else LPITEMIDLIST pidl; // マイコンピュータのPIDLを取得 HRESULT hr = SHGetFolderLocation(NULL, CSIDL_DRIVES, NULL, 0, &pidl); // CSIDL_DRIVES マイコンピュータアイコン // CSIDL_FONTS フォントアイコン // CSIDL_DESKTOP デスクトップアイコン // ... etc #endif SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW(// イメージリストとその中のpidlで指定したアイコンのidを取得 (LPCTSTR)pidl, 0, &shfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_LARGEICON); HIMAGELIST imagelist = (HIMAGELIST)ret; // アイコンリストが取得できたかどうかを表す if (imagelist != nullptr) { // imagelistからアイコンを取得 // ImageList_GetIconはアイコンのコピーを渡すので、後でDestroyIconが必要 HICON hicon = ImageList_GetIcon(imagelist, shfi.iIcon, ILD_NORMAL); // SHGFI_SYSICONINDEX を指定した場合、hIconは無効 //DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; // アイコン static HICON myicon; switch (msg) { case WM_CREATE: // コピーしたアイコンを取得 myicon = my_get_special_icon(); return 0; case WM_DESTROY: // コピーしたアイコンを破棄 DestroyIcon(myicon); PostQuitMessage(0); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); // アイコンを描画 DrawIconEx(hdc, 0 + 10, 10, myicon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("icon test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
WindowsでSHGetFileInfoでアイコンを取得する方法。
SHGFI_SYSICONINDEXを指定した場合、同じアイコンはiIconが同じになるので、テーブルを作っておけばメモリを節約できる。
ちなみに下で、アイコンの描画はDrawIconを使っているが、これはアイコン本来のサイズで描画してくれないので、動作確認のためにはDrawIconExを使ったほうが良い。
DrawIconExに、第五、第六、第七引数に0を指定し、且つ DI_NORMAL | DI_COMPAT をつけて描画すると、アイコンの本来のサイズで描画できる。
DrawIconEx(hdc, 0 + 10, 10, icondrive, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 32 + 10, 10, iconfolder, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 64 + 10, 10, iconfile, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 96 + 10, 10, iconexe, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
本来ならCopyIconしないでそのまま返して、WM_DESTROYのところでDestroyIconすればいいが、「SHGFI_ICONで取得したアイコンはDestroyIconが必要」ということを強調するためにあえて行っている。
#include<windows.h> #include<tchar.h> #include <cstdio> #pragma warning(disable:4996)
// アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW(// アイコンを取得 path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_ICON | /* SHGFI_SYSICONINDEX | */ SHGFI_LARGEICON); // retはアイコンが取得できたかどうかを表す if (SUCCEEDED(ret)) { // アイコンをコピー HICON hicon = CopyIcon(shfi.hIcon); // SHGFI_ICON を指定しているので取得したのはアイコン // なのでDestroyIconで破棄する DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; // アイコン一覧 static HICON icondrive; static HICON iconfolder; static HICON iconfile; static HICON iconexe; switch (msg) { case WM_CREATE: // コピーしたアイコンを取得 icondrive = my_get_icon(L"C:\\"); iconfolder = my_get_icon(L"C:\\Windows"); iconfile = my_get_icon(L"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.35.32215\\atlmfc\\include\\afx.h"); iconexe = my_get_icon(L"C:\\Windows\\notepad.exe"); return 0; case WM_DESTROY: // コピーしたアイコンを破棄 DestroyIcon(icondrive); DestroyIcon(iconfolder); DestroyIcon(iconfile); DestroyIcon(iconexe); PostQuitMessage(0); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); // アイコンを描画 DrawIcon(hdc, 0+10, 10, icondrive); DrawIcon(hdc, 32+10, 10, iconfolder); DrawIcon(hdc, 64+10, 10, iconfile); DrawIcon(hdc, 96+10, 10, iconexe); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("icon test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
よく考えたらImageList_GetIconの時もDestroyIconが必要なので上と合わせるべきなのだが間違っているわけではないというかむしろこちらが正攻法だと思うのでこのままにしておく。
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon // 表示用に ImageList_Draw も使える #include <commctrl.h> #pragma comment(lib, "comctl32.lib") #include <cstdio> #pragma warning(disable:4996) // アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW( path, 0, &shfi, sizeof(SHFILEINFO), /*SHGFI_ICON |*/ SHGFI_SYSICONINDEX | SHGFI_LARGEICON); HIMAGELIST imagelist = (HIMAGELIST)ret; // アイコンリストが取得できたかどうかを表す if (imagelist != nullptr) { // imagelistからアイコンを取得 // ImageList_GetIconはアイコンのコピーを渡すので、後でDestroyIconが必要 HICON hicon = ImageList_GetIcon(imagelist, shfi.iIcon, ILD_NORMAL); // SHGFI_SYSICONINDEX を指定した場合、hIconは無効 //DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; } /* 上と同じ */
wxGLCanvasでglewを使用するとき、glewInit()の呼び出しに失敗する場合がある。
例えば以下のようなコードを書くと、エラーメッセージを取得できる。
GLenum err = glewInit(); if (err != GLEW_OK) { std::string msg = (const char*)glewGetErrorString(err); std::cerr << "Error: " << msg << std::endl; }
これはOpenGLのコンテキストがまだ作成されていないのにglewInit()を呼んでしまったからである。
これを回避する確実な方法は、glewInit()の呼び出しをwxEVT_PAINTの処理時にすることらしい。
つまり描画の最初の一回目でglewInit()する。
BindでwxEVT_PAINT時にRender関数が呼ばれるように設定する。
MyGLCanvas::MyGLCanvas(wxFrame* parent) : wxGLCanvas(parent, wxID_ANY), m_context(new wxGLContext(this)) { Bind(wxEVT_PAINT, &MyGLCanvas::Render, this); /* ... */ }
描画関数。SetCurrentした後でglewInit()する。
void MyGLCanvas::Render(wxPaintEvent& evt) { if (!IsShown()) return; wxGLCanvas::SetCurrent(*m_context); if (glew_inistialized == false) {// 初期化は一回だけでいいのでフラグで制御 GLenum err = glewInit(); if (err != GLEW_OK) {
// エラーの場合はエラー内容を表示
std::string msg = (const char*)glewGetErrorString(err); std::cerr << "Error: " << msg << std::endl; } else { glew_inistialized = true;// 二回呼び出されないようにフラグを立てる } } wxPaintDC(this); int w, h; GetClientSize(&w, &h); glViewport(0, 0, w, h); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 略 */ glFlush(); SwapBuffers(); }