[ juha5803 @ 11.08.2012. 16:41 ] @
Pozdrav,

U pitanju je nešto kao informacioni panel. Postoji na formi dbgrid koji prikazuje podatke (petlja je u pozadini) koji se pomjeraju . Kada dođe do kraja, skoči na početak i tako u krug. Problem je što na nekim računarima dođe do "smrzavanje" tj. prestane se osvježavati dbgrd i ostane prikaz iz tog trenutka ili samo bijel kvadrat na mjestu dbgrida. Kod mene na računaru se nije desilo ni jedanput ta situacija.

Da napomenem problem se desi uglavnom ako neko pomakne miša ili pritisne neki taster na tastaturi.

Ima li iko ideju ili predlog za drugačiji pristup
[ savkic @ 11.08.2012. 22:50 ] @
> Da napomenem problem se desi uglavnom ako neko pomakne miša ili pritisne neki taster na tastaturi.

Ako se prebaciš u neki drugi program pa vratiš u tvoj da li se prikaže normalno?

> Ima li iko ideju ili predlog za drugačiji pristup

Imaš grid sa recimo 50 recorda i na neki interval se menja aktivni slog od prvog do poslednjeg i tako ukrug? Pretpostavljam da tokom toga menjaš i podatke u datasetu na koji je vezan DBGrid. Možeš koristiti neku drugu kontrolu, neki drugi (thirdparty) grid ili nešto poput TVirtualStringTree.
[ reiser @ 11.08.2012. 23:32 ] @
Ako ti se petlja izvrsava u glavnom threadu, izmesti je u posebni thread. Napravi novu klasu koja nasledjuje TThread, u Execute() stavi da na svakih n sekundi/milisekundi salje poruku glavnom threadu preko PostMessage() ili eventualno Synchronize() (ali ti preporucujem prvi nacin za sinhronizaciju threada), a glavni thread ce nakon primanja poruke skrolovati dbgrid za jedno mesto. Otprilike nesto ovako, ovo sam pisao za neki projekat na faxu, ali moze da ti posluzi kao primer za to sto tebi treba:

Code:

//
// Temperature changer thread
// temperature is stored in kelvins, and there are two getters, for kelvins and celsius
//
unit uTemperatureChanger;

interface

uses
  Winapi.Windows, System.Classes;

type
  TTemperatureChanger = class(TThread)
                        private
                          FTemperature   : Double;
                          FVariation     : Double;
                          FSpeed         : Double;
                          FUpdateEvent   : THandle;
                          FMessageHandler: HWND;
                          FWindowMessage : UINT;

                          function GetCelsiusTemperature: Double;
                        protected
                          procedure Execute; override;
                        public
                          constructor Create(const AMessageHandler: HWND; const AWindowMessage: UINT; const AInitial, AVariation, ASpeed: Double);
                          destructor Destroy; override;

                          property TemperatureKelvin: Double read FTemperature;
                          property TemperatureCelsius: Double read GetCelsiusTemperature;
                          property Variation: Double read FVariation;
                          property Speed: Double read FSpeed;
                        end;

implementation

uses
  System.SysUtils;

constructor TTemperatureChanger.Create(const AMessageHandler: HWND; const AWindowMessage: UINT; const AInitial, AVariation, ASpeed: Double);
//
// class constructor
//
begin
  Randomize;

  inherited Create(TRUE);

  FMessageHandler := AMessageHandler;
  FWindowMessage := AWindowMessage;
  FTemperature := AInitial + 273.15;
  FVariation := AVariation;
  FSpeed := ASpeed;
end;

destructor TTemperatureChanger.Destroy;
//
// class destructor
//
begin
  if (not Terminated) and
     (not Suspended) then
  begin
    Terminate;
    SetEvent(FUpdateEvent);
    WaitFor;
  end;

  inherited;
end;

procedure TTemperatureChanger.Execute;
begin
  FUpdateEvent := CreateEvent(nil, FALSE, FALSE, nil);

  while not Terminated do
  begin
    case Random(3) of
      0: ;
      1: FTemperature := FTemperature + FVariation;
      2: FTemperature := FTemperature - FVariation;
    end;

    if FTemperature < 0 then
      FTemperature := 0;

    PostMessage(FMessageHandler, FWindowMessage, 0, 0);
    WaitForSingleObject(FUpdateEvent, Round(Speed * 1000));
  end;
end;

function TTemperatureChanger.GetCelsiusTemperature: Double;
//
// convert temperature to celsius
//
begin
  result := FTemperature - 273.15;
end;

end.


Pogledaj i RegisterWindowMessage API.

U glavnom threadu (formi) primas poruke ovako: http://delphi.about.com/od/adptips2005/qt/formmovenotify.htm
[ juha5803 @ 12.08.2012. 09:11 ] @
Citat:
savkic:
> Da napomenem problem se desi uglavnom ako neko pomakne miša ili pritisne neki taster na tastaturi.

Ako se prebaciš u neki drugi program pa vratiš u tvoj da li se prikaže normalno?

> Ima li iko ideju ili predlog za drugačiji pristup

Imaš grid sa recimo 50 recorda i na neki interval se menja aktivni slog od prvog do poslednjeg i tako ukrug? Pretpostavljam da tokom toga menjaš i podatke u datasetu na koji je vezan DBGrid. Možeš koristiti neku drugu kontrolu, neki drugi (thirdparty) grid ili nešto poput TVirtualStringTree.



Da, nisam obratio pažnju, kod prebacivanja i vraćanja u program zamrzne i kod mene.

Ipak treba mijenjati "taktiku"
[ rambo @ 13.08.2012. 14:16 ] @
Ako je moguće, daj nam malo više informacija, tipa parče koda koje koristiš za to "osvežavanje", neki kraći opis kako radiš čitav taj proces, eventualno malo više podataka o tome šta i zašto ta aplikacija tako radi. Ovako možemo samo da nagađamo i da se vrtimo u krug pokušavajući da zamislimo kako tvoj program radi da bi smo našli gde je problem. Dakle, što je više moguće konkretnih informacija, pa ćemo videti kako možemo da pomognemo.
[ juha5803 @ 13.08.2012. 18:36 ] @
Code:

While not PDM.cdsRedVoznje.Eof do begin

 if GetAsyncKeyState(VK_ESCAPE) = 1 then begin
   exit;
 end ;


  PDM.cdsRedVoznje.Next ;
  PDM.cdsRelacije.Filter   := 'FIRMA_ID = ' + PDM.cdsRedVoznjeFIRMA_ID.AsString  + 'AND PRAVAC_ID = ' + PDM.cdsRedVoznjePRAVAC_ID.AsString  + 'AND CIJENA>0';
  DBGrid2.Refresh ;

  SysUtils.Sleep(PDM.cdsTimeParamTimeScroll1.AsInteger*1000) ;

end ;



Ništa posebno na formi su dva grid-a , lijevi je onaj koji se pomijera, desni je samo "izvod" sa pripadajućim stanicama i cijenama . Praktično blokira bilo kakva intervencija (npr. klik na formu, grid , itd...). Takođe kad sa TEAMVIEWER-om pristupam računaru na kome isprobavam čim se odlogujem isto se desi uz poruku na "not responding". Sve u svemu skoro sam siguran da je problem u načinu prikaza, načinu na koji aplikacija kontroliše osvježavanje.

Usput, postoji li način da fiksiram jedan red npr. na sredini grida pa kad pokazivač dođe na tu poziciju da ga tu zaustavim a da se ispis pomijera prema gore.
[ Rapaic Rajko @ 13.08.2012. 21:24 ] @
Petlje su vrlo nezgodne u glavnom thread-u. Postoje 2 moguca resenja, mozda neko upali:

1) Posle Sleep() postaviti liniju Application.ProcessMessages. Sa ovom komandom treba biti oprezan; ako je recimo slucajno ubacis u neku petlju bez Sleep-a, ubijes procesor k'o zeca (100% opterecenja).
2) Preraditi kod da se petlja vrti iz timer-a. Posto se handle-ovanje timer-a svodi na poruke koje sistem salje aplikaciji, ovo bi takodje radilo bez problema.

Pozz
[ Rapaic Rajko @ 13.08.2012. 21:38 ] @
Aha, sad sam tek video i kako koristis Sleep(). Posto je argument ceo broj sekundi, to ume da bude malo rogobatno.
Ima jedna trik funkcija koju cesto koristim pri radu sa thread-ovima, za proveru Terminated. Nazovimo je SmartSleep(). Sledi preradjen kod:

Code:

procedure SmartSleep(aMils: cardinal); // milisekunde
var
  aFutureTick: cardinal;
begin
  aFutureTick := GetTickCount + aMils;
  while GetTickCount < aFutureTick do
  begin
    Sleep(20); // bilo sta vece od 0, ali dovoljno malo da kod 'tece'
    Application.ProcessMessages;
  end;
end;


Ovo bi trebalo da radi.

Pozz opet ;)

[ reiser @ 13.08.2012. 21:48 ] @
Application.ProcessMessages() je los nacin za sinhronizaciju threada.

http://stackoverflow.com/quest...lication-processmessages-hangs
http://delphi.about.com/od/obj...-processmessages-dark-side.htm

itd..

Kao sto rekoh, koristi poseban thread koji ce raditi pomeranje
[ Rapaic Rajko @ 13.08.2012. 22:01 ] @
Citat:
reiser:
Application.ProcessMessages() je los nacin za sinhronizaciju threada.


Samo za pocetnike :) ; za iole iskusnog sistemasa, posluzice kao i svaka druga funkcija.
Ako cemo vec tako (bez trik resenja), timer je daleko jednostavniji; pisati thread samo zarad pomeranja kursora je... hm, recimo trosenje resursa ;).

Pozz
[ reiser @ 13.08.2012. 22:13 ] @
Slazem se da je Timer bolje resenje od pisanja posebnog threada. Posto nisam upucen, mozes li mi objasniti na sta treba da pazim ako hocu da koristim ProcessMessages() umesto nekog drugog vida sinhronizacije?
[ Rapaic Rajko @ 13.08.2012. 23:28 ] @
Hm, nisam siguran da dobro razumem tvoje pitanje. Ali odgovoricu kako sam razumeo.

1) Ako se koristi timer, nema potrebe za ProcessMessages. Zasto? Zato sto je OnTimer event zapravo obrada sistemske poruke WM_TIMER. Cim se izadje iz event-a, mainthread je slobodan za dalji rad/poruke (Win je event-oriented OS, zar ne?)
2) Ako se ostaje pri originalnom kodu autora teme, onda treba modifikovati while petlju, recimo ovako:

Code:

fScrollStopped := false; // pocinjemo skrolovanje kursora
while not(PDM.cdsRedVoznje.Eof) and not(fScrollStopped) do 
begin
  if GetAsyncKeyState(VK_ESCAPE) = 1 then begin
    exit;
  end ;

  PDM.cdsRedVoznje.Next ;
  PDM.cdsRelacije.Filter   := 'FIRMA_ID = ' + PDM.cdsRedVoznjeFIRMA_ID.AsString  + 'AND PRAVAC_ID = ' +
                                      PDM.cdsRedVoznjePRAVAC_ID.AsString  + 'AND CIJENA>0';
  DBGrid2.Refresh ;

 // SysUtils.Sleep(PDM.cdsTimeParamTimeScroll1.AsInteger*1000) ;
  SmartSleep(PDM.cdsTimeParamTimeScroll1.AsInteger*1000); // ovde imamo ProcessMessages

end ;


Varijabla fScrollStoped je tipa boolean, pripada formi na kojoj se sve nalazi. Recimo da je u nekom handler-u neke kontrole (OnClick, onChange itd.) postavljamo na true; autor ce znati vec KAD zeli da prekine skrolovanje kursora. Application.ProcessMessages (unutar SmartSleep()) ce omoguciti da se taj handler i IZVRSI; na taj nacin izlazimo iz petlje, i sve funkcionise taman kako treba.
Uostalom, evo i kako izgleda originalna funkcija SmartSleep(), koju sam pisao/koristio za rad unutar thread-a:

Code:

procedure TMyThread.SmartSleep(aMils: cardinal); // milisekunde
var
  aFutureTick: cardinal;
begin
  aFutureTick := GetTickCount + aMils;
  while (GetTickCount < aFutureTick) and not(Terminated) do
    Sleep(20); // bilo sta vece od 0, ali dovoljno malo da kod 'tece'  
end;


Terminated se setuje negde drugde (drugi thread, handler, ko ce znati). Zgodan trik, zar ne?

Pozz

P.S. Izvinjavam se sto za operator not koristim zagrade; navika iz ranijih vremena.

P.P.S. Sad videh i onaj nesrecni exit, uh, uh... to je meni isto kao i goto; totalno neprihvatljivo u Pascal-u. Ali to je samo moje misljenje.
[ reiser @ 14.08.2012. 00:00 ] @
Da, to sve lepo zvuci u teoriji, ali u praksi cesto ne radi kako treba :) Sada nemam vremena da napisem prost demo koji ovo potvrdjuje, ali sam se susretao u n slucajeva gde ProcessMessages() jednostavno ne refreshuje iscrtavanje prozora, ili blokira, itd.. Takodje, jednostavno je neprirodno da se na silu tera glavni thread da procesuira windows poruke, jednostavno treba da se pazi da glavni thread uvek bude non-blocking po mogucstvu.
[ Rapaic Rajko @ 14.08.2012. 11:10 ] @
Potpuno se slazem, glavni thread treba da se bavi samo porukama; to si 100% u pravu.
Medjutim, sve sto korisnik uradi ili kontrola 'odreaguje', svodi se opet na poruke/event-e. I onaj kod gore (while petlja) je u nekom handler-u neke poruke (event-u), tako da, potrebna je izuzetna disciplina/vestina kodiranja da se glavni thread BASH oslobodi/rastereti 'worker' taskova.

Sve u svemu, uzivao sam u nasoj maloj diskusiji :).

Svako dobro i srdacan pozdrav

Rajko

[ juha5803 @ 14.08.2012. 12:23 ] @
Otoplilo, više ne mrzne.

Zavhaljujem na savjetima, možda je pomoglo još nekome