マウスのx方向、y方向の移動量dx、dyを検知して操作するには?


ino668  2007-10-17 16:00:20  No: 28028

「あら」さんの作成した「さかさまうす」を使ってみて思いました。
「マウスの移動量を検知して、後に操作すればよいのではないか。」と。

移動量の検知は、「妙義のカタツムリ」さんのものをそのまま使わせてもらうことにしました。

// マウスの回転
function GetMouseRotation(X, Y: Integer): TMouseRotation;
const
  X0: integer = 0;
  Y0: integer = 0;
  X1: integer = 1;
  Y1: integer = 1;
  Rotation: real = 0;
var
  Theta: real;
  Ax, Ay, Bx, By: integer;
  InnerProd, OuterProd: real;
begin
  result:= mrNone;
  Ax:= X-X1;
  Ay:= Y-Y1;      // vectorA: P(X1, Y1) → P(Y1, Y)
  Bx:= X1-X0;
  By:= Y1-Y0;     // vectorB: P(X0, Y0) → P(X1, Y1)
  repeat
    OuterProd:= Ax*By-Ay*Bx;
    if OuterProd = 0 then break;
    InnerProd:= Ax*Bx+Ay*By;
    if Abs(InnerProd) <= 1 then break;
    Theta:= ArcCos(InnerProd/Sqrt(Ax*Ax+Ay*Ay)/Sqrt(Bx*Bx+By*By));
    if OuterProd > 0 then Theta:= 0 - Theta;
    Rotation:= Rotation + Theta;
    if Rotation >= PI*2 then begin
      result:= mrRight;
      Rotation:= 0;
    end else if Rotation <= -PI*2 then begin
      result:= mrLeft;
      Rotation:= 0;
    end;
    X0:= X1;
    Y0:= Y1;
    X1:= X;
    Y1:= Y;
  until true;
end;

つぎに操作ですが、
SetCursorPos(x,y)
を使えばよいと考えました。

var oldx,oldy: integer;
    dx,dy:integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  oldx:=0;
  oldy:=0;
  dx:=0;
  dy:=0;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  dx:= x-oldx;
  dy:= y-oldy;

  oldx:= x;
  oldy:= y;

  SetCursorPos(x,y);
  end;
end;

やってみるとマウスカーソルがフォーム外からフォーム上に入ってきたとき、おかしなところにマウスカーソルが飛んでしまって、フォーム上に乗りません。

特殊な操作が必要なのでしょうか。


キミキミ  2007-10-17 20:09:22  No: 28029

>特殊な操作が必要なのでしょうか。
スクリーン座標とコントロール(フォーム)座標をウッカリ忘れてない?


ino668  2007-10-18 14:54:09  No: 28030

ご指摘ありがとうございます。
でも、残念ながら私にはキミキミさんのご指摘の意味するところがわかりません。
どうも私は初心者レベルのようです。
新規で質問しなおします。
お手数をおかけしました。


Delphiヘルプ  2007-10-18 17:54:44  No: 28031

ココ読む
ScreenToClient
ClientToScreen


ino668  2007-10-19 15:27:07  No: 28032

Delphiヘルプさん  ありがとうございます。

さっそくヘルプファイルを開けて見ました。
ヘルプファイルが英語で書いてあって、何をどうすればよいのか、よくわかりませんでした。一生懸命翻訳してみたのですが・・・・。ちんぷんかんぷんです。

お伝えしていませんでしたが、私が使っているのはDelphi5です。

「初心者だからしようがない」と思って、
マウスの座標や移動量をどのように検知したり操作したりするのか、
教えてください。


deldel  2007-10-19 17:35:25  No: 28033

ここが参考になるかも。
http://www.geocities.jp/asumaroyuumaro/program/tips/getcursorpos.html


Delphiヘルプ  2007-10-19 19:29:57  No: 28034

> ヘルプファイルが英語で書いてあって
英語版Delphi5?
少なくとも日本語版では英語解説ではありませんが???

> 何をどうすればよいのか、よくわかりませんでした。
わからないんだったら使ってみる。
FormのMouseMove等で、ScreenToClient、ClientToScreen を使ってみて、
使用前・使用後の座標をTEdit等に表示してみる。
使い方がわからないのではなく、関数の意味がわからないから使っていないのでしょ?
これは初心者どうこうという問題ではありません。


うんと  2007-10-19 21:06:20  No: 28035

> ヘルプファイルが英語で書いてあって

Win32 API のヘルプを見てるんじゃないかな?

そうじゃなくて ScreenToClient、ClientToScreen はVCLのTControlのメソッド
です。

どうも質問の意味からすると、APIなのかVCLなのかイマイチはっきりしませんが


うんと  2007-10-19 22:41:07  No: 28036

忘れてしまいましたが、Delphi5だと TControl のメソッドが無いのかもしれません。
その場合は、APIを使って以下のようにします。

Label  を三つフォームに貼っておいて

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  p:TPoint;
begin
  Label1.Caption := Format('Clicent Pos: %d , %d',[X, Y]);
  p := Point(X,Y);
  Windows.ClientToScreen(Handle, p);
  Label2.Caption := Format('Screen Pos: %d , %d',[p.X ,p.Y]);
  Windows.ScreenToClient(Handle, p);
  Label3.Caption := Format('Client Pos: %d , %d',[p.X ,p.Y]);
end;

最初の引数は、ウィンドウハンドルで、上の例では  Form1.Handle を
設定しています。Panel1 などの場合は、Panel1.Handle をセットします。


ino668  2007-10-20 15:30:31  No: 28037

みなさん、ありがとうございます。

今まで、時々ヘルプファイルが英語で書いてあるときがあって、苦い思いをしていたわけがわかりました。
Delphiのヘルプではなくて、Win32 APIのヘルプを参照していたんですね。

みなさんの書き込みのおかげで、Form上にマウスカーソルがあるときの座標を取得する方法がよくわかりました。また、マウスを特定の座標に移動する方法もわかりました。

新たな疑問が湧きました。
フォーム上にマウスカーソルが無いとき、どのイベントでマウスの座標を取得するのでしょう。
教えてください。


???  2007-10-20 15:38:23  No: 28038

今までのレスで答えは出るように見えますが…
ヘルプファイルうんぬんの前にネットで検索されてはどうですか?


Mr.XRAY  URL  2007-10-20 20:30:49  No: 28039

ですからマウスの座標はGetCursorPosで取得できます.
以下のコードは,ボタンをクリックしてから2秒後のマウスの位置を表示します.

procedure TForm1.Button1Click(Sender: TObject);
var
     APos : TPoint;
begin
     Sleep(2000);
     GetCursorPos(APos);
     ShowMessage(IntToStr(APos.X)+'   '+IntToStr(APos.Y));
end;

end.

WindowsXP(SP2) + Delphi5(UP1) で確認.
自分の環境は最初に書きましょうね.WindowsやDelphiのバージョンに
よって動作が違うことがあります.
これは,問題を早く(速く?)解決するためであり,マナーでもあります.


Mr.XRAY  URL  2007-10-20 20:35:39  No: 28040

>ですからマウスの座標はGetCursorPosで取得できます.

再掲ですが,ここですよ.ご覧になりましたか?
http://www.geocities.jp/asumaroyuumaro/program/tips/getcursorpos.html


The Long and Winding  2007-10-20 20:46:24  No: 28041

>フォーム上にマウスカーソルが無いとき、どのイベントでマウスの座標を取得するのでしょう。
スクリーン上のどこでもマウスを制御したいならグローバルフックが必要だよ。


ino668  2007-10-21 15:49:46  No: 28042

???  さん、書き込み、ありがとうございます。
分かりが悪くてすみません。

Mr.XRAY  さん、ご指摘ありがとうございます。
困り果てていたときにこのスレッドを見つけ、書き込みのマナーを知らずに書き込んでしまい、皆さんにご迷惑をおかけしております。
すみません。
GetCursorPos()関数は、紹介していただいたHPを参照して、フォーム上なら取得できるとわかりましたが、フォーム上に無いときに取得できなくて困っていました。

The Long and Winding Road さん、書き込みありがとうございます。
「グローバルフック」について、いくつかインターネットを検索してみました。
それでも書かれていることがむずかしくてよくわかりません。
自分はまったくの初心者のようです。
すみませんが、マウスのスクリーン上の位置を知るためのグローバルフックについてのソースコードやわかりやすい解説があるページなどを教えてください。
本当に分かりが悪くてすみません。

Mr.XRAYさんへ
Mr.XRAYさんのHP
http://homepage2.nifty.com/Mr_XRAY/Delphi/plSamples/T_HookKeyMouseEvent.htm
を参照しました。
そのページに記載されているようにボタンやリストビュー、パネルを配置して、ソースコードを貼り付けて、イベントを関連付けて、リストビューにコラムを3つ作って、実行してみました。
ノートパッドを呼び出した後、マウスカーソルが「I」の形になったままで、ノートパッドもDelphiも入力を受け付けなくなってしまいます。
どうやらフリーズ(暴走)してしまっているようです。
windowsXP Delphi5です。
こんなことってあるのでしょうか。
何かおかしなことをしてしまったのでしょうか。


???(2)  2007-10-21 18:13:48  No: 28043

とりあえずMr.Xrayさんのコードを試されてはいかがですか?

procedure TForm1.Button1Click(Sender: TObject);
var
     APos : TPoint;
begin
     Sleep(2000);
     GetCursorPos(APos);
     ShowMessage(IntToStr(APos.X)+'   '+IntToStr(APos.Y));
end;

(ボタンを押してから2秒以内にマウスを任意の位置に動かして試します。)

  実際にどこのイベントで取得すべかについては、作成しているアプリ
ケーションの要件によります。ここでの質問で答えられる内容ではないです。
  それと、やってみれば分かりますが、単に位置を取得するだけというな
ら難しいことは必要ありません。これだけでできます。実際に試行錯誤して
試されましたか?


さどやま  URL  2007-10-21 19:29:12  No: 28044

> GetCursorPos()関数は、紹介していただいたHPを参照して、フォーム上なら取得できるとわかりましたが、フォーム上に無いときに取得できなくて困っていました。

  Mouse.CursorPosでも同じことですが,「フォーム上に無いとき」でも取得はできます。
  例えば

procedure TForm1.Timer1Timer(Sender: TObject);
var
  P: TPoint;
begin
   GetCursorPos(P);//TPoint型の変数Pにマウスの座標を格納
   label1.Caption :='マウスの座標は'+#10#13+
   'デスクトップ上では X:'+
   inttostr(P.X)+',Y:'+inttostr(P.Y);
   P :=ScreentoClient(P);
   label1.Caption :=label1.Caption+#10#13+'フォーム上では  X:'+
   inttostr(P.X)+',Y:'+inttostr(P.Y)+'です。';
end;


ino668  2007-10-21 20:26:52  No: 28045

???さん、さどやまさん、書き込みありがとうございました。

procedure TForm1.Button1Click(Sender: TObject);
にしても、
procedure TForm1.Timer1Timer(Sender: TObject);
にしても、
「『TForm1.』以降に記述されているということは、そのフォーム上にマウスカーソルがあるときである。フォーム上にマウスカーソルが無いときは、プログラムのコントロールができない。」と、勝手に判断をしていて、試してみることはしていませんでした。

「あら」さんの作成した「さかさまうす」では、「onTimer」エベントを使ってマウスの座標を取得しているのかもしれませんね。


Mr.XRAY  URL  2007-10-24 09:31:31  No: 28046

ino668 さん

>どうやらフリーズ(暴走)してしまっているようです。

本題と関係あるかどうかは別として,
Button1ClickのHook := から下の行をコメントアウトして実行してみて下さい.
それでも同じ現象となるでしょうか.

procedure TForm1.Button1Click(Sender: TObject);
begin
     ShellExecute(Self.Handle,'open',
                  PChar('Notepad.exe'),
                  nil,
                  nil,SW_SHOW);
     while True do begin
       TargetWnd := FindWindow('NotePad',nil);
       if TargetWnd<>0 then break;
       Application.ProcessMessages;
     end;
     // ここを無効に(実行させないように)する
//     Hook := SetWindowsHookEx(WH_JOURNALRECORD,Addr(JournalRecordProc),
//                              hInstance,0);
end;


ino668  2007-10-25 15:17:23  No: 28047

Mr.XRAY さん、書き込みありがとうございます。

さて、該当する行をコメントアウトしてみました。
その結果、暴走はしなくなりました。
が、メモ帳に記入しても、何の変化現れません。
JournalRecordProc を呼び出さないためだと思います。

これでよかったのでしょうか?


Mr.XRAY  URL  2007-10-26 05:30:13  No: 28048

>JournalRecordProc を呼び出さないためだと思います

ですね.問題の切り分けのためのテストです.ご協力感謝致します.
で,コードが一部抜けていましたね.

     ListView1.ViewStyle:=vsReport;
     ListView1.Columns.Add;  //コピペの場合ここが必要
     
診断結果ですが,例の場所をコメントアウト(無効に)したままで実行し,

(1)Form1を閉じて(右上の×で)メモ帳が閉じればフック関係のコードに問題あり
(2)Form1を閉じてもメモ帳が閉じなければメモ帳のハンドルの取得に失敗している

ということになります.これ以上は何とも言えません.
なんなんでしょうね.

動作確認環境   WindowsXP(SP1)  +  Delphi5(UP1)  
ちなみに,私もコピペでやってみた結果です.
といっても私の環境では解説ページの通り動作していますが.


ino668  2007-10-26 14:55:11  No: 28049

Mr.XRAY さん、書き込みありがとうございます。

さて、ご指示通り、
  ListView1.Columns.Add; 
を加え、
//     Hook :=  SetWindowsHookEx(WH_JOURNALRECORD,Addr(JournalRecordProc),
というようにコメントアウトしたまま、
実行してみました。

結果は、
Form1を「×」ボタンで閉じると、メモ帳も閉じました。
なお、
メモ帳側で操作しても、Form1側には変化が現れませんでした。
また、
Hook :=
の行のコメントアウトをはずしたものでは、以前報告したとおり、暴走してしまいます。

もしかすると、この(いま書き込みをしている)パソコン自体に問題があるのかも知れません。
いくつか別のパソコンでやってみます。


ino668  2007-10-27 06:00:39  No: 28050

Mr.XRAY さんへ

別のパソコン2台で試してみました。
1台はWindowsXPのノートパソコン。
もう一台はWindows98のデスクトップパソコン。
どちらもマウス操作やキー操作の度にListViewに書き込みがされていました。

特定はできませんが、やはり、この(いま書き込みをしている)パソコン自体に問題があるようです。
お騒がせいたしました。

グローバルフックは、初心者の私にはあまりにも山が高そうで心配ですが、少しずつ勉強していきたいと思います。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加