タスクトレイにアイコンを表示する方法
① アイコンの表示を開始したいタイミングで、Shell_NotifyIcon関数をNIM_ADDで呼び出す
② メッセージループに「タスクトレイのアイコンにアクションがあった」という単位でメッセージが届くので、処理する
③ アイコンを消したいタイミングで、Shell_NotifyIcon関数をNIM_DELETEで呼び出す
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { static const decltype(WM_USER) WM_NOTIFYICON = WM_USER + 100; static const int IconID = 100; switch (msg) { case WM_LBUTTONDOWN://ウィンドウをクリックしたらアイコン表示開始 { NOTIFYICONDATA nid; nid.cbSize = sizeof(nid); nid.hWnd = hwnd; nid.uID = IconID;//複数のアイコンを表示したときの識別ID。なんでもいい。 nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uCallbackMessage = WM_NOTIFYICON; //WM_USER以降の定数。 nid.hIcon = (HICON)LoadIcon(NULL, IDI_APPLICATION); _tcscpy(nid.szTip, _T("タスクトレイで表示する文字列")); int ret = (int)Shell_NotifyIcon(NIM_ADD, &nid); ShowWindow(hwnd, SW_HIDE); } break; case WM_NOTIFYICON: if (wp == IconID && lp == WM_LBUTTONDOWN) { //アイコンに対する操作がWM_LBUTTONDOWNでアイコンのIDが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); //////////////////////////////////////// } break; case WM_DESTROY: DestroyWindow(hwnd); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
printfデバッグは便利だが、コンソールを開かなければいけないという欠点がある。
デバッガに出力するならOutputDebugStringという関数があるが、書式指定が使えないので数字の出力には一手間かかる。
_RPT0 , _RPT1 , _RPT2 , _RPT3 , _RPT4
というデバッグ用のマクロがあり、これをつかえばprintfのように書けるのだが、引数の数が決まっていてやや不便。
https://msdn.microsoft.com/ja-jp/library/ms350217(v=vs.71).aspx
_RPTn使用例:
#include <crtdbg.h> void func(int input1,double input2) { _RPT2( _CRT_WARN, "func inputs = %d , %lf \n", input1, input2 ); }
printf(そして可変長引数)は危険なので、デバッグしようとしてバグを増やしてしまっては本末転倒と考えれば、この仕様は合理的だ。しかしそれでもprintf形式が恋しくなることがあるので、自作する。
自作DebugPrintf
void DebugPrintf(LPCTSTR pszFormat, ...) { va_list argp; TCHAR pszBuf[256]; va_start(argp, pszFormat); _vstprintf(pszBuf, pszFormat, argp); va_end(argp); OutputDebugString(pszBuf); }
可変長引数をとる関数を作りそれをそのまま_vstprintfへ流し、OutputDebugStringでデバッガへ出力。ここでは文字数の上限が256となっているが、リスクを承知でやっているのだからこの点は自己責任となる。
C#のサンプルは沢山あるがCLIで実行できずに困ったのでここに書いておく。
下記は、要素数10のdoubleの配列aの内容を、0~9の1.5倍の値で初期化する。
using namespace System; typedef double datatype_t; ref struct mystruct{ array<datatype_t>^ m_a; public: mystruct(array<datatype_t>^ dst){ m_a = dst; } void function(int number){ m_a[ number] = number*1.5; } }; int main(array<System::String ^> ^args) { array<datatype_t>^ a = gcnew array<datatype_t>(10); mystruct^ plfor = gcnew mystruct(a); System::Threading::Tasks::Parallel::For( 0, a->Length, gcnew Action<int>(plfor, &mystruct::function) ); for(int i= 0 ;i < a->Length;i++){ System::Console::WriteLine( System::String::Format("a[{0}] = {1}",i,a[i])); } Console::ReadLine(); return 0; }
ウィンドウプロシージャをメンバ関数にしたい。しかしWNDCLASSEXのlpfnWndProcに指定できるのは普通の関数かstaticメンバ関数のみ。
しかし一工夫することで、ウィンドウをカプセル化することができる。コードは以下。
(ソースコードをまとめたファイル:CnuWindow.zip )
CnuWindow.h
#pragma once #include <windows.h> class CnuWindow { TCHAR WINDOW_CLASS_NAME[1024]; static LRESULT CALLBACK StaticWndProc( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); private: HWND m_hwnd; HINSTANCE m_hInstance; public: CnuWindow(); virtual ~CnuWindow(); //! @brief ウィンドウ定義用メンバ関数 bool RegistWindow( const TCHAR* window_class_name, const HINSTANCE hInst ); //! @brief ウィンドウプロシージャ virtual LRESULT WndProc( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp ); HWND GetHWnd()const { return m_hwnd; } HINSTANCE GetHInstance()const { return m_hInstance; } //! @brief ウィンドウ作成用メンバ関数 bool nuCreateWindow( LPCTSTR lpWindowName, // ウィンドウ名 DWORD dwStyle, // ウィンドウスタイル int x, // ウィンドウの横方向の位置 int y, // ウィンドウの縦方向の位置 int nWidth, // ウィンドウの幅 int nHeight, // ウィンドウの高さ HWND hWndParent, // 親ウィンドウまたはオーナーウィンドウのハンドル HMENU hMenu // メニューハンドルまたは子ウィンドウ ID ); };
CnuWindow.cpp
#include "CnuWindow.h" #include <windows.h> #include <tchar.h> CnuWindow::CnuWindow() { } CnuWindow::~CnuWindow() { } // ウィンドウを作成する bool CnuWindow::RegistWindow(const TCHAR* window_class_name, const HINSTANCE hInst) { _tcscpy_s(WINDOW_CLASS_NAME, window_class_name); m_hInstance = hInst; WNDCLASSEX wc; // ウィンドウクラスの情報を設定 wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = CnuWindow::StaticWndProc; // ウィンドウプロシージャ wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; // インスタンスハンドル wc.hIcon = (HICON)LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = wc.hIcon; wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = WINDOW_CLASS_NAME; // ウィンドウクラス名 // ウィンドウクラスを登録する if (RegisterClassEx(&wc) == 0) { return false; } return true; } bool CnuWindow::nuCreateWindow( LPCTSTR lpWindowTitle, // ウィンドウ名 DWORD dwStyle, // ウィンドウスタイル int x, // ウィンドウの横方向の位置 int y, // ウィンドウの縦方向の位置 int nWidth, // ウィンドウの幅 int nHeight, // ウィンドウの高さ HWND hWndParent, // 親ウィンドウまたはオーナーウィンドウのハンドル HMENU hMenu // メニューハンドルまたは子ウィンドウ ID ) { m_hwnd = CreateWindow( WINDOW_CLASS_NAME, // ウィンドウクラス名 lpWindowTitle, // タイトルバーに表示する文字列 dwStyle, // ウィンドウの種類 x, // ウィンドウを表示する位置(X座標) y, // ウィンドウを表示する位置(Y座標) nWidth, // ウィンドウの幅 nHeight, // ウィンドウの高さ hWndParent, // 親ウィンドウのウィンドウハンドル hMenu, // メニューハンドル m_hInstance, // インスタンスハンドル this // その他の作成データ ); if (m_hwnd == nullptr) return false; return true; } LRESULT CnuWindow::WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wp, lp); } LRESULT CALLBACK CnuWindow::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { CnuWindow* This = (CnuWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (!This) {//取得できなかった(ウィンドウ生成中)場合 if (message == WM_CREATE) { This = (CnuWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams; if (This) { SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)This); return This->WndProc(hWnd, message, wParam, lParam); } } } else {//取得できた場合(ウィンドウ生成後) return This->WndProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam); }
main.cpp
#include<windows.h> #include <tchar.h> #include "CnuWindow.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { CnuWindow wino; wino.RegistWindow(_T("WINO"), hInstance); wino.nuCreateWindow( _T("ウィンドウO"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 300, 300, nullptr, nullptr ); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
クラス名はCnuWindowにしているので、それを置換すれば独自のクラス名にできる。
内容的にはCreateWindow時にlpParamに自身のthisを入れる典型的な方法なので詳細は他所へ譲るとして、多少解説をする
LRESULT CALLBACK CnuWindow::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { CnuWindow* This = (CnuWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (!This) {//取得できなかった(ウィンドウ生成中)場合 if (message == WM_CREATE) { This = (CnuWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams; if (This) { SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)This); return This->WndProc(hWnd, message, wParam, lParam); } } } else {//取得できた場合(ウィンドウ生成後) return This->WndProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam); }
64ビット環境ではSetWindowLongではなくSetWindowLongPtrを使わなくてはならない。それに伴い、GetWindowLongPtr,GWLP_USERDATAに変更する。
virtual LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
これをvirtualにする理由は、main.cppでいずれ以下のように書きたいからである。
#include<windows.h> #include <tchar.h> #include <cstdint> #include "CnuWindow.h" class CWin1 : public CnuWindow { LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_CREATE: break; case WM_LBUTTONDOWN: MessageBox(hWnd, _T("CWin1"), 0, 0); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wp, lp); } }; class CWin2 : public CnuWindow { LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_CREATE: break; case WM_LBUTTONDOWN: MessageBox(hWnd, _T("CWin2"), 0, 0); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wp, lp); } }; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { CWin1 win1; win1.RegistWindow(_T("WIN1"), hInstance); win1.nuCreateWindow(_T("ウィンドウ1"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 300, 300, nullptr, nullptr); CWin2 win2; win2.RegistWindow(_T("WIN2"), hInstance); win2.nuCreateWindow(_T("ウィンドウ2"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 400, 300, 300, nullptr, nullptr); CnuWindow wino; wino.RegistWindow(_T("WINO"), hInstance); wino.nuCreateWindow(_T("ウィンドウO"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 800, 300, 300, nullptr, nullptr); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
上記コードは、CnuWindowを継承したCWin1,CWin2クラスを作成し、それぞれのインスタンスがまったく異なるウィンドウとして動作する。
WndProcを仮想関数にしないと、CWin1,CWin2を作成してもメッセージが全てCnuWindowの方へ行ってしまうので、変数をグローバルにしなくていい程度のメリットしかなくなってしまう。このようにクラスを作れば、同じコードで違う挙動をするウィンドウを最小限の労力で作ることができる。
マイペイすリボについて調べたときの話。
リボ払いについては腐るほど説明があるのに、なぜかマイペイすリボの説明がどこにもなくて、数日間さまよった経験がある。
結論を書くと、
「マイペイすリボ」とは、三井住友VISAカードにおける、「リボ払い」の商品名である。
つまり、
リボ払いのことを、三井住友VISAカード用語で、「マイペイすリボ」という
らしい。言うまでも無いが、完全に辞書的なものではなく、三井住友VISAカードならではの優遇はある。
とはいえ、随所で「マイペイすリボはおトク」等と言われているが、所詮はリボ払い。
年会費やポイントにつられて数千円をケチると、将来利息で数万円も持って行かれる危険がある。
「小銭拾って札束落とす」私は「おトク」という言葉を決して信用しない。釣り針がでかすぎるからだ。
resize()とreserve()の違い。
一言で言うと、end()の位置が違う。 あと、resizeするとその個数分のコンストラクタが走る。
end()の位置が違うので、push_backした時の挙動が異なる
vectorはnewのラッパーであるから、newの使い方から考えると分かりやすい。
resizeにせよreserveにせよ、その分のメモリを確保することに変わりはない。
これがうれしいときはreserve:
・push_backした時に「使用済みの要素の次の要素」に値が入る。リストの末尾に追加していくような感じになる。size()を見れば保存した項目の個数がわかる。
・少なくともreserveで指定した回数分のpush_backに関しては、再確保が発生しないことが保障される。
これがうれしいときはresize:
・size()が確保した要素数になるので、普通の配列と同じように使える。
余談
動作の違いから、resizeした後にpush_backすることは少ない。
また逆に、reserveしてから、size()以上の要素に対してoperator[]でアクセスしてはいけない。
これらのことをやっているコードは、バグである可能性がある(特に後者)
余談だが、こんなことをしてもいいかもしれない。
std::vector<char> v; v.reserve(100*100); //画像サイズは100*100が相場 printf("100*100 size:%d capacity %d\n",v.size(),v.capacity() ); v.resize(30*30); //実際には30*30に設定された printf("30*30 size:%d capacity %d\n",v.size(),v.capacity() ); v.resize(50*50); //わけあって50*50に変更した printf("50*50 size:%d capacity %d\n",v.size(),v.capacity() ); v.resize(200*200);//結局200*200も使ってしまった printf("200*200 size:%d capacity %d\n",v.size(),v.capacity() );
100*100 | size: | 0 | capacity | 10000 |
30*30 | size: | 900 | capacity | 10000 |
50*50 | size: | 2500 | capacity | 10000 |
200*200 | size: | 40000 | capacity | 40000 |
最初にある程度の大きさをreserveしてcapacityを広げておけば、後のresize時に再確保の可能性が減り、高速化が図れる(かも)。
あと、最初に書いたが、resizeした時はその分のコンストラクタが走る。
#include <vector> class CC{ public: CC(){ printf("construct...\n"); } }; int _tmain(int argc, _TCHAR* argv[]) { std::vector<CC> v; v.reserve(100); v.resize(5); getchar(); return 0; }
実行結果:resizeした5個分のコンストラクタが走っている
construct...
construct...
construct...
construct...
construct...
PlantFactory → Blender Cycles 用
使い方:
①Blenderで、PlantFactoryからfbxエクスポートした植物オブジェクトををShiftキーを押しながら一つ以上選択します。
②このスクリプトを走らせます
import bpy for obj in bpy.context.selected_objects: for mat in obj.data.materials: thisTree = mat.node_tree; thisTreeNodes = mat.node_tree.nodes for node in thisTreeNodes: if node.type == 'TEX_IMAGE': if 'alpha' in node.image.name: # ex. 'alpha map.016' alphaTex = node colorout = alphaTex.outputs['Color'] alphaout = alphaTex.outputs['Alpha'] if alphaout.is_linked: MixNode = alphaout.links[0].to_node MixColor1in = MixNode.inputs['Color1'] MixColor2in = MixNode.inputs['Color2'] thisTree.links.new( colorout , MixColor1in ) thisTree.links.new( alphaout , MixColor2in )