Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Mausbewegung nachahmen


Stread - Di 27.04.10 22:26
Titel: Mausbewegung nachahmen
Hi,
ich möchte die Mausbewegungen, die über den ganzen Bildschirm gehen, wiedergeben. Nach dem drücken des Start Knopfes bewegt man die Maus wie man will und das Programm wiederholt dann diese Bewegungen.

Die Koordinaten der Maus bekomme ich mit GetCursorPos(MousePosition) Dann muss ich das X und Y in ein dynamisches Array speichern? Wie speichere ich es so dass X und Y zwei unterschiedliche Daten sind aber trotzdem nur gemeinsam ausgelesen und verarbeitet werden können?
Die Koordinaten sollen durch einen Timer gesteuert alle 0,5 sek gespeichert werden.

Wie kann ich das Array dann auslesen dass ich immer die richtigen Werte zusammen habe?

Die Mausposition setzte ich ja mit SetCursorPos(X,Y); Wie kann ich dann das X und Y mit dem Array Wert ersetzen? Ich weiß ja nicht wieviele und welches Array gerade dran ist.


Ist die Umsetzung so überhaupt möglich?

Danke


elundril - Di 27.04.10 22:28

nimm doch ein Array vom Typ TPoint. Damit hast du X und Y in einem datentyp gemeinsam und heißen sogar "TPoint.X" und "TPoint.Y". ;)

lg elundril


Namenlosnameless - Di 27.04.10 22:29

warum 2 array's??? du kannst ja eines machen und in einem die Position mit Point(X,Y) speichern, dann umgehst du dieses Problem



Edit: waren wir wohl gleichzeitig!


Xion - Di 27.04.10 22:43

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:

Die Mausposition setzte ich ja mit SetCursorPos(X,Y); Wie kann ich dann das X und Y mit dem Array Wert ersetzen? Ich weiß ja nicht wieviele und welches Array gerade dran ist.


:gruebel:

Interessant find ich ja, dass du genau weißt wie es funktioniert :D Es würde ja auch mit 2 integer arrays gehen :) Verstehe deßhalb das Problem nicht ganz...


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var MousePos: array of TPoint;
[...]
//onTimer
L:=Length(MousePos);
SetLength(MousePosL+1);
MousePos[L]:=Mouse.CursorPos;
[...]
//abspielen, dafür hast du ja dann z.B. auch einen timer:
//Pos:=0; muss vor dem abspielen initialisiert werden!
if Pos<Length(MousePos) then
  SetCursorPos(MousePos[Pos].X,MousePos[Pos].Y)
else
  Timer.Enabled:=False; //aufhören wenn alle angezeigt wurden
Pos:=Pos+1;


Jetzt könnte man noch ne Bewegung interpolieren. Dazu nimmst du bei jedem 10ten Timer-Schritt z.B. ein neues Array Element, sonst zählst du immer nur die Differenz von MousePos[Pos] und MousePos[Pos+1] / 10 dazu.


Bergmann89 - Di 27.04.10 22:51

Hey,

ich würd da ne Liste nehmen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
procedure AddPointToList(X, Y: Integer);
type PPoint = ^TPoint;
var p: PPoint; //Zeiger auf einen Punkt
begin
  new(p); //Speicher reservieren
  p^.X := X; //Daten übernehmen
  P^.Y := Y; //in Liste speichern
  PointList.Add(p);
end;

function GetPointFromList(index: Integer): TPoint;
type PPoint = ^TPoint;
begin
  result := PPoint(PointList[index])^;  
end;

procedure ClearPointList;
type PPoint = ^TPoint;
var i: Integer;
begin
  for i := PointList.Count-1 downto 0 do begin //alle Elemente durchzählen
    dispose(PPoint(PointList[i])); //Speicher freigeben
    PointList.delete(i);           //Element löschen
  end;
end;
--> UNGETESTET!!!
Das Ganze könnte man auch schön in eine extra Klasse packen un sich ne komplette TPointListe schreiben...

MfG Bergmann.


StrikeLucky - Mi 28.04.10 20:07

Wofür steht das "^" vor dem TPoint?
Und wofür steht es nochmal hier: p^.X := X; //Daten übernehmen ?
Was bewirkt es?


FinnO - Mi 28.04.10 21:10

das ^ steht für einen Zeiger.


Delphi-Quelltext
1:
type PInteger : ^Integer //Ein Typ, der auf die Speicherstelle eines Integers zeigt.                    


mit dem ^ nach der Variable holt man sich den Wert, der an der Stelle des Pointers im Speicher liegt zurück.


Delphi-Quelltext
1:
result := VariblePInteger^ + 5                    


Stread - Do 29.04.10 22:20

Ein paar Fragen dazu hätte ich noch:

Die Variable L und MousePosL muss noch deklariert werden oder? Als array? Alle Variabeln in Private rein, weil man sie ja 2x braucht?

Zeile 3-6 kommt in den Timer der das ganze Aufzeichnen soll und Z. 10-14 in den der es abspielen soll?

Wo kommt das Pos im zweiten teil her?

Ich habe bis jetzt noch nicht soviel mit Arrays gearbeitet und kenne mich deshalb nicht so damit aus.
user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:

Verstehe deßhalb das Problem nicht ganz...

Ich weiß zwar wie es theoretisch gehen muss aber nicht welche Befhle ich dafür benötige. :wink:

PS. dass mit der Bewegung interpolieren verstehe ich nicht aber ich bin schon froh wenn das erste klappt.


Xion - Do 29.04.10 23:50

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:

Die Variable L und MousePosL muss noch deklariert werden oder? Als array? Alle Variabeln in Private rein, weil man sie ja 2x braucht?


L ist ein Integer, gerne lokal in der procedure. MousePosL ist ein Tippfehler und muss MousePos heißen :D

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:

Zeile 3-6 kommt in den Timer der das ganze Aufzeichnen soll und Z. 10-14 in den der es abspielen soll?

Genau

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:
Wo kommt das Pos im zweiten teil her?

Das ist ne globale integer Variable. Du musst dafür sorgen, dass die VOR dem Abspielen auf 0 gesetzt wird. Die gibt praktisch an, an welcher Stelle das Abspielen gerade ist (wenn du es am Anfang also auf 10 setzt, wird erst bei der 11ten Position mit dem Abspielen begonnen)

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:

PS. dass mit der Bewegung interpolieren verstehe ich nicht aber ich bin schon froh wenn das erste klappt.

Das machen wir später :D Alles zu seiner Zeit ;) Das Grundproblem ist, dass du natürlich keine fließende Bewegung hast wenn du die Positionen anzeigst, sondern "Zucken" ;)


Stread - Fr 30.04.10 20:40

Ich habe jetzt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
private
  L,Pos:Integer;
  MousePos: array of TPoint;
    { Private-Deklarationen }
  public

    { Public-Deklarationen }
  end;


und deine Routine:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
procedure TForm12.BtAufzeichnenClick(Sender: TObject);
begin
TimerAufzeichnen.Enabled:=TRUE;
Pos:=0;

end;
procedure TForm12.TimerAbspielenTimer(Sender: TObject);
begin
//abspielen, dafür hast du ja dann z.B. auch einen timer:
//Pos:=0; muss vor dem abspielen initialisiert werden!

if Pos<Length(MousePos) then
  SetCursorPos(MousePos[Pos].X,MousePos[Pos].Y)
else
  TimerAb.Enabled:=False; //aufhören wenn alle angezeigt wurden
Pos:=Pos+1;
end;

procedure TForm12.TimerAufzeichnenTimer(Sender: TObject);
begin
//onTimer
L:=Length(MousePos);
SetLength(MousePos+1);
MousePos[L]:=Mouse.CursorPos;
end;


Bei Zeile 17: SetLength(MousePos+1); meldet er den Fehler: E2008 Inkompatible Typen

Ich gebe mit dem +1 die Größe des Arrays an?


elundril - Fr 30.04.10 21:10

Eh klar. Mit SetLength setzt du die neue Länge des Arrays. Diese Methode erwartet 2 Parameter. Einerseits das dynamische Array und andererseits die neue Länge des Arrays (welches du eh schon in L gespeichert hast eigentlich). Deswegen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TForm12.TimerAufzeichnenTimer(Sender: TObject);
begin
//onTimer
L:=Length(MousePos);
SetLength(MousePos, L);
MousePos[L]:=Mouse.CursorPos;
end;


lg elundril


Xion - Fr 30.04.10 21:18


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TForm12.TimerAufzeichnenTimer(Sender: TObject);
begin
//onTimer
L:=Length(MousePos);
SetLength(MousePos, L+1);
MousePos[L]:=Mouse.CursorPos;
end;


Stread - Fr 30.04.10 22:37

Danke Xion

L+1 klappt. Mit dem Button Aufzeichnen starte ich den Timer mit Stop und Abspielen beende ich den Timer und mit Abspielen starte ich den zweiten Timer. Wenn ich nun zum 2. mal den Aufzeichnen Knopf drücke werden die neuen Daten gerade hinten eingefügt. Also muss ich das Array wieder löschen.
Ich habe es mit FillChar(MousePos[0], Length(MousePos)*sizeof(MousePos[0]), 0); probiert aber dann setzt sich der Mauszeiger oben links am Bildschirm fest.

Wie kann ich das Array so hinbekommen wie es am Anfang war?

Ich habe das Intervall beider Timer auf 10 gesetzt. Nun habe ich auch meine flüssige Bewegung. Kann es mit der Methode zu Problemen kommen?


Xion - Fr 30.04.10 22:43

user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:
Wie kann ich das Array so hinbekommen wie es am Anfang war?


Delphi-Quelltext
1:
SetLength(MousePos,0);                    


user profile iconStread hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe das Intervall beider Timer auf 10 gesetzt. Nun habe ich auch meine flüssige Bewegung. Kann es mit der Methode zu Problemen kommen?

Mir fällt da nur ein, dass irgendwann mal dein Speicher überläuft. Das sollte aber ne Weile dauern. Wenn du zwischendrin immer wieder neu aufnimmst, ist das natürlich kein Problem.


Stread - Fr 30.04.10 23:53

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:

Mir fällt da nur ein, dass irgendwann mal dein Speicher überläuft.

Wann circa. 10 min? 1h?

Als nächstes will ich dann dass er auf Mausklick die aktuellen Koordnaten speichert und dann zur richtigen Zeit auf dem richtigen Punkt klickt. Aber dazu muss ich mir erst selbst was überlegen :)
Ich hab ja schon dass er alles Manuell kann. Also per Tastenkombination Koordinaten speichern, ich wähle aus ob Links/rechts/Doppelklick und er macht das dann in der Zeit wo ich es sage.
Nun muss das alles noch mit dem Array der Mausbewegung stimmen.


Bergmann89 - Sa 01.05.10 02:53

Hey,


Delphi Hilfe hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
type TPoint = packed record
  X: Longint;
  Y: Longint;
end;
Longin -2147483648..2147483647 32 Bit, mit Vorzeichen
32Bit = 4 Byte. Da es 2 sind (X und Y) sind es 8 Byte pro 10ms das sind 800Byte pro Sekunde. Nehmen wir an du hast 1024MB (frei) zur verfügung. 1024MB = 1.073.741.824 B. 1.073.741.824 / 800 = 1.342.177,28 Sekunden. Damit kannste ca. 15,5 Tage aufnehmen^^

für die Mausklicks kannst du das änlich machen. Erstell dir n array of record, in dem du dann deine Daten speicherst:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
type
  TMouseButton = (mbLeft, mbRight, mbDouble);
  TMouseClickRec = packed record
    Time: Cardinal;
    Button: TMouseButton;
  end;

var
  MouseClickArr = array of TMouseClickRec;
oder so ähnlich ;)

MfG Bergmann


elundril - Sa 01.05.10 03:14

ich würds komplett in das array einarbeiten. Also statt array of TPoint zu nehmen und dann nochmal ein extra Array (was im übrigen mMn das zusammenfügen dann recht schwer macht) zu verwenden, einfach gleich sowas:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
type 
  TMouseButton = (mbLeft, mbRight, mbDouble, mbNone);
  TMouseRec = packed record
    Pos = TPoint;
    Button = TMouseButton;
  end;

var
  MouseArray = array of TMouseRec;


Das speichert dann bei jeder Koordinate ob mit der Maus geklickt wurde. Wenn ja wird gespeichert mit welcher Taste, wenn nein wird mbNone verwendet. Braucht zwar etwas mehr Speicher, aber ich finde es ist einfacher zum Aufnehmen und Wiedergeben.

lg elundril


Stread - So 02.05.10 00:32

Kann ich meine bisherigen Variablen und das Array behalten und das neue ist nur eine Erweiterung, oder muss ich was ganz neues schreiben? Welches Array frage ich dann in Abspielen ab?

Delphi sagt zu mir nach mbLeft müsste die Klammer wieder zugehen.


Xion - So 02.05.10 08:38

Mach einfach


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
type TMouseButton = (mbNone,mbLeft, mbRight, mbDouble);

type TMousePosData=record 
  X,Y: integer;
  Button: TMouseButton;
end;


MousePos: array of TMousePosData;


Dann ändert sich an deinem bisherigen Code nix. Aber durch MouseButton[x].Button kannst du noch ne Information zusätzlich speichern


Stread - Sa 08.05.10 19:04

Sorry aber ich blicks gerade nicht. Wo muss das hin? In den Timer der auch schon die Mausbewegung aufzeichnet?

Die Abfrage ob und wenn ja welche Maustaste gedrückt wurde muss ja mit der aktuellen Koordinate gespeichert werden, also muss es doch auch ins gleiche Array gespeichert werden oder?

Reicht denn die 1. Zeile um die Art des Mausklicks zu bestimmen?

Wie kann ich beim Abspielen auf diese Informationen zugreifen? Bis jetzt bewegt er ja nur den Mauszeiger.


PS. Sorry dass ich erst jetzt schreibe aber ich hatte noch zu tun.


Xion - So 09.05.10 11:56

Alles quatsch ^^

Du hast doch irgendwo stehen

Delphi-Quelltext
1:
MousePos: array of TPoint;                    


jetzt schreibst du stattdessen

Delphi-Quelltext
1:
MousePos: array of TMousePosData;                    


und schon kannst du per MousePos[x].Button:=mbLeft z.B. die linke Taste zuweisen


Stread - So 09.05.10 19:33

Wahrscheinlich ist das doch zu schwer für mich oder ich bin das Wochenende schief drauf :(

Ich habe bisher:

Delphi-Quelltext
1:
2:
3:
private
L,Pos:Integer;
MousePos: array of  TPoint;


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm12.TimerAufzeichnenTimer(Sender: TObject);
begin
L:=Length(MousePos);
SetLength(MousePos, L+1);
MousePos[L]:=Mouse.CursorPos;
end;


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm12.TimerAbspielenTimer(Sender: TObject);
begin
if Pos<Length(MousePos) then
  SetCursorPos(MousePos[Pos].X,MousePos[Pos].Y)
else
  TimerAbspielen.Enabled:=FALSE; 
Pos:=Pos+1;
end;


Bei den Variablen ersetze ich MousePos: array of TPoint durch MousePos: array of TMousePosData
Das ist noch klar.

Das kommt direkt drunter?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
type TMouseButton = (mbNone,mbLeft, mbRight, mbDouble);

type TMousePosData=record 
  X,Y: integer;
  Button: TMouseButton;
end;


Mir ist nur nicht klar wie das nun die Maustaste in die Koordinate speichern soll. Ich brauche doch noch was neues im Aufzeichnen und Abspielen Timer?

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:

und schon kannst du per MousePos[x].Button:=mbLeft z.B. die linke Taste zuweisen


Wo und überhaupt warum soll ich das zuweißen? Das MousePos[x] ist nicht das Integer X oder?

PS. Wenns dir zu viel ist kannst du es gerne lassen :oops:


Bergmann89 - So 09.05.10 19:57

Hey,


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
type
  TMouseButton = (mbLeft, mbRight, mbDouble);
  TMouseButtons = set of TMouseButton;

  TMouseData = record
    Pos: TPoint;
    Button: TMouseButtons;
  end;

var
  MouseButtons: TMouseButtons;
  MouseData: array of TMouseData;  

procedure TForm1.FormDblClick(Sender: TObject);
begin
  MouseButtons := MouseButtons + [mbDouble];
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  case Button of
    mbLeft : MouseButtons := MouseButtons + [mbLeft];
    mbRight: MouseButtons := MouseButtons + [mbRight];
  end;
end;

procedure TForm1.TimerAufzeichnenTimer(Sender: TObject);
var L: Integer;
begin
  L:=Length(MouseData);
  SetLength(MouseData, L+1);
  MouseData[L].Pos := Mouse.CursorPos;
  MouseData[L].Button := MouseButtons;
  MouseButtons := [];
end;


Ich hab das TMouseButton mal noch im ein Set erweitert, jetz kannst du da alle Buttons rein haun, die du brauchst. Wenn auf die Form geklickt wird, dann speichert er in MouseButtons, welche Buttons gedrückt wurden und schreibt sie dann wenn der Timer zuschlägt in dein Array. Für das Abspielen machste dir jetzt mal selber Gedanken, eig is es ja das gleich, bloß rumgedreht ;)

MfG Bergmann


Stread - So 09.05.10 20:48

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Wenn auf die Form geklickt wird,


Das ist das Problem, ich will die Klicks nicht nur auf die Form beschränken sondern vor allem außerhalb anwenden.
Ich hab schon was, das merkt aber nur ob die Maustaste gedrückt wurde.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm12.TimerButtonklickTimer(Sender: TObject);
begin
if (GetAsyncKeyState(VK_LBUTTON) and $8000) > 0 then
begin
      Button2.Caption := 'Mouse button down';
end
   else
      Button2.Caption := 'Mouse button Up';
end;


elundril - So 09.05.10 20:53

Also Bergmanns Version wirst du doch noch mit deiner kombinieren können. Wenn nicht das brich das Projekt vorläufig mal ab und widme dich denk Grundlagen des Programmierens in Delphi. Ansonsten könnten wir ja gleich das ganze Programm für dich schreiben...

lg elundril


jaenicke - So 09.05.10 21:05

Wenn der Timer genau vor und nach dem Mausklick ausgelöst wird, dann bemerkt dein Tool so aber auch nix von dem Klick. Und bei der Länge eines Mausklicks ist die Wahrscheinlichkeit dafür relativ hoch.

Die einzige Möglichkeit wirklich alle Klicks mitzubekommen führt über Hooks. Das Thema ist aber für dich vermutlich definitiv zu schwierig. Deshalb ist die Prüfung zum Zeitpunkt des Timerereignisses wohl dennoch das Sinnvollste.