wxWidgetsを使用するときに、.libファイルを大量にリンクしなければならない。この問題を解決するために、無数にある.libファイルを一つのファイルにまとめてしまうことができる。
まず、以下のようにmulti-lib/ フォルダ内にwxWidgetsの.libファイルが入っているとする。
D:. └─multi-lib/ wxbase33u.lib wxbase33u_net.lib wxbase33u_xml.lib wxexpat.lib wxjpeg.lib wxlexilla.lib wxmsw33u_adv.lib wxmsw33u_aui.lib wxmsw33u_core.lib wxmsw33u_gl.lib wxmsw33u_html.lib wxmsw33u_media.lib wxmsw33u_propgrid.lib wxmsw33u_qa.lib wxmsw33u_ribbon.lib wxmsw33u_richtext.lib wxmsw33u_stc.lib wxmsw33u_webview.lib wxmsw33u_xrc.lib wxpng.lib wxregexu.lib wxscintilla.lib wxtiff.lib wxzlib.lib
複数のライブラリを一つにまとめるには、lib.exeを使用する。
lib.exe /OUT:纏めたファイル名.lib ライブラリ1.lib ライブラリ2.lib
まず、 x64 Native Tools Command Prompt for VS 2022 を開いて、Visual Studioのツールを使えるようにする。
そして、以下のように入力してcombined_wx.libを作成する。カレントディレクトリに出力されるので、この例だとmulti-lib/combined_wx.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") #if 1 // 一つに纏めた.libファイル群 #pragma comment(lib,"combined_wx.lib") #else // 本来使用する.libファイル群 #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") #endif ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 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);
比較的最近のGLSLでは、gl_ModelViewMatrix、gl_ProjectionMatrixという名前の変数を定義するとエラーになる。正確には、「先頭に gl_ がついている変数名はコンパイルエラーになる」。
glTranslatedなどが使用できないとか以前に、「変数名によってエラーになる」ことが問題。例えば以下のようなコードを書くと、コンパイルエラーが発生する。
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 gl_ModelViewMatrix; uniform mat4 gl_ProjectionMatrix; void main() { gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos, 1.0); vertexColor = vec4(incolor, 1.0); }
ただし、上の場合、gl_Positionは自分で定義しているわけではないのでエラーにならない。
gl_が頭についていない変数を使えばよい。ところで、gl_ModelViewMatrixやgl_ProjectionMatrixは、glMatrixMode→glTranslatef等で渡された行列の可能性がある。これが使えなくなるので、行列は自分で計算しなければいけない。面倒なのでglmを使用することになる。
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 MyModelViewMatrix; uniform mat4 MyProjectionMatrix; void main() { gl_Position = MyProjectionMatrix * MyModelViewMatrix * vec4(aPos, 1.0); vertexColor = vec4(incolor, 1.0); }
glUseProgram(ProgramID); // これの代わりに glGetUniformLocation + glUniformMatrix4fv // //glMatrixMode(GL_MODELVIEW); //glLoadMatrixf(glm::value_ptr(projectionMatrix)); //glMatrixMode(GL_MODELVIEW); //glLoadMatrixf(glm::value_ptr(modelViewMatrix)); glm::mat4 modelViewMatrix; // モデルビュー行列の計算 static int theta = 0.0f; modelViewMatrix = glm::rotate( glm::mat4(1.0), glm::radians((float)theta), glm::vec3(0.0, 0.0, 1.0) ); theta += 5; glm::mat4 projectionMatrix = glm::mat4(1.0); // 投影行列の計算 GLint modelViewMatrixID = glGetUniformLocation(ProgramID, "MyModelViewMatrix"); GLint projectionMatrixID = glGetUniformLocation(ProgramID, "MyProjectionMatrix"); glUniformMatrix4fv(modelViewMatrixID, 1, GL_FALSE, glm::value_ptr(modelViewMatrix)); glUniformMatrix4fv(projectionMatrixID, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
なんか前にも書いた気がするのだが、日本語の検索結果があまり出てこなかったのでここにまとめておく。
meson.build作成したうえで、以下のように実行する
meson setup 出力ディレクトリ --buildtype=モード --backend=vs
例:
releaseとdebugを同時に指定できない。release用.slnとdebug用.slnを別々に作る必要がある。
meson setup release_build --buildtype=release --backend=vs
meson setup debug_build --buildtype=debug --backend=vs
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; switch (msg) { case WM_DESTROY: PostQuitMessage(0); 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-WIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WIN"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
WinMainを使う場合、/SUBSYSTEM:WINDOWSを指定する。
問題は、mesonが勝手に/SUBSYSTEM:CONSOLEをつけてしまうので、エントリポイントがmainなのかWinMainなのかわからなくなりエラーが出る。
これを回避するために、 gui_app: true を指定する。
project('myapp', 'cpp') cpp = meson.get_compiler('cpp') executable('myapp', 'main.cpp', gui_app: true, # WinMainを使うときはこれが必要 cpp_args: [ '/D_WINDOWS' ], link_args: [ '/SUBSYSTEM:WINDOWS' # WinMainを使うときはこれが必要 ] )
meson setup build
meson compile -C build
の順で実行する。
(mesonenv) C:\Users\myuser\source\repos\win32>meson setup build Directory already configured. Just run your build command (e.g. ninja) and Meson will regenerate as necessary. If ninja fails, run "ninja reconfigure" or "meson setup --reconfigure" to force Meson to regenerate. If build failures persist, run "meson setup --wipe" to rebuild from scratch using the same options as passed when configuring the build. To change option values, run "meson configure" instead. (mesonenv) C:\Users\myuser\source\repos\win32>meson compile -C build Activating VS 17.5.4 INFO: automatically activated MSVC compiler environment INFO: autodetecting backend as ninja INFO: calculating backend command to run: C:\Users\myuser\anaconda3\envs\mesonenv\Library\bin\ninja.EXE -C C:/Users/szl/source/repos/win32/build ninja: Entering directory `C:/Users/myuser/source/repos/win32/build' [0/1] Regenerating build files. The Meson build system Version: 1.0.1 Source dir: C:\Users\myuser\source\repos\win32 Build dir: C:\Users\myuser\source\repos\win32\build Build type: native build Project name: myapp Project version: undefined C++ compiler for the host machine: cl (msvc 19.35.32217.1 "Microsoft(R) C/C++ Optimizing Compiler Version 19.35.32217.1 for x64") C++ linker for the host machine: link link 14.35.32217.1 Host machine cpu family: x86_64 Host machine cpu: x86_64 Build targets in project: 1 Found ninja-1.10.2 at C:\Users\myuser\anaconda3\envs\mesonenv\Library\bin\ninja.EXE Cleaning... 0 files. [1/1] Linking target myapp.exe
#include<windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; switch (msg) { case WM_DESTROY: PostQuitMessage(0); 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-WIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WIN"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
個人的注意点
・find_libraryの第一引数はライブラリファイル名だが、.libはつけない
・gui_app: true を必ずつける
project('myapp', 'cpp') wxwidgets_incdir = include_directories( 'C:/libraries/wxWidgets/include', 'C:/libraries/wxWidgets/release/lib/vc_x64_dll/mswu' ) wxwidgets_libdir = 'C:/libraries/wxWidgets/release/lib/vc_x64_dll' cpp = meson.get_compiler('cpp') executable('myapp', 'main.cpp', gui_app: true, dependencies: [ cpp.find_library('wxbase32u' , dirs: wxwidgets_libdir), cpp.find_library('wxbase32u_net' , dirs: wxwidgets_libdir), cpp.find_library('wxbase32u_xml' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_adv' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_aui' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_core' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_gl' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_html' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_media' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_propgrid', dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_qa' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_ribbon' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_richtext', dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_stc' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_webview' , dirs: wxwidgets_libdir), cpp.find_library('wxmsw32u_xrc' , dirs: wxwidgets_libdir), ], include_directories: wxwidgets_incdir, cpp_args: [ '/D__WXMSW__' , # windows は /D '/DWXUSINGDLL' # linux系 は -D ], link_args: [ '/SUBSYSTEM:WINDOWS' ] )
// プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/frame.h> // wxFrameに必要 ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 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);
調べていて思ったこと。
ユーザー定義リテラルは、リテラルの末尾につけるサフィックスを自分で定義する機能。「1.0f」のfの部分を自分で定義できる。
あくまでリテラルにつけるものなので、変数等には使えない。
operator"" の後にサフィックスを与える。 「_」開始でなくてもビルドは通るが、必ず先頭に「_」をつけなければいけないらしい。
以下のような定義例がしばしばみられる。
#include <iostream> // 名前空間を定義することで、これが有効な範囲でだけ使用できる namespace suffix { long double operator"" _k(const long double value) { return value * pow(10, 3); } long double operator"" _h(const long double value) { return value * pow(10, 2); } long double operator"" _da(const long double value) { return value * pow(10, 1); } /* 10^0 に接頭語はない */ long double operator"" _d(const long double value) { return value / pow(10, 1); } long double operator"" _c(const long double value) { return value / pow(10, 2); } long double operator"" _m(const long double value) { return value / pow(10, 3); } } int main() { using namespace suffix; // 1.0 × 10^3 + 2 × 10^(-3) auto length = 1.0_k + 2.0_m; printf("%lf\n", length); }
しかし何か違う気がする。
C++には主に数値系で精度や範囲などを指定するsuffix(f,LLなど)と、文字列系で文字コードを指定するprefix(u8"hello"など)があるわけだが、両方とも定数の解釈方法を指定しているだけで、別にその定数に対して演算をしているのとは違う(気がする)。
だからどちらかというと以下のような使い方のほうが本来の用途に近いのでは...?と思う。
class Radian { double value; public: Radian(const Radian& rad) { value = rad.value; } Radian(double value) { this->value = value; } }; class Degree { double value; public: Degree(const Degree& deg) { value = deg.value; } Degree(double value) { this->value = value; } }; namespace suffix { Radian operator"" _rad(const long double value) { return Radian( value ); } Degree operator"" _deg(const long double value) { return Degree( value ); } } int main() { using namespace suffix; Radian rad = 1.0_rad; Degree deg = 1.0_deg; // エラー // Radian rad2 = 1.0_deg; }
つまりキャストのようなものだ。しかしこれでは rad=1.0;としても通ってしまう。別に間違ってはいないがせっかく使うならミスを減らせるような機能にしたい。
というわけで以下のようにしてみる。これで単位が不明な浮動小数点をうっかり代入してしまうのを防ぐことができた。
#include <iostream> class Radian; class Degree; namespace suffix { Radian operator"" _rad(const long double value); Degree operator"" _deg(const long double value); } class Radian { double value; private: Radian(double value) { this->value = value; }// ユーザー定義リテラルからしか呼び出せないようにする public: Radian(const Radian& rad) { value = rad.value; } // ユーザー定義リテラルがコンストラクタへアクセスするために必要 friend Radian suffix::operator"" _rad(const long double value); operator double()const { return value; } }; class Degree { double value; private: Degree(double value) { this->value = value; }// ユーザー定義リテラルからしか呼び出せないようにする public: Degree(const Degree& deg) { value = deg.value; } // ユーザー定義リテラルがコンストラクタへアクセスするために必要 friend Degree suffix::operator"" _deg(const long double value); operator double()const { return value; } }; namespace suffix { Radian operator"" _rad(const long double value) { return Radian( value ); } Degree operator"" _deg(const long double value) { return Degree( value ); } } int main() { using namespace suffix; Radian rad = 2.5_rad; Degree deg = 1.0_deg; // エラー // Radian rad2 = 1.0_deg; // これもエラー //rad = 1.0; rad = 3.0_rad; std::cout << rad << std::endl; }
キャストしろ。
OpenAI APIというよりPythonがわからない話。
function callingは、「こんな関数を用意したけど『???』という質問に回答するためには、どの関数を呼び出せばよい?」という問いに、「関数名と引数で答えてくれる」機能である。
この、「こんな関数を用意した」という部分、用意した関数についての説明をOpenAI APIに渡すためのリストfunctionsパラメータについて。
私はPython素人なので、functions = [{...}]の部分について調べた。
以下のようなプログラムで見る。
import openai # OpenAI API への送信と、その結果の受信は、json形式で行われる import json ############################################################### import os import re
############################### # 処理に必要な関数を用意 ############################### # @brief Google検索する # @param keyword 検索キーワード # @param datefrom 記事の更新日時範囲 # @param dateto 記事の更新日時範囲 def getGoogleSearch(keyword,datefrom,dateto): # 本当はここに関数の処理を書くのだが、今回はOpenAI APIの返答のためのサンプルなので実装しない # function callingを呼び出すだけなら別に定義の必要もないわけだが格好がつかないので一応。 pass # @brief 決められたサイトからニュースを取得する # @param newssite サイトのURL # @param count 取得する結果の件数 def getNews(newssite,count): # こちらも同じ pass # @brief 自分のハードディスクからキーワードを持つファイルを検索する # @param keyword 検索ワード # @param extension 拡張子 def searchDocument(keyword,extention): # こちらも同じ pass
############################### # どんな関数を用意したかをOpenAI APIに教えるためのリスト。今回知らせる関数は三つ。 ############################### functions = [ { # 用意した関数の名前 "name": "getGoogleSearch", # 関数の説明 "description": "Google検索する", # パラメータ一覧 "parameters": { "type": "object", "properties": { "keyword" : { "type": "string", "description": "検索キーワード。スペース区切りで複数可" }, "datefrom" : { "type": "string", "description": "記事の更新日の範囲の古いほう。yyyy/mm/ddで指定" }, "dateto": { "type": "string", "description": "記事の更新日の範囲の新しいほう。yyyy/mm/ddで指定" }, }, # 必須パラメータ "required": ["keyword"], }, }, ############################################### { # 用意した関数の名前 "name": "getNews", # 関数の説明 "description": "決められたサイトからニュースを取得する", # パラメータ一覧 "parameters": { "type": "object", "properties": { "newssite" : { "type": "string", "description": "サイトのURL" }, "count" : { "type": "integer", "description": "取得する結果の件数" }, }, # 必須パラメータ "required": ["newssite","count"], }, }, ############################################### { # 用意した関数の名前 "name": "searchDocument", # 関数の説明 "description": "自分のハードディスクからキーワードを持つファイルを検索する", # パラメータ一覧 "parameters": { "type": "object", "properties": { "keyword" : { "type": "string", "description": "検索するキーワード" }, "extention" : { "type": "string", "description": "拡張子" }, }, # 必須パラメータ "required": ["keyword","extention"], }, } ]
############################### # OpenAIに何かしら問いかける ############################### # APIキー openai.api_key = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 問いかけの内容 ask_get_allitem = [ { "role": "user", "content": "飲み会の時の画像を探してください。田中さんが写っています。" } ] # OpenAIに送信 response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=ask_get_allitem, functions=functions, ) # 結果を取得 res_message = response['choices'][0]['message']
############################### # OpenAIからの返答を確認する ############################### # もし、OpenAIが、この応答には関数呼び出しが必要と判断したら、 # res_message['function_call'] に、関数呼び出しに必要な情報が入っている if res_message.get('function_call'): # OpenAIが処理にふさわしいと判断した関数の名前 function_name = res_message['function_call']['name'] # OpenAIが処理にふさわしいと判断した関数の引数 arguments = json.loads(res_message['function_call']['arguments']) # 何を呼び出すべきと判断したか、確認のために表示 print("function_name: ", function_name) print("arguments: ", arguments) else: # OpenAIが関数呼び出しをしないと判断した場合、messageの中身をそのまま表示する print(response.choices[0]["message"]["content"].strip())
functionsは辞書のリストで表す。リストは [項目1, 項目2 , 項目3]の形をしている。つまり
functions=[ 関数1について , 関数2について , 関数3について }
という形をしている。で、関数1について、これは辞書になっている。辞書はkeyとvalueのペアを一つの項目とし、ペアを複数持つ。
keyとvalueのペアは、key : valueの形で持ち、それらを{}でまとめて一つの辞書にする。つまり
dic = { key1 : value1 , # アイテム1 key2 : value2 , # アイテム2 key3 : value3 , # アイテム3 }
という形をしている。
結果、各関数に関する情報を辞書としてまとめ、それをリストとしたものをfunctionsに渡すということになる。
functions = [ # 関数1に関する情報を辞書で指定 { "name" : "function1", # keyは"name" , valueは"function1" "description" : "関数1の説明", # keyは"description" , valueは"関数1の説明" }, # 関数2に関する情報を辞書で指定 { # 略 }, # 関数3に関する情報を辞書で指定 { # 略 } ] # こんな風にアクセス print( functions[0]['name'] )
Blenderで作ったモデルをUE5に移行するにはfbx経由で行う。
まずUV展開し、テクスチャをロードしマテリアルとして設定する。
エクスポートする際に、Selected Objectsを選択。あるいは、Meshだけを選択する。このあたりの設定をしないと、ライトなどもエクスポートされてしまう。
ファイル→レベルにインポート。特に気を付けるところはない。
「コンテンツ」の中に、マテリアルとSphereが追加されている。
wxWidgetsのwxScrollBarの使用例。これはスクロールバーのみのコントロールになる。
スクロールバーとしての設定はSetScrollbar関数で行う。
https://docs.wxwidgets.org/3.0/classwx_scroll_bar.html#ae69c239fd6af4ebcabf46efa9fc5092e
#include <wx/wx.h> #include <wx/sizer.h> #include <wx/frame.h> #include <wx/panel.h> class MyFrame : public wxFrame { std::vector<int> _itemlist; int row = 3; int col = 1; int itemcount() { return _itemlist.size(); } wxScrollBar* _scroll; wxGridSizer* _gridSizer; public:
MyFrame() : wxFrame(NULL, wxID_ANY, "wxGridSizer サンプル", wxDefaultPosition, wxSize(400, 300)) { for (size_t i = 0; i < 17; i++) { _itemlist.push_back(i); } int vgap = 2;// 垂直方向の間隔 int hgap = 2;// 水平方向の間隔 _gridSizer = new wxGridSizer(row, col, vgap, hgap); for (int i = 0; i < row*col; ++i) { //// 新しいパネルを作成 wxPanel* panel = new wxPanel(this/*MyFrameで管理*/, wxID_ANY); //// パネルの背景色を設定 panel->SetBackgroundColour(wxColour(200, 200, 100 * (i + 1) % 256)); //// パネルをgridSizer追加 _gridSizer->Add(panel, 1, wxEXPAND); // パネルにラベルを追加 wxStaticText* label = new wxStaticText(panel, wxID_ANY, wxString::Format("Panel %d", i)); wxBoxSizer* boxSizer = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(label, 1, wxALIGN_CENTER); panel->SetSizer(boxSizer); } // 水平分割のSizer作成 wxBoxSizer* boxSizer = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(_gridSizer, 1, wxEXPAND); //スクロールバー作成 _scroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL); boxSizer->Add(_scroll, 0, wxEXPAND); // OnScrollをscrollにBind _scroll->Bind(wxEVT_SCROLL_CHANGED, &MyFrame::OnScroll, this); _scroll->SetScrollbar( 0, // position スクロールバーの位置 row, // thumbSize つまみのサイズ。これは一度に画面上に表示する行数と等しいことが望ましい itemcount(), // range 総行数 2, // pageSize ページ単位でのスクロールを行ったときに移動する量 true // refresh trueにするとスクロールバーの位置を更新する ); // 初期表示を行う panel_update(); // MyFrameにgridSizerを指定 SetSizer(boxSizer); Centre();// ウィンドウを画面中央に表示 }
// wxScrollBarをクリックしたときに呼び出されるイベントハンドラ void OnScroll(wxScrollEvent& event) { // スクロールバーの位置を取得 int pos = event.GetPosition(); // pos をデバッグ出力 wxLogDebug(wxString::Format("pos = %d", pos)); panel_update(); }
// panelの内容を更新する void panel_update() { // _scroll の位置を取得 int pos = _scroll->GetThumbPosition(); // 一行ずつスクロールしたいので、 // 一番左上のパネルに表示するアイテム番号を計算 int offset = pos * col; for (int i = 0; i < row * col; ++i) { // _gridSizerに登録されているpanelを取得 wxPanel* panel = (wxPanel*)_gridSizer->GetItem(i)->GetWindow(); // panel上にあるlabelを取得 wxStaticText* label = (wxStaticText*)panel->GetChildren()[0]; if ((offset + i) >= itemcount()) { // アイテム数を超えたら *** を表示 label->SetLabelText("***"); } else { if (offset + i == itemcount()-1) { // 最後のアイテムなら (last item) を表示 label->SetLabelText(wxString::Format("Panel %d (last item)", _itemlist[offset + i])); } else { // それ以外はアイテム番号を表示 label->SetLabelText(wxString::Format("Panel %d", _itemlist[offset + i])); } } } }
}; class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame(); frame->Show(true); return true; } }; wxIMPLEMENT_APP(MyApp);
wxGridSizerを使うとコントロールをタイル状に並べられる。
#include <wx/wx.h> #include <wx/sizer.h> #include <wx/frame.h> #include <wx/panel.h> class MyFrame : public wxFrame { public:
MyFrame() : wxFrame(NULL, wxID_ANY, "wxGridSizer サンプル", wxDefaultPosition, wxSize(400, 300)) { int row = 3; // 行数 int col = 4; // 列数 int vgap = 2;// 垂直方向の間隔 int hgap = 2;// 水平方向の間隔 wxGridSizer* gridSizer = new wxGridSizer(row, col, vgap, hgap); for (int i = 0; i < row*col; ++i) { // 新しいパネルを作成 wxPanel* panel = new wxPanel(this/*MyFrameで管理*/, wxID_ANY); // パネルの背景色を設定 panel->SetBackgroundColour(wxColour(100, 100, 100 * (i + 1) % 256)); // パネルをgridSizer追加 gridSizer->Add(panel, 1, wxEXPAND); // パネルにイベントハンドラを設定 panel->Bind(wxEVT_LEFT_DOWN, &MyFrame::OnClick, this); } // MyFrameにgridSizerを指定 SetSizer(gridSizer); Centre();// ウィンドウを画面中央に表示 }
// wxPanelをクリックしたときのイベントハンドラ void OnClick(wxMouseEvent& event) { wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject()); if (panel) { wxColour color = panel->GetBackgroundColour(); wxLogMessage("%d", color.Blue()); } }
}; class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame(); frame->Show(true); return true; } }; wxIMPLEMENT_APP(MyApp);
// wxPanelをクリックしたときのイベントハンドラ void OnClick(wxMouseEvent& event) { wxGridSizer * gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); // gridSizerの全ての要素へアクセスするfor文 for (int i = 0; i < gridSizer->GetItemCount(); ++i) { wxSizerItemList& item = gridSizer->GetChildren(); wxWindow* panel = item.Item(i)->GetData()->GetWindow(); panel->SetBackgroundColour(wxColour(255, 0, 0));// パネルを着色 panel->Refresh(); // 再描画指示 panel->Update(); // 再描画を即座に実行 } }
まず、MyFrameのコンストラクタでメニューを作成して、OnClickにBindする。
// 画面上部にメニューバーを作成 wxMenuBar* menuBar = new wxMenuBar(); wxMenu* menu = new wxMenu(); menu->Append(wxID_DELETE, "削除"); menuBar->Append(menu, "メニュー"); SetMenuBar(menuBar); Bind(wxEVT_MENU, &MyFrame::OnClick, this, wxID_DELETE);
Sizerの削除は要素の管理関係が厄介。管理自体は親(MyFrame)がしているのだが、Sizerを外したりdeleteしたりすると一緒に削除されたり、削除されたことが親に知らされずに不正なアクセスになったりする。
まずはGetChildren()でSizerでサイズ管理しているアイテム一覧を取り出し、個別に削除してからSizerを削除することになる。
// 「削除」メニューをクリックしたときのイベントハンドラ void OnClick(wxCommandEvent& event) { wxGridSizer* gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); if (gridSizer) { // gridSizer->Clear(true); // gridSizerから全ての要素を削除する // trueを指定した場合、全ての子要素は削除されるが、親がそれを知らないので後で知らせる必要がある // falseを指定した場合、子要素は削除されないので、後で削除する必要がある // 使いにくいので今回は使用しない // gridSizerから全てのウィジェットを取り出し、破棄する const wxSizerItemList list = gridSizer->GetChildren(); for (wxSizerItem* item : list) { wxWindow* window = item->GetWindow();// パネルを取得 if (window) { window->Destroy();// パネルを削除 delete ではなくDestroy()を呼び出すべき } } // MyFrameからgridSizerを取り外す // 第二引数がtrueのときは、現在のSizerを削除する // 第二引数がfalseのときは、削除しない this->SetSizer(nullptr,true); } }
パネルだけをほかに移行するような場合、パネルのポインタを保存しておき、MyFrame側のSetSizerの第二引数にfalseを渡すと、gridSizerが勝手に削除されなくなるので、後で自分でgridSizerだけを削除する。
// 「削除」メニューをクリックしたときのイベントハンドラ void OnClick(wxCommandEvent& event) { wxGridSizer* gridSizer = dynamic_cast<wxGridSizer*>(this->GetSizer()); if (gridSizer) { std::vector<wxWindow*> panels; // パネルのバックアップ先 // gridSizerから全てのウィジェットを取り出し、破棄する const wxSizerItemList list = gridSizer->GetChildren(); for (wxSizerItem* item : list) { wxWindow* window = item->GetWindow(); if (window) { panels.push_back(window);// パネルをバックアップ //window->Destroy(); // 削除はしないようにする } } // 第二引数がfalseのときは、現在のSizerを削除しない this->SetSizer(nullptr,false); }
}