Sposób na 100% pewne pobranie pozycji kursora

0

Witam,

Mam pytanie do Was w jaki sposób mogę zapisać pozycję kursora na danym oknie, tak aby po jego zmniejszeniu, przesunięciu - kursor zawsze ustawił się w tym samym miejscu. Ale, żeby nie było tak łatwo - to trzeba wziąć pod uwagę możliwość przesunięcia komponentu na formie.

W związku z tym obecnie w swoim projekcie przy zapisywaniu pozycji kursora robię to na 3 sposoby pobierając wszystkie możliwe koordynaty okna/kontrolki:

Zapis do bazy:

// funkcje pobierające dane koordynacyjne
  WinControl_Handle := WindowFromPoint(pt); // uchwyt kontrolki nad którą znajduje się kursor
  Window_ClassName := GetWindowClass(pt); // nazwa klasy okna np. TForm
  Control_ClassName := string(Trim(GetWinControlName(WinControl_Handle))); // nazwa projektowa np. btnPolacz
  Control_Caption := GetCaptionAtPoint(pt); // caption komponentu np. 'Połącz'

  Control_Pos_Left := GetControlPos(WinControl_Handle).Left;
  Control_Pos_Top := GetControlPos(WinControl_Handle).Top;
  Window_Pos_Left := GetWindowPos(Window_ClassName).Left;
  Window_Pos_Top := GetWindowPos(Window_ClassName).Top;

// pt.X i pt.Y to pozycja aktualnej pozycji kursora na ekranie
                   IBSQL1.SQL.Add('insert into tasks ' +
                   '(window_name, control_name, control_caption, mouse_pos_x, mouse_pos_y, ' +
                   'mouse_control_click_left, mouse_control_click_top, mouse_control_left, mouse_control_top) '  +
                   'VALUES ' +
                   '(:window_name, :control_name, :control_caption, :mouse_pos_x, :mouse_pos_y, ' +
                   ':mouse_control_click_left, :mouse_control_click_top, :mouse_control_left, :mouse_control_top)');
IBSQL1.ParamByName('window_name').Value := Window_ClassName;
IBSQL1.ParamByName('control_name').Value := Control_ClassName;
IBSQL1.ParamByName('control_caption').Value := Control_Caption;
// 1 sposób
IBSQL1.ParamByName('mouse_pos_x').Value := IntToStr(pt.X - Window_Pos_Left); // pozycja myszki na okienku
IBSQL1.ParamByName('mouse_pos_y').Value := IntToStr(pt.Y - Window_Pos_Top);  // pozycja myszki na okienku
// 2 sposób
IBSQL1.ParamByName('mouse_control_click_left').Value := IntToStr(pt.X - Control_Pos_Left); // pozycja klikniecia na kontrolce
IBSQL1.ParamByName('mouse_control_click_top').Value := IntToStr(pt.Y - Control_Pos_Top);  // pozycja klikniecia na kontrolce
// 3 sposób
IBSQL1.ParamByName('mouse_control_left').Value := IntToStr(Control_Pos_Left); // pozycja kontrolki na oknie
IBSQL1.ParamByName('mouse_control_top').Value := IntToStr(Control_Pos_Top); // pozycja kontrolki na oknie

Mając takie dane jak:
pozycja kursora na okienku (czyli okienko X = 0. Y = 0, Kursor: X = 30, Y = 60)
pozycja klikniecia na kontrolce (czyli button.X = 2 button.Y = 4)
pozycja kontrolki na oknie (czyli button.left = 120, button.top = 200)
teoretycznie powinienem móc później bez problemu odczytać pozycję danej kontrolki w oknie nie zależnie w którym miejscu się znajduje sama kontrolka jak i okno.

ODCZYT

W dalszej części projektu szukam okienka poprzez FindWindow podając jako paramentr zmienną WINDOW_NAME, gdzie przechowywana jest wcześniej zapisana nazwa okna (klasa oraz caption z CONTROL_CAPTION) oraz pobieram jego HWND.
Następnie na okienku robię

EnumChildWindows(HWND, @EnumChildWinProc, 0); 

co zwraca mi listę komponentów na oknie i ładuję je do KBM.

function EnumChildWinProc(AHandle: HWND): boolean; stdcall;
var
  ControlClass, ControlText : array[0..255] of Char;
begin
  Result := True;
  GetClassName(AHandle, ControlClass, SizeOf(ControlClass));
  SendMessage(AHandle, WM_GETTEXT, SizeOf(ControlText), integer(@ControlText));
  with frmMenu do
  begin
    // Dodaj do kbmControlInfo informacje o kontrolkach
    kbmControlInfo.Append;
    kbmControlInfoHandle.Value := AHandle;
    kbmControlInfoControlName.Value := GetWinControlName(AHandle);
    kbmControlInfoControlClass.Value := ControlClass;
    kbmControlInfoControlCaption.Value := ControlText;
    kbmControlInfoControlCount.Value := ControlCnt;
    kbmControlInfoControlPos_Left.Value := GetControlPos(AHandle).Left; // pozycja kontrolki na oknie 
    kbmControlInfoControlPos_Top.Value := GetControlPos(AHandle).Top;  // pozycja kontrolki na oknie
    kbmControlInfo.Post;

    ControlCnt := ControlCnt + 1;
  end;
end;

Przypisuję zmienne z datasetu w którym trzymam wcześniej zapisane instrukcje:

    
Window_ClassName := IBDataSet_InstructionsWINDOW_NAME.Value;
Control_ClassName := IBDataSet_InstructionsCONTROL_NAME.Value;
Control_Caption := IBDataSet_InstructionsCONTROL_CAPTION.Value;

Kolejnym krokiem jest wyszukanie w KBM kontrolki która wcześniej zapisaliśmy i wczytaliśmy do grida. Robię to poprzez funkcję LOCATE:

var
  ControlFound: boolean
begin // znajdź wiersz z nazwą kontrolki
  ControlFound := kbmControlInfo.Locate('ControlName', Control_ClassName, []);

W ten sposób odnajduję np. button o nazwie 'Połącz' lub Panel.

Kolejnym krokiem jest pobranie AKTUALNYCH danych pozycji okienka i kontrolki i przypisanie z datasetu do zmiennych.

MouseClick_Pos_X := IBDataSet_InstructionsMOUSE_POS_X.Value;
MouseClick_Pos_Y := IBDataSet_InstructionsMOUSE_POS_Y.Value;
MouseControlClick_Left := IBDataSet_InstructionsMOUSE_CONTROL_CLICK_LEFT.Value;
MouseControlClick_Top := IBDataSet_InstructionsMOUSE_CONTROL_CLICK_TOP.Value;
MouseControl_Pos_Left := IBDataSet_InstructionsMOUSE_CONTROL_LEFT.Value;
MouseControl_Pos_Top := IBDataSet_InstructionsMOUSE_CONTROL_TOP.Value;
Window_Pos_Left := GetWindowPos(Window_ClassName).Left;
Window_Pos_Top := GetWindowPos(Window_ClassName).Top;

kmbControl_Pos_Left := kbmControlInfoControlPos_Left.Value;
kmbControl_Pos_Top := kbmControlInfoControlPos_Top.Value;

A tera zaczyna się MAGIA:
Ustawienie kursora na pozycji realizuję poprzez:

      
// Jeśli Caption jest pusty lub nie znaleziono kontrolki na formie i nazwa klasy kontrolki nie jest pusta
if ((Trim(Control_Caption) = '') or not ControlFound) and not (Control_ClassName = '') then 
begin // ustaw pozycję kursora w okienku w miejscu gdzie zapisaliśmy pozycję kursora
  SetCursorPos(Window_Pos_Left + MouseClick_Pos_X, Window_Pos_Top + MouseClick_Pos_Y)
  // tu właśnie jest pewien problem bo w niektórych przypadkach działa powyższa funkcja a w niektórych poniższa
  //SetCursorPos(MouseControlClick_Left + kmbControl_Pos_Left, MouseControlClick_Top + kmbControl_Pos_Top);
end else // jeśli caption nie jest pusty lub kontrolka jest na formie i nie ma nazwy klasy
begin
  if (Control_Caption <> '') then // jesli ma caption
  begin // wyszukaj na liście po captionie np. Buttona o nazwie 'Połącz'
     if kbmControlInfo.Locate('ControlCaption', Control_Caption, []) then 
        SetCursorPos(Window_Pos_Left + MouseClick_Pos_X, Window_Pos_Top + MouseClick_Pos_Y) // znalazł
     else
        SetCursorPos(MouseControlClick_Left + kmbControl_Pos_Left, MouseControlClick_Top + kmbControl_Pos_Top); 
  end else
  begin
     SetCursorPos(MouseControlClick_Left + MouseControl_Pos_Left, MouseControlClick_Top + MouseControl_Pos_Top);
  end;
end;

Szczerze to kod powyżej jest straszny i założę się, że odczyt można zrobić w prostszy sposób max 1 IF.
Dość obszerny kod ale mam nadzieję, że wszystko jest jasne i będziecie w stanie mi pomóc.

Reasumując wszystko:
Potrzebuję funkcję która ustawi mi dokładnie kursor na pozycji w którą wcześniej zapisaliśmy do bazy.

**Dane jakie mam: **

  • Pozycja okna na ekranie
  • Pozycja kontrolki na oknie
  • Pozycja kliknięcia na kontrolce
  • Nazwa klasy okna (jest zawsze)
  • Nazwa klasy kontrolki (nie zawsze jest)
  • Caption kontrolki/okna (nie zawsze jest)

Powyższy schemat działa w większości dobrze, ale NIE ZAWSZE a musi zawsze bo inaczej jestem w dupie. ;/

0

Przy zapisie przekształć pozycje na pozycje odnośnie kontrolki i zapisz to. P:=Controlka.ScreenToClient(P);
Przy odtwarzaniu z pozycji kontrolki przekształć na pozycje. P:=Controlka.ClientToScreen(P);

0

Mogę Cię prosić abyś to bardziej zobrazował na przykładzie z powyższego kodu? Nie proszę o gotowca ale nie do końca wydaje mi się, że wiem o co Tobie chodzi.

usunięcie cytowania całego poprzedniego posta - fp

0

Mam pytanie do Was w jaki sposób mogę zapisać pozycję kursora na danym oknie, tak aby po jego zmniejszeniu, przesunięciu - kursor zawsze ustawił się w tym samym miejscu.

Co znaczy w tym samym miejscu? W tym samym koordynacie względem okna, względem ekranu czy może względem kontrolki nad którą się znajduje. A może chodzi o procentowy pomiar względem okna, ekranu czy kontrolki?
Zakładając że chodzi o procentowo względem okna, liczysz procent wartości x,y względem rozmiaru okna a po zmianie rozmiaru wyliczasz z tych procentów nowy x i y. Jeżeli chodzi o procentowo względem kontrolki to dochodzi sprawdzenie nad jaką kontrolką się znajdujesz i traktowanie jej jako okna.

Powyższy schemat działa w większości dobrze, ale NIE ZAWSZE a musi zawsze bo inaczej jestem w dupie. ;/

No to może zdecyduj się czego ty chcesz.

0

Chodzi mi o to, aby postawić kursor dokładnie w tym samym miejscu w którym wcześniej zapisaliśmy jego pozycję NA OKNIE WZGLĘDEM KONTROLKI, tak, aby w przypadku gdy np. dostaniemy aktualizację programu w którym poprzednio Button znajdował się w miejscu X = 10 Y = 15 a po aktualizacji został przeniesiony na pozycję X = 50 Y = 70 program go odszukał i ustawił kursor dokładnie w miejscu jego kliknięcia sprzed aktualizacji, dodatkowo jeżeli okno zostało przesunięte to tak jak wspomniałem, aby program wiedział gdzie jest okno i obliczył pozycję kontrolki względem nowej pozycji okna.

Mam nadzieję, że jasno to wyjaśniłem.

Celem programu jest naśladowanie wcześniej nagranej akcji przez użytkownika i dokładne (inteligentne) odtworzenie jej krok po kroku. Napisałem inteligentne ponieważ program sam będzie wiedział gdzie dana kontrolka się znajduje pomimo jej przesunięcia i zmianie rozmiaru okna.

0

Zapisujesz tylko i wyłacznie we współrzędnych odnośnie tego przycisku:
http://msdn.microsoft.com/en-us/library/aa923200.aspx
http://msdn.microsoft.com/en-us/library/aa931003.aspx

0
user322 napisał(a):

Mam nadzieję, że jasno to wyjaśniłem.

W miarę.

No to robisz tak:

  1. Wyszukujesz współrzędne X,Y myszki na twoim oknie
  2. Szukasz na jakiej kontrolce znajduje się to X,Y
  3. Odejmujesz od X,Y pozycję kontrolki
  4. Przekształcasz te X i Y na procentowe względem rozmiarów tej kontrolki
  5. Zapisujesz identyfikator kontrolki i te dwie procentowe wartości

Odzyskiwanie:
1.Szukasz kontrolki o danym identyfikatorze
2.Przekształcasz wartości procentowe na X i Y na kontrolce po jej aktualnych rozmiarach.
3.Dodajesz współrzędne kontrolki oraz współrzędne okna.
4.SetCursorPos.

Zapewne można użyć czegoś lepszego niż SetCursorPos tutaj ale ekspertem z WinApi nie jestem.

0
_13th_Dragon napisał(a):

Zapisujesz tylko i wyłacznie we współrzędnych odnośnie tego przycisku:
http://msdn.microsoft.com/en-us/library/aa923200.aspx
http://msdn.microsoft.com/en-us/library/aa931003.aspx

Nie wiem czy dobrze się zrozumieliśmy, ale czy to się sprawdzi gdy zapisuje pozycje kursora innych aplikacji i mam dostęp tylko do uchwytu kontrolki na innej aplikacji, dodam, że jak wiadomo uchwyty po restarcie programu ulegają zmianie więc po restarcie obcej aplikacji uchwyt przyda mi się jak kasa psu na budę.

h45tegh napisał(a):

No to robisz tak:

  1. Wyszukujesz współrzędne X,Y myszki na twoim oknie
  2. Szukasz na jakiej kontrolce znajduje się to X,Y
  3. Odejmujesz od X,Y pozycję kontrolki
  4. Przekształcasz te X i Y na procentowe względem rozmiarów tej kontrolki
  5. Zapisujesz identyfikator kontrolki i te dwie procentowe wartości

W praktyce:

  
// funkcja zwraca mi pozycję okna względem ekranu
function GetWindowPos(WindowName: string): TRect;
var
  Rect: TRect;
  h: HWND;
begin
  h := FindWindow(PChar(WindowName), nil);

  GetWindowRect(h, Rect);
  Result := Rect;
end;

WinControl_Handle := WindowFromPoint(pt); // pobierz uchwyt kontrolki nad ktora znajduje sie kursor

1. Wyszukujesz współrzędne X,Y myszki na twoim oknie

Window_Pos_Left := GetWindowPos(Window_ClassName).Left;
Window_Pos_Top := GetWindowPos(Window_ClassName).Top;

GetCursorPos(CursorPos);
PozycjaKursoraNaOknie.X := CursorPos.X - GetWindowPos.Left;
PozycjaKursoraNaOknie.Y := CursorPos.Y - GetWindowPos.Top;

2. Szukasz na jakiej kontrolce znajduje się to X,Y

  
// funkcja zwraca pozycje kontrolki względem okna
function GetControlPos(Control_HWND: HWND): TRect;
var
  Rect: TRect;
begin
  GetWindowRect(Control_HWND, Rect);
  Result := Rect;
end;

Control_Pos_Left := GetControlPos(WinControl_Handle).Left;
Control_Pos_Top := GetControlPos(WinControl_Handle).Top;

3. Odejmujesz od X,Y pozycję kontrolki

PozycjaKursora.X := PozycjaKursoraNaOknie.X - Control_Pos_Left;
PozycjaKursora.Y := PozycjaKursoraNaOknie.Y - Control_Pos_Top;

Czy PozycjaKursora jest już dokładną pozycją kliknięcia na okienku?
Co z pozycją kliknięcia? Bo jeśli użytkownik kliknął np. na Panel który jest długi: Dlugość(X) = 500 Szerokość(Y) = 30 a ja ustawię kursor na pozycji PozycjaKursoraNaOknie.X - Control_Pos_Left zakładając, że panel przylega do krawędzi okna to Pozycja kontrolki.X będzie wynosiła 0 a my kliknęliśmy np. na X = 300.
Wydaje mi się, że potrzebne będą dodatkowe współrzędne kliknięcia na kontrolce, czy coś motam?

4. Przekształcasz te X i Y na procentowe względem rozmiarów tej kontrolki?

0

Czy PozycjaKursora jest już dokładną pozycją kliknięcia na okienku?

Ale dlaczego ty mnie pytasz o twój kod?

Wydaje mi się, że potrzebne będą dodatkowe współrzędne kliknięcia na kontrolce, czy coś motam?

Ja ciebie proszę, dlaczego ty interpretujesz moje instrukcje tak jak ci się podoba? Jeżeli twoim zdaniem potrzebne do tego jest IP komputera to je sobie pobieraj, ale moja instrukcja jest bądź co bądź ramowa i ja jej nie implementuję.

Jeżeli nie potrafisz zrozumieć mojego opisu który jednak bądź co bądź wymaga myślenia to ja tobie nie pomogę. Jeżeli dla ciebie dodawanie, odejmowanie i mnożenie to zbyt wiele to co ja mogę zrobić.
Czekaj aż @olesio ci gotowca da bo on już wszystkie możliwe tematy WinApi ma oklepane.

0

Czy aby chodzi Ci o kursor(myszki), czy kontolkę (aż sie sam brzydzę tym stwierdzeniem) ostatnio wyświetlaną w oknie?

0

Kontrolkę w przypadku, gdy na nią kliknięto, lub okno jeżeli np. kliknięto belkę programu.

0

Jest ktoś w stanie pomóc?

0

Twoje zdanie jest nie logiczne. Jak można rozumieć czyjś problem i wnioskować z tego, że druga osoba nie rozumie czego chce. Na logikę Ty nie rozumiesz co chcę osiągnąć, więc albo nie wczytałeś się dokładnie albo nie jesteś w stanie mi pomóc bo nie potrafisz zrozumieć mojego problemu. Od samego początku piszę, że chcę zapisać pozycję kursora na kontrolce lub oknie w zależności pod czym jest aktualnie kursor (panel, belka tytułowa, button, pulpit, dbgrid, Groupbox lub wiele innych), a następnie odczytać jego pozycję poprzez ustawienie kursora dokładnie nad tą samą kontrolką INNEGO OKNA OBCEJ APLIKACJI uwzględniając jej możliwe przesunięcie na oknie lub samego okna względem pulpitu.

0

Rozważania teoretyczne (nie chce mi się sprawdzać)
Zapis:
Pobierasz uchwyt głównego okna (jeżeli kursor jest nad oknem potomnym to pobierasz parenta tak długo aż trafisz na okno główne). Współrzędne kursora przeliszasz za pomocą funkcji ScreenToClient podając jako 1 parametr główne okno (to które pobierałeś) no i masz współrzędne które sobie zapiszesz.
Odczyt:
Masz uchwyt okienka aplikacji to GetWindowRectangle, później SetCursorPos na Left i Top powiększone o zapisane współrzędne i powinieneś mieć kursor w tym samym miejscu w którym był poprzednio zapisane.

1 użytkowników online, w tym zalogowanych: 0, gości: 1