Utworzenie informacji że program się uruchamia

0

Witam.
W moim programie jest dużo danych przygotowywanych i przetwarzanych w trakcie uruchamiania. Umieściłem je w sekcji FormCreate. Wszystko działa bardzo bobrze, jednakże pomimo iż mam raczej dobry sprzęt (i7 4GHz), to po uruchomieniu programu przez około 15 sekund wygląda jakby nic się nie działo. Próbowałem też umieścić te procedury w sekcji FormShow, lecz nic to nie zmieniło. Jeśli nie wywołuję tych procedur, to okno aplikacji pojawia się natychmiast po uruchomieniu.
Chodziło by mi o to, aby po uruchomieniu programu pokazywało się okno aplikacji, na którym mógłbym umieścić informację o uruchamianiu np. typu Label, a potem uruchomić te czasochłonne procedury. Zależy mi aby były one wywoływane tylko raz, a nie np OnActivate. Niestety nie udało mi się tego zrobić.
Proszę o pomoc.

4

To tak tylko podpowiem (bo nie programuje nic w Delphi), że może by tak ładowanie danych puścić w innym wątku i użytkownikowi wyświetlać info, że dane już załadowanie, a do tego czasu niech się kręci jakiś spinner czy jakieś splash logo? Ma to sens?

Link raczej stary, ale może sam coś lepszego znajdziesz

https://www.experts-exchange.com/articles/6613/Adding-threads-for-loading-data-in-background-to-a-delphi-application.html

5
Klakierus napisał(a):

Witam.
W moim programie jest dużo danych przygotowywanych i przetwarzanych w trakcie uruchamiania. Umieściłem je w sekcji FormCreate.

No to już zrobiłeś błąd – nie powinieneś takich rzeczy umieszczać w kodzie formularza. Klasy służące do zarządzania danymi powinny być od nich niezależne i być tworzone na zewnątrz nich.

Wszystko działa bardzo bobrze, jednakże pomimo iż mam raczej dobry sprzęt (i7 4GHz), to po uruchomieniu programu przez około 15 sekund wygląda jakby nic się nie działo.

Czyli na moim komputerze trwało by to z minutę. :]

Nie marnując czasu na buzzwordy, wszystkie klasy służące do przechowywania i zarządzania danymi oddziel od formularzy i zapisz w osobnych modułach. Natomiast wszystkie instrukcje ładujące dane czy je obrabiające (te, które zjadają te piętnaście sekund) wywołaj przed wywołaniem metod tworzących instancje formularzy (czyli pośrednio lub bezpośrednio w głównym pliku projektu).

Po drugie, zrób okno splash, które będzie widoczne na ekranie podczas ładowania danych. Utwórz je dynamicznie, przekazując Application jako Owner. Natomiast aby takie okienko reagowało na komunikaty, cały proces ładowania danych wydziel do wątku pobocznego (albo do kilku).

To tak z grubsza.

0

a ja jestem ciekaw (wiem, wiem, ciekawość to pierwszy stopień ...) cóż to za dane są generowane że aż tyle czasu zajmują? Nie jest to przypadkiem coś dociągane z netu?

2

a ja dam prostszy sposób. FormCreate i FormShow wywołuje sie tak na prawde przed wyrysowaniem okna, dlatego najlepszym rozwiązaniem jest stworzenie własnej symulującej coś na krztałt OnAfterShow

const
  WM_AFTER_SHOW = WM_USER + 300; 
type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  private
    procedure OnAfterShow(var Msg: TMessage); message WM_AFTER_SHOW;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.OnAfterShow(var Msg: TMessage);
begin
  // tutaj wywołaj swoje metody
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_AFTER_SHOW, 0, 0);
end;
0

Dziękuję za rady. Na razie skorzystam z porady kolegi flasher86. To co napisał furious programming muszę w wolnym czasie przemyśleć i zrozumieć, jestem na dużo niższym poziomie, piszę właściwie tylko pomoce dla siebie.
Co do pytania robertz68, to nie dane z netu, to wczytywanie i obróbka grafik w wysokiej rozdzielczości, wstępne przygotowanie kolorów, miejsc pod inne grafiki i napisy itp.
Dziękuję za pomoc.
Pozdrawiam.

0
flasher86 napisał(a):

FormCreate i FormShow wywołuje sie tak na prawde przed wyrysowaniem okna, dlatego najlepszym rozwiązaniem jest stworzenie własnej symulującej coś na krztałt OnAfterShow

No i w czym to niby pomoże? Nadal okno będzie zamrożone i do czasu przetrawienia danych nie będzie odpowiadało na komunikaty. Bo tutaj problemem nie jest moment przetwarzania danych, a wydzielenie tego procesu z głównego wątku, tak aby interfejs nie ”wisiał”.

Poza tym sterowanie interfejsem i zarządzanie oknami z poziomu zdarzeń konkretnego okna (np. głównego okna) to zła praktyka i nie powinno się tego robić. Okna mają żyć własnym życiem, a aplikacja powinna być zarządzana przez mechanizmy niezależne od formularzy.

0

Pewnie chodzi o to, by pojawiło sie okno/splash z info, że coś się dzieje, a po 30 sekundach ożyło. Bo teraz z tego co rozumiem to odpalasz program i bez znaków życia on przez 30 sekund odpala się nie daje znać, że żyje - w ten sposób ,możesz włączyć go drugi raz i zaraz masz całą kolekcje nowych procesów co wiszą i co jakiś czas będą się odpalać ;) Niemniej masz racje - ładowanie danych powinno być asynchroniczne.

0

somedev - dokładnie tak. Zrobiłem zgodnie z radą flasher-a i działa poprawnie.
Niestety dotychczas pisałem tylko proceduralnie i właśnie czytam o klasach/wątkach - próbuję nauczyć się tego, o czym napisał furious programming.

0

Gdy czytam co pisze kolega furious programming wszystko wydaje się jasne i logiczne. Poczytałem o tworzeniu wątków, troszkę to jest dla mnie zagmatwane, dlaczego nie można wprost uruchomić danej procedury w nowym wątku, należy tworzyć nowe klasy, ale nie ma co narzekać, tylko się nauczyć tego.
Zacząłem więc od przerzucenia procedur i funkcji przygotowujących i obrabiających dane do nowego modułu. Było trochę zamieszania, gdyż stale używane obrazy miałem jako zmienne globalne i użycie ich było niejawne, a tymczasowo używane obrazy były jako zmienne lokalne przekazywane jawnie. Udało się wszystko zrobić bez problemów, wszystkie przekazania zmiennych są teraz jawne.
Jednakże przy próbie tworzenia wątków zgłupiałem i mam kilka pytań. Abym to zrozumiał posłużę się przykładami, a nie moimi procedurami. Jeśli przykładowo mam taki program (piszę naprędce z głowy, nie kopiuję z kompilatora,więc możliwe są błędy):

var 
     obraz, wzor : TBitmap;
     kolor1, kolor2 : TColor;

procedure TForm1.obrobka(var obraz:TBitmap; wzor:TBitmap; kolor1, kolor2:TColor);
begin
   ...
end;

begin
   obraz:=TBitmap.Create;
   wzor.Picture.LoadFromFile('plik1.bmp');
   obrobka(obraz,wzór,kolor1,kolor2);
   ...
   obraz.Free;
end.

Następnie przerzucam procedurę "obrobka" do nowego modułu i przerabiam, aby później mogła być wywoływana w wątku

unit procedury;
...
type 
TprocZrobObraz=procedure(obj:TObject; obraz, wzor:TBitmap; kolor1, kolor2:TColor) of object;

procedure obrobka(obj:TObject; procZrobObraz:TprocZrobObraz);

implementation

procedure obrobka(obj:TObject; procZrobObraz:TprocZrobObraz);
var 
   obraz : TBitmap;
begin
   obraz:=TBitmap.Create;
   ...
   procZrobObraz(obj, obraz, wzor, kolor1, kolor2);
   obraz.Free;
end;

Jak w takim przypadku przekazać w programie głównym zmienne do danej procedury?
U mnie kompilator stale wywala że jest niezgodność danych, niemożna przypisać TBitmap do mojego nowego typu danych.
W jaki sposób powinienem deklarować dane które mają być zmienione - odpowiednik "var" (obraz) oraz te które są tylko do odczytu (wzór)?

3

Ależ można używać tego prościej od razu w innym wątku. To że @furious programming mówił o wątkach to sądzę, ze albo jest skrótem myślowym, albo po prostu nadal z tego korzysta, niemniej tutaj nie używał bym wątków, jako takich ale samego konceptu. Wątki jak zauważyłeś są nieco skomplikowane w użyciu, dlatego opakowano je w wyższa abstrakcje. Obecnie można wzorem C#, w prosty sposób tworzyć taski, które w dowolnym momencie mogą zostać utworzone i uruchomią kod do przetwarzania współbieżnego. Taski mają szereg zalet - są prostsze w użyciu, lepiej się synchronizują i prościej poczekać na zakończenie jakiegoś zadania, oraz co najważniejsze - task poprzez PPL (Parallel Programming Library , z kolei w C# jest TPL - Task Parallel Library), jest korelowany z wątkiem i to PPL zarządza osobnym wątkiem i dba, żeby było ok, do tego stopnia, że nie trzeba martwić się o ilość wątków, jakieś thread poolery etc. gdyż to wszystko robi za nas PPL- dla przykładu tworząc ręcznie wątki można stworzyć ich tyle, że program nie będzie nic robił tylko zmieniał konteksty na procesorze, natomiast używając PPL - wątków jest mniej niż tasków, niemniej to PPL tym zarządza by wszystko wykonywało się równolegle. Uważam, że do Twojego problemu lepszy będzie TTask niż twardy wątek, no i bardziej nowocześnie i łatwo.

Masz sznurek: http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_TTask_from_the_Parallel_Programming_Library

Przepiszę przykład

procedure TForm1.Button1Click(Sender: TObject);
var
 aTask: ITask;
begin
 aTask := TTask.Create (procedure ()
   begin
     sleep (3000); // 3 seconds
     ShowMessage ('Hello');
   end);
 aTask.Start;
end;

Tutaj po kliknięciu przycisku metoda szybko się skończy i program będzie odblokowany, a po 3 sekundach nagle wyskoczy messagebox. Tutaj definiujesz podczas założenia taska procedurę, która się wykona. Coś takiego samego możesz osadzić w ShowForm, żeby asynchronicznie zacząć wczytywać pewne dane.

0
furious programming napisał(a):

No i w czym to niby pomoże? Nadal okno będzie zamrożone i do czasu przetrawienia danych nie będzie odpowiadało na komunikaty. Bo tutaj problemem nie jest moment przetwarzania danych, a wydzielenie tego procesu z głównego wątku, tak aby interfejs nie ”wisiał”.

U siebie w programie jak miałem podobną sytuację, to tez stworzyłem własne AfterShow dokładnie w taki sposób jak kolega chciał. Po prostu takie było wymaganie, że najpierw ma się pokazać całe okno główne. Wtedy dopiero ma zacząć się ładowanie danych.

Co do wydzielenia tworzenia danych do oddzielnego wątku, to dyskutowałbym czy zawsze istnieje taka potrzeba. Jaki jest tego sens w tym przypadku skoro i tak z programu nie da się skorzystać w tym momencie, a dane muszą być przetworzone? Natomiast przerwanie ładowania danych można osiągnąć tak samo bez tworzenia nowego wątku.

2

Ładowanie asynchroniczne jest sensowne wtedy kiedy można korzystać z innych funkcji programu nie wymagających danych. Dodatkowo jest lepsze UX, jeśli aplikacja coś robi i nie zamarza na ekranie, bez możliwości odrysowania, czy przesunięcia okna.

0

Jeszcze jedno pytanie, czy w Delphi poszczególne taski korzystają z kopii zmiennych lokalnych, czy też będą sobie te zmienne wzajemnie nadpisywać?
Pytam, ponieważ narobiłem ręcznie kopii zmiennych dla każdego taska, a syn piszący w C powiedział że to raczej bez sensu, że w C każdy nowy task operuje na swojej kopii zmiennych.

0

W C nie ma taskow ;) Może chodzi o C#? Pokaz kawałek kodu.

0
somedev napisał(a):

W C nie ma taskow ;) Może chodzi o C#? Pokaz kawałek kodu.

Nie wiem, w jakim C on pisze, teraz jest na uczelni, jak wróci to spytam dokładniej.
Po prostu wychodząc rano z domu zwrócił mi uwagę, on nie zna Delphi więc pytam na forum.

0

No to pokaz kawałek kodu bo nie wiem za bardzo o jakie zmienne i gdzie się Tobie rozchodzi (Twój kod)

0

Chodziło mi o zmienne lokalne procedury, np:

procedure RobCos(var param:byte);
var task:array of ITask;
    zmienna:byte;

begin
   zmienna:=5;
   SetLength(task,zmienna);
   task[0]:=TTask.Create(procedure() begin
     zmienna:=10;
   end);
   task[0].Start;
   task[1]:=TTask.Create(procedure() begin
     zmienna:=20;
   end);
   task[1].Start;
   param:=zmienna;
end;

czy każdy task ma własną kopię zmiennej i na koniec tasku zmienna pozostaje niezmieniona , czy nawzajem sobie tą zmienną nadpisują.
Sądzę że sobie nadpisują, bo już kompilator żądał aby liczniki pętli były zmiennymi lokalnymi wewnątrz tasków.

  var i:byte
begin
   task[1]:=TTask.Create(procedure() begin
       for i:=0 to 10 do

było źle, a poprawnie było:

begin
   task[1]:=TTask.Create(procedure() var i:byte
     begin
       for i:=0 to 10 do

wobec czego każdy task u mnie używa tylko swoich lokalnych zmiennych.

1
somedev napisał(a):

To że @furious programming mówił o wątkach to sądzę, ze albo jest skrótem myślowym, albo po prostu nadal z tego korzysta, niemniej tutaj nie używał bym wątków, jako takich ale samego konceptu.

Miałem na myśli to, aby ładowanie danych przenieść do wątku pobocznego. A to czy skorzystamy z niego pośrednio czy bezpośrednio, zależy od tego jakie klasy mamy dostępne. Można użyć klasy TThread, można TTask – jak kto woli/umie. Byle tego użyć. ;)


Mr.YaHooo napisał(a):

U siebie w programie jak miałem podobną sytuację, to tez stworzyłem własne AfterShow dokładnie w taki sposób jak kolega chciał. Po prostu takie było wymaganie, że najpierw ma się pokazać całe okno główne. Wtedy dopiero ma zacząć się ładowanie danych.

I tak samo miałem w swoim platformerze. On jest aplikacją stricte jednowątkową, więc gdy odpalana jest pętla główna (wykonująca kod gry), to okno musi już być widoczne na ekranie, a OnShow wywoływane jest zanim okno fizycznie się pojawi. W Delphi działającym rozwiązaniem jest właśnie AfterShow, a w Lazarusie można to zrobić prościej – wymusić pokazanie okna w OnShow i tak mam teraz:

procedure TGameForm.FormShow(ASender: TObject);
begin
  ShowWindow(Handle, SW_SHOWNORMAL); // tu wywalamy formularz na ekran
  Game.Start(); // tu startujemy główną pętlę gry
end;

Jeśli zaremuję linijkę z ShowWindow to gra będzie działać normalnie, ale okno nigdy się nie pojawi. Natomiast wołanie ProcessMessages po przetworzeniu każdej klatki gry pozwala wykonywać pętlę komunikatów okna na bieżący, czyli odmalowywać okno i je przesuwać/rozciągać.

No i tak – tutaj możemy z tego skorzystać, bo takie jest założenie. Jeśli założenie jest takie, że dany kod ma być wykonany tuż po tym jak okno pojawi się na ekranie to możemy skorzystać z AfterShow lub z tego co podałem dla innego IDE. Jeżeli natomiast kod ma się wykonywać zupełnie niezależnie to tylko wątki poboczne.

Co do wydzielenia tworzenia danych do oddzielnego wątku, to dyskutowałbym czy zawsze istnieje taka potrzeba.

Nie zawsze istnieje – mój platformer jest jednym z takich przypadków, gdy wątki nie są konieczne. Podobnych przypadków jest masa, jednak nie jest nim przypadek opisany w tym wątku. Chyba że jego autor nie zamierza zadbać o to, aby splash reagował na komunikaty (o istotności ich przetwarzania niżej).

Jaki jest tego sens w tym przypadku skoro i tak z programu nie da się skorzystać w tym momencie, a dane muszą być przetworzone?

Taki, że procedura obsługi komunikatów okna nie jest wstrzymywana podczas ładowania danych. A to oznacza, że dopóki dane są wczytywane, dopóty okno nie reaguje na żadne akcje. Nie da się go przesunąć, nie jest przemalowywane, nie reaguje na komunikaty myszy i klawiatury, ani na żadne inne. W przypadku splasha, najbardziej istotne jest odmalowywanie okna – które nie jest obowiązkowe, jednak wpływa na wrażenia estetyczne.

Przykładem poprawnej implementacji ekranu wizytówki jest GIMP. Pierwszy jego rozruch na moim laptopie trwa około pół minuty. W tym czasie ładowane są dane, a w splashu zmienia się treść etykiety i stan progressbara. Aby nie gapić się na splasha jak w święty obrazek, w miedzyczasie otwieram sobie katalogi czy robię coś w innych programach. Następnie minimalizuję wszystko i wracam do splasha GIMP-a. Ten ładnie się odmalowuje, kończy ładowanie i uruchamia edytor. Wszystko wygląda poprawnie, estetycznie.

Natomiast przykładem niepoprawnej implementacji splasha jest… Lazarus. Ten uruchamia się w kilka sekund, jednak ładowanie danych zrealizowane jest w taki sposób, o jakim napisałeś @flasher86 i Ty (rozwiązanie à la AfterShow) – czyli bez jakiejkolwiek synchronizacji. Okno splasha nie reaguje na żadne komunikaty i nie przemalowuje swojej powierzchni, więc jeśli przesunę nad nim okno, to wyryję w nim dziurę (która nigdy nie zostanie z powrotem zamalowana). A wygląda to paskudnie:

splash fail.png

Przy czym różnica pomiędzy splashami programów GIMP i Lazarus jest taka, że wizytówka tego pierwszego zawiera komponenty o zmiennej zawartości (etykiety z bieżącą operacją oraz progressbar), a tego drugiego jest zwykłą grafiką o statycznej zawartości. Tak więc splash programu GIMP musi być przemalowywany na bieżąco (w przeciwnym razie wyświetlanie informacji o postępie nie będzie miało sensu), a wizytówka Lazarusa nie musi (choć powinna, ze względu na estetykę).

Co prawda da się napisać kod ładujący w głównym wątku, w taki sposób, aby nie blokować kolejki komunikatów okna wizytówki, ale również zależy to od konkretnego przypadku. Bo inaczej można podejść do tego tematu, jeśli ładowanie danych trwa np. minutę i składa się z kilku wywołań długotrwale działających metod (np. po 10+ sekund na każde wywołanie), a inaczej, gdy trwa tyle samo, ale składa się z wywołań setek metod pracujących krótkotrwale (np. po góra 100ms na każde wywołanie).


Klakierus napisał(a):

czy każdy task ma własną kopię zmiennej i na koniec tasku zmienna pozostaje niezmieniona , czy nawzajem sobie tą zmienną nadpisują.

Przejdź się do dokumentacji/wiki i poczytaj o metodach anonimowych. ;)

0

Aby przyśpieszyć uruchamianie programu, chciałem przetwarzać kilka obrazów jednocześnie. Używam Win7 64bit, program podczas uruchamiania zużywa 10-15% czasu procesora i 60MB pamięci.
Niestety stworzenie więcej niż jednego wątku powoduje błąd "Out of resources" gdy Firefox zajmuje 3 procesy i zużywa łącznie ponad 500MB pamięci.
Jak zwiększyć dostępność pamięci dla programu? W kompilatorze znalazłem tylko wielkość stosu.

0

Można użyć zmiennej z danej procedury wywołującej taska i wtedy wszystkie taski będą miały tą samą zmienną. Jeśli z kolei zadeklarujesz zmienną w samym tasku to jest ona w każdym inna, zresztą to są metody anonimowe czyli to są zupełnie dwie inne metody mimo, że mają podobne ciało. Możesz wywołać w środku zewn. procedurę, ale wtedy też w obrębie jej samej będzie iny stos. Jeśli użyjesz zmiennej z procedury która wywołuje task, to oczywiście ją zmienisz, nie jest ona kopiowana w obrębie taska. Zresztą w c# jest z tego co pamiętam podobnie. Zobacz efekt tego kodu:

procedure TForm1.Button1Click(Sender: TObject);
var task:array of ITask;
    zmienna:byte;
begin
   zmienna:=5;
   SetLength(task,zmienna);

   task[0]:=TTask.Create(procedure()
    var liczba : byte;
    begin
     liczba := 2;
     zmienna:=zmienna + liczba;
   end);
   task[0].Start;

   task[1]:=TTask.Create(procedure()
    var liczba : byte;
    begin
     liczba := 3;
     zmienna:= zmienna + liczba;
   end);
   task[1].Start;

   task[0].Wait();
   task[1].Wait();

   ShowMessage(zmienna.ToString());

end; 
0
Klakierus napisał(a):

Aby przyśpieszyć uruchamianie programu, chciałem przetwarzać kilka obrazów jednocześnie. Używam Win7 64bit, program podczas uruchamiania zużywa 10-15% czasu procesora i 60MB pamięci.
Niestety stworzenie więcej niż jednego wątku powoduje błąd "Out of resources" gdy Firefox zajmuje 3 procesy i zużywa łącznie ponad 500MB pamięci.
Jak zwiększyć dostępność pamięci dla programu? W kompilatorze znalazłem tylko wielkość stosu.

Pokaż kod - może gdzieś tworzysz w pętli w cholerę wątków i nie zdajesz sobie sprawy, że przeginasz. Dodatkowo jaki masz komputer? Jakie jest jego wykorzystanie przy komunikacie?

0

Jestem pewien że nigdzie nie tworzę wątków w pętli. Kod jest zbyt duży żeby go przedstawiać.
Albo mam gdzieś jakiś przeciek, albo to darmowe Delphi 10.3 jest niezbyt tego. Wcześniej program się bardzo dziwnie zachowywał, nie będę opisywać wszystkiego, tylko 2 przykłady. Zanim wstawiłem splash screen był tylko Label ukrywany po uruchomieniu, w założeniu, bo czasem się pojawiał, a czasem nie. Po zmianie w trakcie uruchamiania na visible:=false a po uruchomieniu visible:=true, działał zgodnie z zapisem, po przywróceniu (uruchamianie - true, po uruchomieniu - false) znowu raz się pojawiał a raz nie.
Przy wątkach czasami nie wychodził z wątka, a po 10 minutach debugowania dawałem spokój. Założyłem zmienną stan:byte i każdy z wątków ustawiał po zakończeniu odpowiednie bity na zmiennej stan. Przy debugowaniu się zdziwiłem, gdy pierwsza linia w procedurze "stan:=0" zarówno przed jej wykonaniem jak i po jej przejściu debuger twierdził że wartość jest niedostępna. Przerwałem na parę godzin. Zacząłem od początku (tworzenie wątków) i jak na razie działa. Czas uruchamiania poprawił się do 5-6 sekund, a wykorzystanie procesora wzrosło do ponad 60% przy uruchamianiu. Szukałem przecieków, lecz nic nie znalazłem.
Wcześniej wymieniony Label dalej działał jak chciał, więc za radą @furious programming zrobiłem splasha. Wszystko było dobrze oprócz tego, że miałem na pasku 2 ikony programu (jedna wskazywała na program, druga na pustą aplikację). Zamknięcie którejkolwiek z ikon zamykało program.
Zmieniłem delphi Application.MainFormOnTaskbar := False; i wygląda wszystko dobrze (na razie).

0
Klakierus napisał(a):

Wcześniej wymieniony Label dalej działał jak chciał, więc za radą @furious programming zrobiłem splasha. Wszystko było dobrze oprócz tego, że miałem na pasku 2 ikony programu (jedna wskazywała na program, druga na pustą aplikację).

Obstawiam, że nie ustaliłeś poprawnie Ownera w konstruktorze tego okna.

0
begin
  Application.Initialize;
  Screen:=TScreen.Create(Application);
  Screen.Show;
  Screen.Update;
  Application.MainFormOnTaskbar := False;
  Application.CreateForm(TForm1, Form1);
  Form1.Przygotowanie;
  Application.CreateForm(TKonfig, Konfig);
  Application.CreateForm(TPodgWydr, PodgWydr);
  Application.CreateForm(TWczytQR, WczytQR);
  Screen.Close;
  Screen.Free;
//  Screen.Destroy;
  Application.Run;
end.
0
furious programming napisał(a):

I tak samo miałem w swoim platformerze. On jest aplikacją stricte jednowątkową, więc gdy odpalana jest pętla główna (wykonująca kod gry), to okno musi już być widoczne na ekranie, a OnShow wywoływane jest zanim okno fizycznie się pojawi. W Delphi działającym rozwiązaniem jest właśnie AfterShow, a w Lazarusie można to zrobić prościej – wymusić pokazanie okna w OnShow i tak mam teraz:

O ile dobrze kojarzę, to w C++ Bilderze też to tak działa. Ja wybrałem sposób z wysłaniem komunikatu oraz obsłużeniem go za pomocą mechanizmu BEGIN_MESSAGE_MAP END_MESSAGE_MAP po prosto tak według mnie jest bardziej elegancko.

furious programming napisał(a):

Nie zawsze istnieje – mój platformer jest jednym z takich przypadków, gdy wątki nie są konieczne. Podobnych przypadków jest masa, jednak nie jest nim przypadek opisany w tym wątku. Chyba że jego autor nie zamierza zadbać o to, aby splash reagował na komunikaty (o istotności ich przetwarzania niżej).

Według mnie dobrze zaprojektowana aplikacja oprócz pokazania samego splasha powinna pokazywać na nim również jakiś progressbar żeby było faktycznie wiadomo, że coś się dzieje. Jednak jeśli mamy do czynienia z wykonywaniem w wątku głównym ładowania całego programu też przecież jest możliwość aby całość ładnie reagowała na komunikaty.

Taki, że procedura obsługi komunikatów okna nie jest wstrzymywana podczas ładowania danych. A to oznacza, że dopóki dane są wczytywane, dopóty okno nie reaguje na żadne akcje. Nie da się go przesunąć, nie jest przemalowywane, nie reaguje na komunikaty myszy i klawiatury, ani na żadne inne. W przypadku splasha, najbardziej istotne jest odmalowywanie okna – które nie jest obowiązkowe, jednak wpływa na wrażenia estetyczne.

A co za problem podczas aktualizacji postępu w progressbarze dać ProcessMessagess? Wtedy ładnie wszystko działa. Tak wiem, jest to dość brzydki sposób i wiele osób go krytykuje. Jednak czasami warto sięgnąć po taki sposób, szczególnie jeśli ktoś jest niedoświadczony w pracy z wątkami i potem w pewnym momencie coś mu wybuchnie..

Co prawda da się napisać kod ładujący w głównym wątku, w taki sposób, aby nie blokować kolejki komunikatów okna wizytówki, ale również zależy to od konkretnego przypadku. Bo inaczej można podejść do tego tematu, jeśli ładowanie danych trwa np. minutę i składa się z kilku wywołań długotrwale działających metod (np. po 10+ sekund na każde wywołanie), a inaczej, gdy trwa tyle samo, ale składa się z wywołań setek metod pracujących krótkotrwale (np. po góra 100ms na każde wywołanie).

O i tu mamy decydującą kwestię. Jeśli ja u siebie mam dużo krótkich wywołań, wtedy to wszystko ma sens. ProgressBar idzie gładko do przodu. Jeśli mam mało wywołań które trwają wieki, takie coś jest bez sensu. Bo i tak wtedy trzeba odczekać te parę (czasem paręnaście) sekund nim kolejny krok dobiegnie końca i zostanie wywołane magiczne ProcessMessages

0

@Klakierus: utwórz okno splasha podając w konstruktorze nil – spróbuj w ten sposób.

0
furious programming napisał(a):

@Klakierus: utwórz okno splasha podając w konstruktorze nil – spróbuj w ten sposób.

Gdy dawałem nil to wcześniej nie zamykał się splash po zamknięciu programu. Teraz nie ma różnicy.
Jeśli użyję Application.MainFormOnTaskbar := True; to pozostaje jedna ikona po zamknięciu programu.

Program na razie działa tak jak chciałem. Dziękuję wszystkim za pomoc.

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