いつもお世話になっております。
今回はドラッグによる円の描画方法について教えていただきたく質問させていただきま
した。
透明ウィンドウ上(TOPMOSTになってます)での検知として
・WM_LBUTTONDOWNのとき、キャプチャを開始(Cap_flag = true)
・WM_LBUTTONMOVEの間、円を描画(←Cap_flag = trueのとき)
・WM_LBUTTONUPのとき、キャプチャを解く(Cap_flag = false)
と以上のような形でドラッグして円を描画しようと考えています。
そこで、WM_LBUTTONMOVE(Cap_flag = true)のときの処理なんですが
円の描画を随時書き直すような感じでSetTimerで次のように書いてみました↓
void CALLBACK Ges_TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
HDC hdc;
HBRUSH hBrush;
//キャプチャ開始地点(ges.x, ges.y)
//キャプチャ終了地点(koko.x, koko.y)
//画面のデバイスコンテキストを取得
hdc = GetDC(hWnd);
//ペン作成、設定
SelectObject(hdc, CreatePen(PS_SOLID, 0, RGB(0, 55, 0)));
hBrush = (HBRUSH)GetStockObject(NULL_BRUSH); //空のブラシを取得
SelectObject(hdc, hBrush);
//始点と終点との距離計算
float A = (float)abs(koko.x - ges.x);
float B = (float)abs(koko.y - ges.y);
float C = (float)(sqrt(pow(A,2) + pow(B,2)));
//上記の距離Cを直径とした円弧を描く
if(koko.x > ges.x){
if(koko.y > ges.y){
AngleArc(hdc, (int)(ges.x+(A/2)), (int)(ges.y+(B/2)), (DWORD)C/2, (float)
0, (float)360);
}
else{
AngleArc(hdc, (int)(ges.x+(A/2)), (int)(koko.y+(B/2)), (DWORD)C/2,
(float)0, (float)360);
}
}else{
if(koko.y > ges.y){
AngleArc(hdc, (int)(koko.x+(A/2)), (int)(ges.y+(B/2)), (DWORD)C/2,
(float)0, (float)360);
}
else{
AngleArc(hdc, (int)(koko.x+(A/2)), (int)(koko.y+(B/2)), (DWORD)C/2,
(float)0, (float)360);
}
}
ReleaseDC(hWnd, hdc);
}
ちなみにLBUTTONDOWN時の始点と、LBUTTONUP時の終点を結んだ線を直径とする
円弧を描画したかったので、AngleArcを使ってみました。
と、このような感じですが円が描画されません。
どうすれば円が描画されるのか、基本的なところが間違っているのかもしれませんが
お分かりの方いましたらご教授の方よろしくお願いいたします。
環境は、WinXP, Visual Studio2005, VC++ Win32です。
円が描画されないと言う内容についてもっと具体的に書いた方が良いと思います。
全く何も描画されないのか?
描画されるけれど、目的の円ではない物が描画されるのか?
目的の物ではない物が描画されるのであれば、どんな物が描画されているのか?
文章でのやり取りになるので出来る限り詳しく書かないと伝わらないですよ。
ああ、このコードってタイマーイベントの中ですよね。
画面が無効化されていない状態で描画しているからじゃないかなぁ。
描画する部分は、WM_PAINTイベントが来た所でするべきだと思います。
タイマーイベント内でやるとしたら描画の為の座標の計算とか
画面の再描画を促す為に無効化を行なうとかじゃないかなぁ。
Windowsでの描画の仕組みを調べて見たほうが良いですよ。
Windowsではただ単にDCに対して描けば良いと言うわけでは有りませんから。
PATIOさん
>円が描画されないと言う内容についてもっと具体的に書いた方が良いと思います。
そうですよね。すいません↓
円の描画は全く何も表示されない状態です。
WM_LBUTTONMOVEの処理に入り、Cap_flag = trueのときに
SetTimer(cl_hwnd, 0, 1, Ges_TimerProc)で描画処理へ飛ばすようにしていて
その条件(Cap_flag = true)に入っていることは確認できたのですが…。
WM_LBUTTONMOVEなんていつの間にかできたのか。
バグの切り分けとかいうのをすることになるな。
他人にはあなたのプログラムなんてわからんから。
AngleArcの戻り値がエラーを示していないかとか何で調べないの?
hdcは妥当なの?
ges.xは妥当なの?
SelectObjectの戻り値?解放?
CreatePenの戻り値?解放?
などなど
> ちなみにLBUTTONDOWN時の始点と、LBUTTONUP時の終点を結んだ線を直径とする
> 円弧を描画したかったので、AngleArcを使ってみました。
まずは始点と終点を結ぶ直線を描けるようになってはどうかと。
一歩ずつ行こうよ。
>WM_LBUTTONMOVEなんていつの間にかできたのか。
僕は WM_MOUSEMOVE なら知っているよ。
WM_LBUTTONMOVE は自分で作ったメッセージですか?
試しに Ges_TimerProc 関数の最後に次の2行を追加してみる。
void CALLBACK Ges_TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
HDC hdc;
HBRUSH hBrush;
:
省略
:
InvalidateRect( hWnd, NULL, TRUE );
UpdateWindow( hWnd );
}
これで描画されるかどうか確認してみること。
これでも描画されないなら Ges_TimerProc の描画ロジックにバグがあるかもね。
>どうすれば円が描画されるのか、基本的なところが間違っているのかもしれませんが
>お分かりの方いましたらご教授の方よろしくお願いいたします。
普通描画は WM_PAINT で処理すべきだと思うな。
タイマー関数では描画を促す InvalidateRect() や
InvalidateRect()+UpdateWindow()を送れば良いんじゃないの。
うーーん、
WM_PAINT内で描かないので、
ウインドウが重なったときに描画したのが消えてしまったとか?
WM_PAINT内で描かない場合は、WM_PAINT内でまとめて再描画しないとだめですね。
まずは、XY座興、直径固定でマウスの操作関係なしで描いてみたらどうですか。
はじめは、たいちうさんのご意見通り直線を描いてみるのがいいですね。
PATIOさん、wclrp ( 'o')さん、たいちうさん、金魚ちゃんさん、ITOさん
ご意見ありがとうございます。
>WM_LBUTTONMOVEなんていつの間にかできたのか。
自分の書き間違いでした。すいません、WM_MOUSEMOVEのメッセージです。
>試しに Ges_TimerProc 関数の最後に次の2行を追加してみる。
>
>void CALLBACK Ges_TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
>{
> HDC hdc;
> HBRUSH hBrush;
> :
> 省略
> :
> InvalidateRect( hWnd, NULL, TRUE );
> UpdateWindow( hWnd );
>}
と追加しても描画はされませんでした。やはり書き方が間違っているのですね。
まずは、皆さんのご指摘の通り
始点と終点を結ぶ直線から、WM_PAINTでの描画をしてみたいと思います。
> まずは、皆さんのご指摘の通り
> 始点と終点を結ぶ直線から、WM_PAINTでの描画をしてみたいと思います。
最初は、マウスの動き関係なしで、座標は固定で、他のウインドウ(メモ帳等)
を重ねても消えないことを確認するといいです。
この回答は解決方法では無いです。
1、目的を鑑みて、描画にタイマー使うのは×。
2、マウスイベントでは座標計算して変数にいれるだけ。あと、
再描画イベントを発生させるだけにする。
3、再描画のハンドラ内で、表示領域クリア。そして円描画。
と変更したほうが、後々うれしいかもしれません。
プログラミングでご飯食おうと思うなら・・・。
ITOさん、+さん、ありがとうございました。
透明ウィンドウ上に描くということでなかなか描画されなかったのですが
ウィンドウの設定を
SetLayeredWindowAttributes(hWnd, RGB(0xff, 0xff, 0xff), 255, LWA_COLORKEY |
LWA_ALPHA);
に変更し、WM_PAINTで描画処理することで描画させることができました。
また
>1、目的を鑑みて、描画にタイマー使うのは×。
>2、マウスイベントでは座標計算して変数にいれるだけ。あと、
> 再描画イベントを発生させるだけにする。
>3、再描画のハンドラ内で、表示領域クリア。そして円描画。
を参考にさせていただき、タイマーを使わずにWM_MOUSEMOVEで
座標計算・再描画イベントを発生させ、ドラッグ中の円描画もできるようになりまし
た。
皆さん、とても丁寧でわかりやすいご指摘、本当にありがとうございました。
またご質問させていただくこともあるかと思いますが、その際はよろしくお願いしま
す。