Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Jak wykonać coś z opóźnieniem 500ms (Reaktywacja)

25 views
Skip to first unread message

m

unread,
May 25, 2011, 8:23:00 AM5/25/11
to

Daniel P.

unread,
May 26, 2011, 3:24:35 AM5/26/11
to
W dniu 2011-05-25 14:23, m pisze:

Czy do takich celów nie wystarczy proste:

for i := 1 to 2*5 do
begin
Sleep(50);
Application.ProcessMessages;
end;

--
Pozdrawiam,
Daniel P.

Arivald

unread,
May 26, 2011, 3:42:12 AM5/26/11
to
W dniu 2011-05-26 09:24, Daniel P. pisze:

Boże, widzisz i nie grzmisz....

--
Arivald

Arivald

unread,
May 26, 2011, 4:02:24 AM5/26/11
to
W dniu 2011-05-25 14:23, m pisze:

Zamiast "uruchom_za_chwile" powinieneś dać temu nazwę "Delayed Event" -
opóźnione zdarzenie. I dostosować to tak żeby łatwo było ten kod używać
ze zdarzeniami.

Dobrze by było też zrobić jeden, globalny obiekt który by się tym
zajmował, z możliwie automatycznym zarządzaniem pamięcią. Zwolniło by to
użytkownika takiego obiektu od tworzenia i niszczenia obiektów.

--
Arivald

Daniel P.

unread,
May 26, 2011, 4:15:13 AM5/26/11
to
W dniu 2011-05-26 09:42, Arivald pisze:

Faktycznie, w przypadku suwaka to nie najszczęśliwsze rozwiązanie. Nie
przeczytałem dokładnie problemu.

--
Pozdrawiam,
Daniel P.

Arivald

unread,
May 26, 2011, 4:28:07 AM5/26/11
to
W dniu 2011-05-26 10:15, Daniel P. pisze:

Jest wiele podobnych sytuacji. Np masz grid czy listę, i chcesz po
wybraniu elementu wykonać powolną akcję... bez opóźnienia nie dało by
się nawigować klawiaturą, bo każde przeniesienie fokusa na inny item
trwało by wieki.

Podobnie jest z np. widokiem bazodanowym master=>detail. Nie watro od
razu otwierać datasetu detali, póki jeszcze user scrolluje po masterze.

Jeszcze inny przykład: wyszukiwanie. Nie chcemy aby wyszukiwanie
startowało za każdym razem jak user wprowadzi znak... lepiej poczekać,
może zaraz wprowadzi następny.


--
Arivald

m

unread,
May 26, 2011, 5:05:00 AM5/26/11
to
W dniu 2011-05-26 10:02, Arivald pisze:

> W dniu 2011-05-25 14:23, m pisze:
>> W nawiązaniu do wątku "Jak wykonać coś z opóźnieniem 500ms":
>> http://groups.google.com/group/pl.comp.lang.delphi/browse_thread/thread/328bfd69c4a4d9d2/fba5e5d6c1867a68
>>
>>
>>
>>
>> Taka trochę rozwinięta wersja
>> http://forum.4programmers.net/Delphi_Pascal/180505-uruchom_za_chwile
>>
>> M
>
> Zamiast "uruchom_za_chwile" powinieneś dać temu nazwę "Delayed Event" -
> opóźnione zdarzenie.
Co do nazwy to się zgodzę.

> I dostosować to tak żeby łatwo było ten kod używać
> ze zdarzeniami.
Nie za bardzo rozumiem jak by to moglo działać?
Bo teraz to działa w ten sposób że np. wizualny komponent w procedurze
obsługi zdarzenia nie wykonuje włąsiwego kodu zdarzenia tylko przekazuje
informacje jaka procedura z jakim opóźnieniem ma sie wykonać.

// gdzieś a_uruchomMulti: TUruchomZaChwileMulti;

procedure TForm2.scrlbr1Change(Sender: TObject);
begin
a_uruchomMulti.uruchom_za_chwile(3000,procedura_suwak1);
end;

Wiec w kodzie do obsługi zdarzenia w sumie są dwie procedury
"scrlbr1Change" i "procedura_suwak1"

Aby to dawało się łatwiej używać ze zdarzeniami to musiało by działać
np. tak że w kodzie definiujemy zdarzenia które miały by być opóźniane

a_DelayEvent.OpozniajZdarzenie(Button1.onClik,200);
a_DelayEvent.OpozniajZdarzenie(Button2.onMouseDown,200);

a w procedurze obsługi zdarzenia nie modyfikujemy kodu tylko zostawiamy
tak jak było. A obiekt typu TDelayEvent sam będzie zarządzał aby
zdarzenia wykonały się z odpowiednim opóźnieniem.

Tylko czy to jest możliwe do wykonania na ta chwile nie wiem

Arivald

unread,
May 26, 2011, 7:36:18 AM5/26/11
to
W dniu 2011-05-26 11:05, m pisze:


type
//base for all delayed events
TDelayedEvent = class
protected
//unikalne id timera
ID: integer;
timeout: integer;
//procedura wołana przez TEventDelayer jak przyjdzie czas na
wykonanie eventu
procedure Fire(); virtual; abstract;
end;

TDelayedNotifyEvent = class
protected
Event: TNotifyEvent;
procedure Fire(); override;
public
OriginalSender: TObject;
constructor Create(aOriginalSender: TObject; aEvent: TNotifyEvent);
end;

TEventDelayer = class
private
EventsStorage: TObjectList; //lub TList<TDelayedEvent>

class procedure TimerProc(Wnd: HWnd; Msg, TimerID, SysTime:
Longint); stdcall;


procedure AddToStorage(DelayedEvent: TDelayedEvent);
public
.. ctor, dtor...

function DelayNotifyEvent(Sender: TObject; Event: TNotifyEvent;
timeout: integer): boolean;

end;

var EventDelayer: TEventDelayer;

procedure TDelayedNotifyEvent.Fire();
begin
Event(self);
end;


class procedure TimerProc(Wnd: HWnd; Msg, TimerID, SysTime: Longint);
stdcall;
var
ed: TDelayedEvent;
begin
//ExtractDelayedEventForTimerId szuka, zwraca i usuwa z TEventDelayer
//obiekt TDelayedEvent powiązany z danym ID timera

ed := EventDelayer.ExtractDelayedEventForTimerId(TimerID);
if not Assigned(ed) then Exit;

try
ed.Fire();
finally
ed.Free();
end;
end;

procedure TEventDelayer.AddToStorage(DelayedEvent: TDelayedEvent);
var
ed: TDelayedEvent;
begin
//najpierw znajdz stary TDelayedEvent dla danego eventu.
ed := ExtractOldDelayedEvent(DelayedEvent);
KillTimer(0, ed.ID);
ed.Free();

//dodaj nowy
DelayedEvent.Id := SetTimer(0, 0, DelayedEvent.Timeout, Addr(TimerProc));
FStorage.Add(DelayedEvent);
end;


function TEventDelayer.DelayNotifyEvent(
var Sender: TObject;
Event: TNotifyEvent;
timeout: integer
): boolean;
begin
Result := not (Sender is TDelayedEvent);

//event opóźniony: odtworzyć oryginalną zmienną "Sender"
if Sender is TDelayedEvent then
begin
Sender := (Sender as TDelayedEvent).OriginalSender;
Exit;
end;

//event nie opóźniony: dodać do struktur
AddToStorage(TDelayedNotifyEvent.Create(Sender, Event, timeout));
end;


TEventDelayer w tablicy EventsStorage utrzymuje listę aktywnych eventów
(tych dla których timeout się nie skończył).
Po zakończeniu timouta TEventDelayer wyciaga TDelayedEvent z tablicy
(żeby ponowne dodanie wewnątrz obsługi opóźnionego eventu nie kaszaniło)
Na wysiągniętym TDelayedEvent woła Fire();, a potem go usuwa.

TDelayedEvent.Fire() wołał by podany "Event", podając siebie samego
(TDelayedEvent) jako Sender, co pozwalało by odróżnić opóźniony event od
zwykłego, a także na odtworzenie originalnego Sendera eventu.


i w kodzie dodawał być na początku procedury obsługi eventu linijkę
opóźniacza:

procedure TForm2.scrlbr1Change(Sender: TObject);
begin
//obsługa opóźniania.
//zwrócenie True z DelayNotifyEvent oznacza że event zostanie
opóżniony, i należy wyjść z procedury.
//zwrócenie False oznacza że event już jest opóźniony, należy
kontynuować.
if EventDelayer.DelayNotifyEvent(Sender, scrlbr1Change, 1000) then Exit;

//dalej normalna obsługa eventu, taka jak była wcześniej
// DelayNotifyEvent() odtworzy właściwy Sender
....
end;

Dla różnych typów eventów dodajesz odpowiednie nowe typy dziedziczace po
TDelayedEvent, plus nowe wersje DelayNotifyEvent().


Jeśli z TEventDelayer-a zrobisz z TComponent, to będziesz mógł także
użyć FreeNotification do automatycznego usuwania tych obiektów dla
których obiekt implementujący procedurę obsługi eventu sostał
zniszczony. Wtedy już nic nie będzie stało na przeszkodzie żeby
TEventDelayer był globalnym singletonem.


Zmienna TDelayedEvent.ID:
Zamiast używać TTimer polecam SetTimer() and KillTimer() z WinAPI
Dzięki temu oszczędzisz zasoby, bo każdy TTimer alokuje
sobie osobne okno.

Wtedy wartość zwróconą z SerTimer() przypiszesz do TDelayedEvent.ID. A w
callbacku timera (TEventDelayer.TimerProc) dostaniesz tą samą wartosć, i
na jej podstawie odszukasz właściwy TDelayedEvent. Do resetu timere
(KillTimer()) też użyjesz tego ID.


pisane z palca, więc na pewno coś nie zadziała ;-)

--
Arivald

m

unread,
May 27, 2011, 8:40:09 AM5/27/11
to
>
>
> pisane z palca, więc na pewno coś nie zadziała ;-)
>

Najważniejsze ze już wiem o co chodzi :)
I koncepcja jak najbardziej mi się podoba.

M

Karol K

unread,
May 27, 2011, 3:27:20 PM5/27/11
to
W dniu 2011-05-27 14:40, m pisze:
http://forum.4programmers.net/Delphi_Pascal/180505-uruchom_za_chwile
zaktualizowane , zaimplementowałem klasy dla
TNotifyEvent i TMouseMoveEvent

M


0 new messages