Uruchomienie procedury z opóźnieniem (bez timera)

0

Zdarzenie ruchu myszy generuj wiele zdarzeń , użytkownik rusza myszka i w pewnym momencie przestaje ruszać. Jak 3 sekundy po tym jak mysz jest nieruchoma uruchomić procedurę która wyświetli komunikat "BUU"

Jakiś zarys koncepcji

type
  TCos = class

  end;

var
  cos: Tcos;

function uruchom_po_chwili(p_delay: Integer; p_procedure:TProc): TCos;
begin
  // ????
end;

procedure TForm75.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if Assigned(Cos)  then Cos.free;

  cos := uruchom_po_chwili( 3000,
            procedure
            begin
               Showmessage('BUU');
            end)
end;

Czy da sie coś takiego zrobić OmniThreadLibrary?

1

Do tego celu właśnie służy timer, a konkretnie TIdleTimer.

0

Ewentualnie możesz odpalić w tle wątek ze sleepem.

0

Znalazłem coś takiego
http://blog.kluug.net/2015/03/05/procedure-call-with-delay/

Wewnętrznie "TOCallWithDelay.Create" tworzy watek
który w Execute robi "Sleep(1)" dopóki nie minie zadany czas i uruchamia moją metodę jak skończy odliczać.
Jednocześnie można przerwać odliczanie czasu

// Typical usage:

  procedure TForm1.Btn1Click(Sender: TObject);
  begin
    TOCallWithDelay.Create(Self, 1000,
      procedure
      begin
        ShowMessage('Hello');
      end);
  end;

// If you want to stop the execution in the future, use

  procedure TForm1.Btn1Click(Sender: TObject);
  var
    xCall: TOCallWithDelay;
  begin
    TOCallWithDelay.Create(Self, @xCall, 1000,
      procedure
      begin
        ShowMessage('Hello');
      end);

    xCall.Free;//stop TOCallWithDelay -> do not call the method
  end;
1

A czy możesz nam wyjaśnić, czemu nie chcesz skorzystać ze specjalnie przeznaczonego do tego celu narzędzia?

Możesz sobie albo to robić ręcznie "zwykłym" TTimer i przy jego wywołaniach sprawdzać, co użytkownik zrobił, albo (jak pisał @furious programming) użyć dedykowanego komponentu, jakim jest TIdleTimer.

Czemu się upierasz przy swojej wersji i chcesz wymyślać koło na nowo? Czy ma to jakieś uzasadnienie w Twoim projekcie, czy po prostu masz taką wizję (albo np. chcesz to zrobić w ramach ćwiczeń/nabierania wprawy)?

0

Uzywam timer-ów ale mam wrażenie ze nazbyt często i trochę gmatwa to co się dzieje w aplikacji.
Nie jest problem to zrobić za pomoca Timer , raczej pytanie jak to zrobić inaczej ;)
Np. za pomocą OmniThreadLibrary i anonimowych funkcji

0

Nie znam Twojego projektu, więc się jednoznacznie nie wypowiem, ale nie jestem pewien, czy stosowanie wątków na pewno będzie uproszczeniem w stosunku do TIdleTimer :P

0

Poz tym zamiast stosować wiele timerów, możesz ich ilość zmniejszyć, a następnie podczas wywołania obsłużyć parę rzeczy naraz. Ustawiasz w aplikacji jakieś flagi i w zależności od ich wartości, podczas obsługi OnTimer wykonujesz różne działania. Oczywiście - trzeba to zrobić z głową, ale jest to jakaś alternatywa dla wrzucenia kilkunastu/dziesięciu/set timerów ;)

2

Wstaw gdzieś sobie ten kod:

  TThread.CreateAnonymousThread(procedure()
  const
    IDLE_MS = 3000; //3 sekundy
  var
    tLII: tagLASTINPUTINFO;
    bDone: Boolean;
  begin
    tLII.cbSize:= SizeOf(tagLASTINPUTINFO);
    bDone:= False;
    while not bDone do
    begin
      if GetLastInputInfo(tLII) then
      begin
        if (GetTickCount - tLII.dwTime > IDLE_MS) then
        begin
          bDone:= True;
          ShowMessage('BUU');
        end;
      end;
      Sleep(100);
    end;
  end).Start;

Kod wywołuje w wątku anonimową procedurę która w pętli będzie sprawdzała ile MS minęło od ostatniej akcji użytkownika (naciśnięcie przycisku, ruch myszką) jeżeli ten czas przekroczy zadany w IDLE_MS zostanie pokazany komunikat "BUU" a pętla zostanie przerwana (czyli jedno wywołanie tego kodu spowoduje najwyżej jedno wywołanie komunikatu).

0
Adamek Adam napisał(a):

Uzywam timer-ów ale mam wrażenie ze nazbyt często i trochę gmatwa to co się dzieje w aplikacji.

Prawda, nie sposób się nie zgodzić :)

Nie jest problem to zrobić za pomoca Timer , raczej pytanie jak to zrobić inaczej ;)
Np. za pomocą OmniThreadLibrary i anonimowych funkcji

Za pomocą Omni to nie nie do końca wiem (za duże to, nie mam tyle czasu na jego dogłębne poznawanie :/), ale powinieneś rzucić okiem na Async/Await:

procedure TForm1.Button1Click(Sender: TObject);
var
  button: TButton;
begin
  button := Sender as TButton;
  button.Caption := 'Working ...';
  button.Enabled := false;
  Async( 
    // executed in a background thread
    procedure begin
      Sleep(5000);
    end).
  Await( 
    // executed in the main thread after 
    // the anonymous method passed to 
    // Async has completed its work
    procedure begin
      button.Enabled := true;
      button.Caption := 'Done!';
    end);
end;

Fajnie też wygląda TScheduledTasks z QuickLib:

myjob := TMyJob.Create;
myjob.Name := Format('Run at %s and repeat every 1 second until %s',[DateTimeToStr(ScheduledDate),DateTimeToStr(ExpirationDate)]);
scheduledtasks.AddTask('Task1',[myjob],True,
                            procedure(task : ITask)
                            begin
                              cout('task "%s" started',[TMyTask(task.Param[0]).Name],etDebug);
                              TMyJob(task.Param[0]).DoJob;
                            end
                          ).OnException(
                            procedure(task : ITask; aException : Exception)
                            begin
                              cout('task "%s" failed (%s)',[TMyJob(task.Param[0]).Name,aException.Message],etError);
                            end
                          ).OnTerminated(
                            procedure(task : ITask)
                            begin
                              cout('task "%s" finished',[TMyJob(task.Param[0]).Name],etDebug);
                            end
                          ).OnExpired(
                            procedure(task : ITask)
                            begin
                              cout('task "%s" expired',[TMyJob(task.Param[0]).Name],etWarning);
                            end
                          ).StartAt(ScheduledDate
                          ).RepeatEvery(1,TTimeMeasure.tmSeconds,ExpirationDate);
scheduledtasks.Start;
1

Panowie, nie przesadzacie trochę? Co za różnica czy użyje się timera, czy wątku, skoro w dalszym ciągu i tak wykorzystuje się mechanizmy działające w tle, niezależnie od wątku głównego (poniekąd). ;)

Spośród wszystkich propozycji, akurat timer jest najłatwiejszy w obsłudze. Tworzy się go raz i zwalnia raz, włącza i wyłącza jedną instrukcją, kod zdarzeń wykonuje w ramach głównego wątku, więc nie trzeba niczego synchronizować. Żyć nie umierać. Jak trzeba synchronizacji to trzeba i wtedy dobiera się odpowiednie rozwiązanie, ale do prostych zastosowań jest idealny – prosty, wygodny, niezależny od platformy.

Adamek Adam napisał(a):

Uzywam timer-ów ale mam wrażenie ze nazbyt często i trochę gmatwa to co się dzieje w aplikacji.

Ja rozumiem, że gdyby tych timerów naraz działało kilkadziesiąt czy kilkaset, w dodatku powiązanych ze sobą, to faktycznie można by mieć problemy z zapanowaniem nad przepływem sterowania. Jednak jeśli ma się ich co najwyżej kilka, to IMO trzeba chcieć nie rozumieć, aby nie rozumieć co się dzieje.

Nie jest problem to zrobić za pomoca Timer , raczej pytanie jak to zrobić inaczej ;)

No to jak – jest problem czy go nie ma? Bo teraz piszesz, że nie jest problemem wykonanie tego za pomocą timera, a wcześniej napisałeś, że jednak jest problem. Niepotrzebnie kombinujesz, choć fakt – dobrze jest poznać też inne możliwe rozwiązania.

0

Trochę to z lenistwa a trochę aby mieć "piękny przejrzysty kod".
Widać mam za dużo wolnego czasu skoro mam czas zastanawiać się jak coś zrobić zamiast to robić :)

0

Ponownie to napiszę - nie jestem pewien, czy bawienie się w wątki i wymyślanie koła na nowo da ładniejszy kod, niż można uzyskać korzystając ze specjalnych i przeznaczonych do realizacji danych funkcji komponentów. Może masz rację, ale jakoś ciężko mi to sobie wyobrazić/zrozumieć.

0
Adamek Adam napisał(a):

Trochę to z lenistwa a trochę aby mieć "piękny przejrzysty kod".

No i właśnie z obu tych powodów powinieneś wybrać timer. ;)

0
furious programming napisał(a):
Adamek Adam napisał(a):

Trochę to z lenistwa a trochę aby mieć "piękny przejrzysty kod".

No i właśnie z obu tych powodów powinieneś wybrać timer. ;)

Oczywiście, że dokładnie odwrotnie.
Trzeba rozwijać swój warsztat, a nie lecieć na timerach, datamodule i klepaniu formatek całe życie...
Dzięki takiemu podejściu jak Twoje (nie ma żadnego Delphi hejtera w pobliżu? ;-)), jesteśmy (my, quasi-delphi community) dekadę za wszystkim co najciekawsze w branży.
I właśnie dlatego, dość tego.
Czas na nowe wyzwania, czyli witaj .NET Core :)

2
wloochacz napisał(a):

Oczywiście, że dokładnie odwrotnie.

Nie jest odwrotnie – proste zadania rozwiązuje się prostymi sposobami. O ile do jednorazowego wywołania z opóźnieniem, Async/Await jest eleganckim rozwiazaniem, to do pracy cyklicznej timer nadaje się idealnie.

„Pięknego, przejrzystego kodu” nie tworzy się poprzez nadźganie w nim metod anonimowych, czy cukrując składnię poprzez dociągnięcie bibliotek przerabiających Delphi na Javę.

Trzeba rozwijać swój warsztat, a nie lecieć na timerach, datamodule i klepaniu formatek całe życie...

A przepraszam bardzo, kto leci całe życie na timerach, datamodule i klepaniu formatek? Bo nie przypominam sobie, abym każdemu i w każdym przypadku wciskał na siłę timery.

Dzięki takiemu podejściu jak Twoje (nie ma żadnego Delphi hejtera w pobliżu? ;-)), jesteśmy (my, quasi-delphi community) dekadę za wszystkim co najciekawsze w branży.

Oczywiście, bo to ja jestem winny tego, że Delphi (a Free Pascal trzy razy bardziej) jest zacofane w stosunku do technologii takich jak Java czy C#, czy że ludzie nadal korzystają z przedpotopowych dialektów/środowisk i siedzą w swojej ”strefie komfortu” rodem z ubiegłego tysiąclecia.

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