http://suzulang.com/category/programming/sourcecode_others/
このページは元々しないか、URLが変更されたか、または削除されています。
wxWidgetsで.xrcファイルからデザインを読み込んで適用(1) xrcファイルからGUI作成 [ Updated]
wxWidgetsで画面デザインを定義する.xrcファイルを読み込んでGUIを作成する。
<?xml version="1.0" ?> <resource version="2.3.0.1"> <!-- トップレベルはコントロールである必要がある。パネルを作成。 --> <object class="wxPanel" name="myMainPanel">
<!-- レイアウト用のサイザーを作成 --> <object class="wxBoxSizer" name="myBoxSizer"> <orient>wxVERTICAL</orient> <!-- レイアウト方法の指定 -->
<!-- コントロールは sizeritem に入れる --> <object class="sizeritem"> <!-- 左側と上側に15pxの余白を入れる --> <flag>wxLEFT|wxTOP</flag> <border>15</border> <!-- このコントロールはサイザーのサイズ変更で大きさが変わらない --> <option>0</option>
<!-- 名前 myButton を追加 --> <object class="wxButton" name="myButton"> <label>ボタン</label> <size>100,30</size> </object>
</object>
<!-- コントロールは sizeritem に入れる --> <object class="sizeritem"> <!-- 上下左右の余白は15px --> <flag>wxALL|wxEXPAND</flag> <border>15</border> <!-- このコントロールはサイザーのサイズ変更で大きさが変わる --> <option>1</option>
<object class="wxTextCtrl" name="mytext1"> <label>ボタン</label> <size>100,30</size> <!-- サイザーが有効なのでサイズは無視される --> </object>
</object>
</object>
</object> </resource>
// 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に必要 #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") ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <wx/xrc/xmlres.h> // ウィンドウ作成 class MyFrame : public wxFrame { public:
void PostCreate() { wxXmlResource::Get()->InitAllHandlers();// 初期化 wxXmlResource::Get()->Load(R"(C:\test\layout.xrc)"); // ファイル読み込み wxXmlResource::Get()->LoadPanel(this, "myMainPanel"); // GUIの作成 this->Layout(); // レイアウトの更新 }
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // 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);
パネルにはLoadPanelがあるが、すべてのコントロールにLoad***が用意されているわけではない。LoadObject関数を使って、部品名とその部品のクラスを指定して読み込む。
<?xml version="1.0" ?> <resource version="2.3.0.1"> <!-- 名前 myButton を追加 --> <object class="wxButton" name="myButton"> <label>ボタン</label> <size>300,30</size> </object> </resource>
void PostCreate() { wxXmlResource::Get()->InitAllHandlers();// 初期化 wxXmlResource::Get()->Load(R"(C:\test\button.xrc)"); // ファイル読み込み wxWindow* btn = (wxWindow*)wxXmlResource::Get()->LoadObject(this, "myButton"/*識別子*/, "wxButton"/*クラス*/); // boxsizerの追加 wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); this->SetSizer(sizer); sizer->Add(btn, 0, wxALL, 10); this->Layout(); // レイアウトの更新 }
原則として、メモリ上のテキストを読み込む方法はない。しかし、wxWidgetsにはメモリ上に仮想的なファイルを作成する機能があるので、これを使って仮想的な.xrcファイルを作れば実現できる。
仮想ファイルを作るために、fs_mem.h が必要。
#include <wx/fs_mem.h>
void PostCreate() { wxXmlResource::Get()->InitAllHandlers(); wxString layout_xrc =
R"(<?xml version="1.0" ?> <resource version="2.3.0.1"> <object class="wxPanel" name="myPanel"> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <object class="wxTextCtrl" name="text"/> <option>0</option> <size>100,30</size> <flag>wxALL</flag> <border>10</border> </object> </object> </object> </resource> )"
; wxFileSystem::AddHandler(new wxMemoryFSHandler); wxMemoryFSHandler::AddFile("myxrc", layout_xrc.GetData().AsWChar(), layout_xrc.size()*2); wxXmlResource::Get()->Load("memory:myxrc"); wxWindow* mysizer = wxXmlResource::Get()->LoadPanel(this, "myPanel"); this->Layout(); // レイアウトの更新 }
VC++で生成した.exeファイル内のタイムスタンプ部分を確認して無理やり書き換えてみた。 [ Updated]
諸事情により簡易的でよいので.exeファイルの解析をしたくなった。.exeファイルをバイナリエディタで見てみて、全く同じプロジェクトを二回以上ビルドした結果が完全一致しない。理由の一つがタイムスタンプなので、これをいじってみる。
VC++でコンソールアプリケーションを作成する。この時、余計なものが埋め込まれるとそれだけビルド時の変化が大きくなる可能性があるので、わかる範囲で不要なオプションを取り除く。
・.pdbファイルを出力しない
プロジェクトのプロパティ → リンカー → デバッグ → デバッグ情報の生成 → いいえ
・マニフェストを埋め込まない
プロジェクトのプロパティ → リンカー → マニフェスト ファイル → マニフェストの生成 → いいえ
・ASLRをしない(Address Space Layout Randomization)
ASLRはexe内のデータの位置をランダムにするクラッキング対策のオプションで、多分悪影響を与えるので切っておく。/DYNAMICBASE:NOを設定。
プロジェクトのプロパティ → リンカー → 詳細設定 → ランダム化されたベースアドレス → いいえ
まずビルドし、上書きされないように.exeファイルの名前を変える。それからもう一度ビルドする。
右クリック→プロパティ→詳細 で更新日時を確認。
実際にはここの表示を変えるわけではないのだが、バイナリ中の値を解析してこの日付とおおむね同じものが出てくれば正解だとわかるので参考にする。
WinMergeで二つの.exeをバイナリ比較する。手元のバージョンでは、CompareボタンのプルダウンにBinary比較がある。
一部違う場所が出てくるので、その右4バイトに着目する。時間を置かずにビルドすると、数秒しか違わないはずなので、違いはせいぜい下1バイトになる可能性が高い。intelのCPUはリトルエンディアンなので、バイトの並び順が逆になっている。従って変化している場所を含めて右に4バイトがタイムスタンプとなる。
上記で得られた値 0x65abe167 が実際にビルド時刻であることを確認するため、以下のプログラムを書いて確認する。
#include <ctime> #include <string> #include <iostream> #pragma warning(disable:4996) // タイムスタンプから時刻表示を作成 std::string TimestampToDate(time_t timestamp) { struct tm* tm = localtime(×tamp);// 整数値 timestamp を tm 構造体に変換 char str[50]; strftime(str, sizeof(str), "%Y/%m/%d %H:%M:%S", tm);// tmを文字列化 return std::string(str); } int main() { // タイムスタンプ(16進数指定) // バイナリに埋め込まれている値はリトルエンディアンなので // バイナリエディタ上の表示とバイト単位で逆順にして入力する time_t timestamp = 0x65abe167; std::cout << TimestampToDate(timestamp) << std::endl; return 0; }
結果
確かにファイルのプロパティ上の表示と一致する内容を得られたので、この数値がタイムスタンプであることは理解できた。今度はこれを書き換えてみる。
まず、適当な日付をタイムスタンプに変換するため、以下のプログラムを書いて走らせる。
#include <ctime> #include <string> #include <iostream> #pragma warning(disable:4996) // 時刻からタイムスタンプを作成 time_t DateToTimestamp(const std::string date) { struct tm tm = {}; int year, month, day; int hour, minute, second; sscanf(date.c_str(), "%d/%d/%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second ); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = minute; tm.tm_sec = second; return mktime(&tm); } int main() { std::string date_str = "2022/01/01 5:25:30"; // 適当な日付 unsigned int timestamp = DateToTimestamp(date_str); printf("%x \n", timestamp); }
結果
こうして得られたタイムスタンプを、バイナリエディタで書き込む。書き込みにはStirlingを使っている。入力時、リトルエンディアンなのでバイト単位で逆順に指定する。
タイムスタンプはビルド時の色々なステップで入れられるらしいので、同じ値を見つけたらすべて書き換える。
先ほどはファイルのプロパティから確認したが、この日付はファイルのプロパティには実は表示されないので、PEヘッダを読むツールで確認する。探したところPEviewが適している。
以下からPEview version 0.9.9 ( .zip 31KB )をダウンロードする。
http://wjradburn.com/software/
wxWebView上で実行したTinyMCEの結果を取得する [ Updated]
wxWebView上でTinyMCEを動かし、その編集内容をC++側に取得する。
これを応用すればHTMLエディタ的なものが作れる。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script> <script>
// TinyMCEから入力内容を取得する関数 function MyGetContent(){ var myContent = tinymce.get("myTinyMCEarea").getContent(); return myContent; }
</script> </head> <body> <!-- TinyMCE本体 --> <textarea id="myTinyMCEarea">my text</textarea> </body> </html>
wxWebView::RunScriptを使用すれば、読み込んでいるページのJavaScriptを実行し、結果を取得できる。これを応用し、my_test.html側にMyGetContent()関数を作成してから、RunScriptをして、取得した結果をメッセージボックスで表示する。
// 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に必要 #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") // wxWebViewを使うために必要 #include <wx/webview.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { wxWebView* webView; public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // wxWebView インスタンスの作成 webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxWebViewBackendEdge, 0, "" ); webView->LoadURL(R"(C:\test\data\tinymce\my_test.html)");// TinyMCEを使うHTML読み込み // ボタンを作成し、sizerに追加 wxButton* button = new wxButton(this, wxID_ANY, wxT("Run Script")); sizer->Add(webView, 1, wxEXPAND, 0); sizer->Add(button, 0, wxALIGN_CENTER | wxALL, 10); SetSizer(sizer); // ボタンのイベントをバインド button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this); } // ボタンクリックイベントのハンドラ void OnButtonClicked(wxCommandEvent& event) { if (webView) {
wxString jsCode = wxT(R"(MyGetContent();)"); wxString ret; webView->RunScript(jsCode, &ret);// my_test.html に定義したJavaScriptのfunction呼び出し wxMessageBox(ret);
} } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // 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);
TinyMCEを使ってみる [ Updated]
TinyMCEはブラウザ上で使えるHTMLのWYSIWYGエディタ。
公式ページ:
サイトの最下部付近にある GET TinyMCE FREE から、「TinyMCE Open Source Community」 内の 「Download TinyMCE Community」をクリックしてダウンロード開始。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script> </head> <body> <!-- TinyMCE入力エリア --> <textarea>first text</textarea> </body> </html>
C:\test\data │ └─tinymce │ CHANGELOG.md │ my_test.html │ └─js └─tinymce ├─icons ├─langs ├─models ├─plugins ├─skins └─themes
my_test.htmlをFirefoxなどのブラウザで開くと、エディタを使用できる。
JavaScriptで、入力したテキストを取得する。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script>
<script> // TinyMCEから入力内容を取得する関数 function MyGetContent(){ var myContent = tinymce.get("myTinyMCEarea").getContent(); return myContent; } // ボタンが押された時に実行する関数。 // TinyMCEからテキストを取得し、MyCheckAreaに表示する。 function MyButtonClick(){ document.getElementById("MyCheckArea").innerHTML = MyGetContent(); } </script>
</head> <body> <!-- TinyMCE本体 --> <textarea id="myTinyMCEarea">first text</textarea> <!-- 内容取得確認用ボタン --> <button id="showContent" onclick="MyButtonClick()" >Show Content</button> <!-- 取得した内容の表示領域 --> <div id="MyCheckArea"></div> </body> </html>
公式ドキュメントよりも詳しいTinyMCEの使い方(基本編)
公式ドキュメントよりも詳しいTinyMCEの使い方(基本編)
wxWidgetsのwxWebViewを使ってWebサイトを表示してみる [ Updated]
wxWebViewを使うとWebサイトを簡単に表示できる。問題はレンダリングエンジンの指定に注意が必要。
// 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に必要 #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") // wxWebViewを使うために必要 #include <wx/webview.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // wxWebView インスタンスの作成 wxWebView* webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, // レンダリングエンジンの指定 // wxWebViewBackendWebKit, // wxWebViewBackendEdge, // wxWebViewBackendIE, wxWebViewBackendDefault, 0, "" ); webView->LoadURL("https://www.suzulang.com/"); } 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);
少なくともWindowsでレイアウトが崩れる理由は、wxWebViewBackendDefaultの指定だとIEになってしまうからで(’明示的に指定する場合はwxWebViewBackendIE)、これをEdgeにしなければならない。
wxWebViewBackendEdgeを指定する。
ただし、これを指定してエラーになったり、何も表示されない場合は、wxWidgetsをCMakeする際にwxUSE_WEBVIEW_EDGE,wxUSE_WEBVIEW_EDGE_STATICを含めて再ビルドする必要がある。
なお、どうもWindowsではwxWebViewBackendWebKitは動かないらしい。とはいえEdgeは中身がChromiumなのでwxWebViewBackendEdgeで大抵のページは表示される(に違いない)。
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // wxWebView インスタンスの作成 wxWebView* webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, // レンダリングエンジンの指定 // wxWebViewBackendWebKit, wxWebViewBackendEdge, // wxWebViewBackendIE, //wxWebViewBackendDefault, 0, "" ); webView->LoadURL("https://www.suzulang.com/"); }
ファイルパスを指定して、ローカルにあるhtmlを読み込める。
webView->LoadURL("C:/test/local.html");
LoadURLの代わりにSetPageを使用すれば、メモリ上のHTMLを表示できる。
//webView->LoadURL("C:/test/local.html"); webView->SetPage( R"( <html> <head></head> <body><p style="font-size:5em;">テスト</p></body> </html> )" , "");