05. december 2002 - 09:17Der er
25 kommentarer og 1 løsning
Fælles cursor for alle komponenter på formen
Jeg er rendt ind i et ret ejendommeligt problem. Jeg har et program, der skal hente nogle data fra en database. Mængden varierer fra 5 til 80000+ rækker. Ikke underligt, kan det tage nogle sekunder, før programmet er klart til at fremvise et af de store datasæt i en tilhørende DBChart. Mit problem er, at programmet ikke svarer, mens det henter data, og jeg har haft lidt bøvl med at sætte cursoren til at indikere "optaget". Min kommando Cursor:=crSQLWait; giver ingen resultat. Jeg lavede et lille test program på et tidspunkt, der også henter de store datasæt, men har ikke meget andet på formen end et par knapper, labels og et DBGrid. Der opdagede jeg, at når musen befinder sig over en af komponenterne, har den crDefault cursoren, mens den over de "tomme" områder af formen får den rigtige - crSQLWait - cursor. Ved at indsætte noget ekstra kode kunne jeg til nød fikse problemet i test programmet. Jeg kan ikke huske den nøjagtige ordlyd (det ligger på en anden maskine), men noget i denne retning : for i:=0 to FormMain.ControlCount-1 do TControl(FormMain.Controls[i]).Cursor:=crSQLWait; I det nye program er der mange flere komponenter, der placeret på forskellige paneler. Er der ikke en mere simpel måde at sætte en fælles cursor for hele programmet ?
Nåh ja, er der en måde, at få programmet til blive ved med at gentegne formen, mens den er ved at hente data ?
det er rigtigt det som morten siger, men der er lige en ting man skal tænke på. Hvis du har et loop som kører igennem 80000 eller flere gange, så bliver Application.ProcessMessages jo udført mere end 80.000 gange hvilket vil sløve hentningen af dataene meget drastisk.
I stedet bør du sætte en variabel som tæller op i loopet (hvis der ikke allerede er det i MyDataSet) og så kun køre Application.ProcessMessages hver 1000'e gang i løkken eller lignende. Prøv dig frem.
counter := 0; while not MyDataSet.Eof do begin //Hente data Inc(Counter); if Counter mod 1000 = 0 then Application.ProcessMessages; end;
hmmm...jeg har aldrig rodet med andet end MySQL databaser i Delphi ved hjælp af nogle smarte units, så jeg kender ikke til hvordan det fungerer med de indbyggede komponenter i Delphi.
Jeg så bare mortens kommentar og ville bare lige optimere den lidt.
Men den henter altså alt indholdet efter du har kaldt ADODataSet.Open; ? Er ADODataSet en komponent ? Har den så ikke nogle events man kan bruge til noget i denne sammenhæng ?
ADODataSet er en komponent i Delphi 6 og den har en OnFetchProgress event. Jeg skrev en eventhandler til den og satte et breakpoint deri, men programmet udførte aldrig den kode ...
Jeg fandt ud af, at man kan sætte eoAsyncFetch i ExecuteOptions property for et ADODataSet. Så kommer der nemlig OnFetchProgress events.
Jeg har nu en Gauge komponent til vise fremskridtet. Desværre er det kun Gauge komponenten, der bliver opdateret, når jeg kalder Application.ProcessMessages ...
Når du kalder Application.ProcessMessages, udfører Windows de jobs som ligger og venter i messageskøen, og så vidt jeg ved repainter den også din applikation hvis der er behov for det
flere points er altid godt ;) men her har du svaret.
var Save_Cursor:TCursor; Begin Save_Cursor := Screen.Cursor; Screen.Cursor := crHourGlass; { Show hourglass cursor } try { Do some lengthy operation } finally Screen.Cursor := Save_Cursor; { Always restore to normal } end; end;
btw: Application.ProcessMessages; er nok en af de hurtigste rutiner at lave hvis der ikke sker noget, så jeg ville fortrække at kalde den hele tiden, så programmet ikke virker som om det er gået i stå, eller hakker.
koden herunder tog 70 ms. at udfører på en P4 1800 var Tid1:Cardinal; i:integer; Begin Tid1:=GetTickCount; for i := 0 to 1000000 do begin Application.ProcessMessages; end; showmessage(inttostr(Gettickcount-tid1)); end;
det har du ret i, men du er nødtil at håndtere dine beskeder på et eller andet tidspunkt alligvel, så det du mister ved at kører den hele tiden er uden betydning for de fleste applicationer. Hvis der kommer meget data i ens message cue (som tager langtid at håndtere), vil ens program jo bare lagge totalt når du endlig går igang med at proccesse den. ;)
men du har da ret i, at din rutine vil kunne spare tid go vil værer mere hensigtmæssigt i nogle sammenhæng.
//If debugging is the process of removing bugs, then programming must be the process of putting them in.
>>slowaterz Jeg har to ideer til dit loop problem! :)
1. Tråde! 2. Sæt en progressbar på så man kan se at programmet laver noget, men at det bare tager tid... En ide var måske at lave progressbaren i sin egen form... Jeg fandt selv en gang en komponent ved navn TFloatProg som egentlig bare var en form med en ProgressBar på som så havde nogle properties (meget let at lave selv)... Så er det ellers bare at opdatere den en gang i mellem... Jeg bruger den selv til et program der henter en hulens masse filer ind i et TListView, ud fra en fil-type jeg selv har oprettet, og der virker den aldeles udemærket! :)
Hvis du er for doven til selv at lave den bette form + progressbar der skal til, så er koden her:
Så vidt jeg husker får Application.ProcessMessages cursoren til at blive sat tilbage til default. Prøv at sætte Screen.Cursor := crSQLWait efter hvert eneste kald til Application.ProcessMessages.
slowaterz: så erstat med crSQLWait istedet for crHourGlass......
så den kommer til at se sådanne ud. var Save_Cursor:TCursor; Begin Save_Cursor := Screen.Cursor; Screen.Cursor := crSQLWait; { Show hourglass cursor } try { Do some lengthy operation } finally Screen.Cursor := Save_Cursor; { Always restore to normal } end; end;
hvis timeglasset kommer frem, så virker det. Jeg havde bare ikke set du brugte crSQLWait.
siz23 >> det var stort set det samme, jeg har prøvet - bare uden "try .. finally". Jeg satte cursoren til crSQLWait, men det var altid timeglasset, der kom frem.
Det kan være, zimp har ret og det er Application.ProcessMessages, der gør det ... Problemet er i så fald, at selvom jeg sætter cursoren for hver gang, hvadenten det er før eller efter, så bliver cursoren "nulstillet" inden, ma når at se den.
ok ok, nu sad jeg lige og kiggede lidt på hvilket component du brugte... ;)
først lidt fra delphi hjælpen. ************************************************************* Applies to TDBChart component
Declaration property ShowGlassCursor : Boolean;
Description Default Value: True The ShowGlassCursor property determines if TDBChart will change the current cursor shape to a glass cursor whenever records are retrieved from Tables or Queries. This notifies the user of record retrieval activity. For fast and small queries or tables you might want to set it to False, thus not disturbing the user.
Ja, men jeg kunne ikke få programmet til at opdatere cursoren ...
Så jeg endte med at gå tilbage til et af hermandsens forslag - jeg lagde operationen, der henter data ud i en tråd, som kun opdaterer min progress bar. Der var jeg vist for hurtig til at afvise hans svar ...
Projektet er siden afsluttet og afleveret. Hvis jeg kommer til at arbejde med det igen, vil jeg nok vælge en anden løsning ...
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.