「あら」さんの作成した「さかさまうす」を使ってみて思いました。
「マウスの移動量を検知して、後に操作すればよいのではないか。」と。
移動量の検知は、「妙義のカタツムリ」さんのものをそのまま使わせてもらうことにしました。
// マウスの回転
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;
やってみるとマウスカーソルがフォーム外からフォーム上に入ってきたとき、おかしなところにマウスカーソルが飛んでしまって、フォーム上に乗りません。
特殊な操作が必要なのでしょうか。
>特殊な操作が必要なのでしょうか。
スクリーン座標とコントロール(フォーム)座標をウッカリ忘れてない?
ご指摘ありがとうございます。
でも、残念ながら私にはキミキミさんのご指摘の意味するところがわかりません。
どうも私は初心者レベルのようです。
新規で質問しなおします。
お手数をおかけしました。
ココ読む
ScreenToClient
ClientToScreen
Delphiヘルプさん ありがとうございます。
さっそくヘルプファイルを開けて見ました。
ヘルプファイルが英語で書いてあって、何をどうすればよいのか、よくわかりませんでした。一生懸命翻訳してみたのですが・・・・。ちんぷんかんぷんです。
お伝えしていませんでしたが、私が使っているのはDelphi5です。
「初心者だからしようがない」と思って、
マウスの座標や移動量をどのように検知したり操作したりするのか、
教えてください。
ここが参考になるかも。
http://www.geocities.jp/asumaroyuumaro/program/tips/getcursorpos.html
> ヘルプファイルが英語で書いてあって
英語版Delphi5?
少なくとも日本語版では英語解説ではありませんが???
> 何をどうすればよいのか、よくわかりませんでした。
わからないんだったら使ってみる。
FormのMouseMove等で、ScreenToClient、ClientToScreen を使ってみて、
使用前・使用後の座標をTEdit等に表示してみる。
使い方がわからないのではなく、関数の意味がわからないから使っていないのでしょ?
これは初心者どうこうという問題ではありません。
> ヘルプファイルが英語で書いてあって
Win32 API のヘルプを見てるんじゃないかな?
そうじゃなくて ScreenToClient、ClientToScreen はVCLのTControlのメソッド
です。
どうも質問の意味からすると、APIなのかVCLなのかイマイチはっきりしませんが
忘れてしまいましたが、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 をセットします。
みなさん、ありがとうございます。
今まで、時々ヘルプファイルが英語で書いてあるときがあって、苦い思いをしていたわけがわかりました。
Delphiのヘルプではなくて、Win32 APIのヘルプを参照していたんですね。
みなさんの書き込みのおかげで、Form上にマウスカーソルがあるときの座標を取得する方法がよくわかりました。また、マウスを特定の座標に移動する方法もわかりました。
新たな疑問が湧きました。
フォーム上にマウスカーソルが無いとき、どのイベントでマウスの座標を取得するのでしょう。
教えてください。
今までのレスで答えは出るように見えますが…
ヘルプファイルうんぬんの前にネットで検索されてはどうですか?
ですからマウスの座標は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のバージョンに
よって動作が違うことがあります.
これは,問題を早く(速く?)解決するためであり,マナーでもあります.
>ですからマウスの座標はGetCursorPosで取得できます.
再掲ですが,ここですよ.ご覧になりましたか?
http://www.geocities.jp/asumaroyuumaro/program/tips/getcursorpos.html
>フォーム上にマウスカーソルが無いとき、どのイベントでマウスの座標を取得するのでしょう。
スクリーン上のどこでもマウスを制御したいならグローバルフックが必要だよ。
??? さん、書き込み、ありがとうございます。
分かりが悪くてすみません。
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です。
こんなことってあるのでしょうか。
何かおかしなことをしてしまったのでしょうか。
とりあえずMr.Xrayさんのコードを試されてはいかがですか?
procedure TForm1.Button1Click(Sender: TObject);
var
APos : TPoint;
begin
Sleep(2000);
GetCursorPos(APos);
ShowMessage(IntToStr(APos.X)+' '+IntToStr(APos.Y));
end;
(ボタンを押してから2秒以内にマウスを任意の位置に動かして試します。)
実際にどこのイベントで取得すべかについては、作成しているアプリ
ケーションの要件によります。ここでの質問で答えられる内容ではないです。
それと、やってみれば分かりますが、単に位置を取得するだけというな
ら難しいことは必要ありません。これだけでできます。実際に試行錯誤して
試されましたか?
> 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;
???さん、さどやまさん、書き込みありがとうございました。
procedure TForm1.Button1Click(Sender: TObject);
にしても、
procedure TForm1.Timer1Timer(Sender: TObject);
にしても、
「『TForm1.』以降に記述されているということは、そのフォーム上にマウスカーソルがあるときである。フォーム上にマウスカーソルが無いときは、プログラムのコントロールができない。」と、勝手に判断をしていて、試してみることはしていませんでした。
「あら」さんの作成した「さかさまうす」では、「onTimer」エベントを使ってマウスの座標を取得しているのかもしれませんね。
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;
Mr.XRAY さん、書き込みありがとうございます。
さて、該当する行をコメントアウトしてみました。
その結果、暴走はしなくなりました。
が、メモ帳に記入しても、何の変化現れません。
JournalRecordProc を呼び出さないためだと思います。
これでよかったのでしょうか?
>JournalRecordProc を呼び出さないためだと思います
ですね.問題の切り分けのためのテストです.ご協力感謝致します.
で,コードが一部抜けていましたね.
ListView1.ViewStyle:=vsReport;
ListView1.Columns.Add; //コピペの場合ここが必要
診断結果ですが,例の場所をコメントアウト(無効に)したままで実行し,
(1)Form1を閉じて(右上の×で)メモ帳が閉じればフック関係のコードに問題あり
(2)Form1を閉じてもメモ帳が閉じなければメモ帳のハンドルの取得に失敗している
ということになります.これ以上は何とも言えません.
なんなんでしょうね.
動作確認環境 WindowsXP(SP1) + Delphi5(UP1)
ちなみに,私もコピペでやってみた結果です.
といっても私の環境では解説ページの通り動作していますが.
Mr.XRAY さん、書き込みありがとうございます。
さて、ご指示通り、
ListView1.Columns.Add;
を加え、
// Hook := SetWindowsHookEx(WH_JOURNALRECORD,Addr(JournalRecordProc),
というようにコメントアウトしたまま、
実行してみました。
結果は、
Form1を「×」ボタンで閉じると、メモ帳も閉じました。
なお、
メモ帳側で操作しても、Form1側には変化が現れませんでした。
また、
Hook :=
の行のコメントアウトをはずしたものでは、以前報告したとおり、暴走してしまいます。
もしかすると、この(いま書き込みをしている)パソコン自体に問題があるのかも知れません。
いくつか別のパソコンでやってみます。
Mr.XRAY さんへ
別のパソコン2台で試してみました。
1台はWindowsXPのノートパソコン。
もう一台はWindows98のデスクトップパソコン。
どちらもマウス操作やキー操作の度にListViewに書き込みがされていました。
特定はできませんが、やはり、この(いま書き込みをしている)パソコン自体に問題があるようです。
お騒がせいたしました。
グローバルフックは、初心者の私にはあまりにも山が高そうで心配ですが、少しずつ勉強していきたいと思います。
ツイート | ![]() |