Kontrola ładowania i zamykania aplikacji – jakie macie patenty?

Kontrola ładowania i zamykania aplikacji – jakie macie patenty?
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0

Mamy większą aplikację lub grę (nieistotne), która podczas rozruchu ładuje różne dane z plików zewnętrznych – zawartość plików konfiguracyjnych, obrazy itd. Niektóre dane są wymagane (np. obrazy), bez których program nie może działać prawidłowo, a niektóre są potrzebne (np. konfiguracja), choć możliwe jest pominięcie ich ładowania. Podczas zamykania programu, zwalniana jest pamięć i pewne dane są z powrotem zapisywane na dysku (np. konfiguracja).

Kod źródłowy składa się z dziesiątek klas, służących do reprezentowania tych danych. Jakieś listy z obrazami, drzewka obiektów z najróżniejszymi danymi zassanymi z konfigów itd. Niektóre z tych obiektów opakowują po kilka innych, tworząc drzewka.

I teraz meritum – weźmy pod lupę rozruch. Podczas ładowania danych może się zdarzyć, że któregoś potrzebnego pliku nie ma (np. obraz) i leci wyjątek, albo zawartość pliku konfiguracyjnego jest skopana (zmatolona struktura xml) i trzeba zapytać użytkownika co robić – resetować czy anulować rozruch (ubić program). To samo przy zamykaniu programu – nie da się zapisać konfiguracji, bo odmowa dostępu lub jakikolwiek inny problem.

W jaki sposób kontrolujecie obsługę błędów i dajecie użytkownikowi możliwość wyboru co robić? A dokładniej pytając, w jaki sposób ładujecie dane do obiektów, aby z jednej strony dało się poinformować użytkownika i dopytać co robić jeśli wystąpi błąd, a z drugiej strony, aby kod źródłowy nie wyglądał jak śmietnik?


Samo zagadnienie wydaje się trudne do ogarnięcia, więc dorzucę jeszcze trochę, coby było ciekawiej. ;)

Mamy obiekt, który wewnątrz posiada osadzonych kilka mniejszych. Główny obiekt np. reprezentuje całe drzewo z pliku konfiguracyjnego i ma główną metodę LoadFromFile. W tej metodzie otwierany jest plik konfiguracyjny, a nastepnie wołane są metody LoadFromTree każdego osadzonego obiektu, przekazując im konkretną gałąź drzewa konfiguracji. Każdy z tych obiektów ładuje swoje porcje danych.

Meritum – kilka osadzonych obiektów załaduje swoje porcje, ale jeden z nich nie będzie w stanie i walnie wyjątkiem. W jaki sposób obsłużycie ten błąd, tak aby użytkownik dostał komunikat i mógł pominąć ładowanie danej gałęzi lub całkowicie przerwać proces rozruchu? Pozwolicie wydostać się wyjątkowi poza główną metodę LoadFromFile czy obsłużycie go wewnątrz?


Ogólnie chciałbym się dowiedzieć jakie macie techniki pisania kodu realizującego taką kontrolę błędów i interakcję z użytkownikiem. Ja wiem, że najłatwiej byłoby komunikat wyświetlić wewnątrz LoadFromFile i w razie wciśnięcia przez użytkownika przycisku Abort po prostu dupnąć Halt i mieć problem z głowy, ale takie coś raczej nazwałbym potworkiem, a nie dobrą architekturą programu.

No, zapraszam do dyskusji.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 5x, ostatnio: flowCRANE
somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
1

Zrób fabryki do wczytywania danych i tworzenia obiektów. Obiekty wewnętrzne powinny implementować dany interface ale tworzone winny być na zewnątrz w fabryce a klasa pobierana z kontenera żeby oddzielić używanie klasy od implementacji. Takie obiekty powinny być wstrzykiwane przez konstruktor lub właściwość. Ofc dialog powinien być w kodzie interfacu gdzie wywołuje fabrykę - fabryka zwraca wyjątek i w kodzie usera a nie fabryki pytasz czy haltowac czy kontynuować kreacje.

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0

Masz jakiś krótki PoC ilustrujący to o czym napisałeś? Albo jakąś lekturę?

Początkowo (bo deadline potwornie gonił…) klasy zaprojektowałem w sposób zgodny z uniwersalnymi kontenerami, takimi jak np. TStrings, czyli każdą wyposażyłem w metodę LoadFrom*. To pozwoliło mi szybko zaimplementować ładowanie danych, a na kontrolę błędów i tak nie było czasu, więc się tym nie przejmowałem – drutowałem kod na szybko (i teraz mam za swoje). Fabrykę bym zrobił, ale musiałbym sporo kodu przerobić, a nie za bardzo mi się to opłaca – dlatego dopytuję o ciekawe pomysły, niekoniecznie logiczne/popularne. ;)

Jeden z takich ”dziwnych” pomysłów mam, który pozwoli dodać pełną kontrolę błędów i interakcję z użytkownikiem, bez dużych zmian w kodzie i robienia bajzlu, ale na razie nie będę pisał o nim, coby nie podpowiadać.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 5x, ostatnio: flowCRANE
somedev
  • Rejestracja:ponad 6 lat
  • Ostatnio:prawie 5 lat
  • Postów:666
2

Moim zdaniem ta dokumentacja nie dokumentuje jedynie frameworka (który nie jest tylko frameworkiem do jednego np. dostępu do bazy, czy robienia webapi - mORMot posiada tak wiele elementów, że jest jakby sposobem na kreacje aplikacji), a dodatkowo zawiera bardzo dobre wstępy teoretyczne do różnych podejść, pokazując na Pascalu wszystko. Rozdział 15.2 powinien pokazać co chciałem przekazać, natomiast całość jest pożyteczna. Dokumentacja trochę waży bo 25MB, ale pobrałem sobie i czytam. Uważam, że to skarbnica wiedzy. Z drugiej strony warto zobaczyć jakie podejście jest np. w C# np, poczytaj o Autofacu czy kontenerze DI z .net core.

WL
Albo seria artykułów o DI w Spring4D; a nie czekaj, to FPC i nie dla nich Spring4D :P...
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0

Nie pasuje mi tu fabryka – to bez sensu. No bo jak, mam tworzyć fabrykę, która tylko raz w trakcie sesji stworzy tylko jednen obiekt? Gdybym miał mieć cały zestaw obiektów do utworzenia, gdzie każdy obiekt ma wspólną klasę-rodzica, to by to miało ręce i nogi, ale tak to tylko zapaskudzę kod i dołożę sobie masę roboty.

Tutaj potrzeba czegoś na kształt callbacków, aby uniknąć implementowania logiki dotyczącej interakcji z użytkownikiem wewnątrz klas z danymi. Ewentualnie coś w formie zdarzeń wstrzykiwanych z zewnątrz.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0
furious programming napisał(a):

Mamy większą aplikację lub grę (nieistotne), która podczas rozruchu ładuje różne dane z plików zewnętrznych – zawartość plików konfiguracyjnych, obrazy itd. Niektóre dane są wymagane (np. obrazy), bez których program nie może działać prawidłowo, a niektóre są potrzebne (np. konfiguracja), choć możliwe jest pominięcie ich ładowania. Podczas zamykania programu, zwalniana jest pamięć i pewne dane są z powrotem zapisywane na dysku (np. konfiguracja).

Kod źródłowy składa się z dziesiątek klas, służących do reprezentowania tych danych. Jakieś listy z obrazami, drzewka obiektów z najróżniejszymi danymi zassanymi z konfigów itd. Niektóre z tych obiektów opakowują po kilka innych, tworząc drzewka.

To, tak naprawdę nie ma znaczenia, ponieważ mięso problemu leży gdzie indziej.

Ale tak przy okazji; ja zawsze takich przypadkach opieram się na automatycznej serializacji i deserialziacji obiektów.
To mnie całkowicie zwalnia z ręcznym wiązaniem obiektów z danymi i łatwo pozwala na rozwijanie modelu danych dla konfiguracji z pełnym wsparciem odczytu konfiguracji z różnych wersji modelu danych obiektów konfiguracji.
Tu jest trochę zabawy, aby zrobić to ogólnie, ale warto.

I teraz meritum – weźmy pod lupę rozruch. Podczas ładowania danych może się zdarzyć, że któregoś potrzebnego pliku nie ma (np. obraz) i leci wyjątek, albo zawartość pliku konfiguracyjnego jest skopana (zmatolona struktura xml)

Przy poprawnej deserializacji jesteś odporny na takie problemy.
Poprawnej, tj takiej, która potrafi odczytać dane z pliku, który nie jest w 100% zgodny z modelem obiektowym.

i trzeba zapytać użytkownika co robić – resetować czy anulować rozruch (ubić program). To samo przy zamykaniu programu – nie da się zapisać konfiguracji, bo odmowa dostępu lub jakikolwiek inny problem.

W jaki sposób kontrolujecie obsługę błędów i dajecie użytkownikowi możliwość wyboru co robić? A dokładniej pytając, w jaki sposób ładujecie dane do obiektów, aby z jednej strony dało się poinformować użytkownika i dopytać co robić jeśli wystąpi błąd, a z drugiej strony, aby kod źródłowy nie wyglądał jak śmietnik?


Samo zagadnienie wydaje się trudne do ogarnięcia, więc dorzucę jeszcze trochę, coby było ciekawiej. ;)

Mamy obiekt, który wewnątrz posiada osadzonych kilka mniejszych. Główny obiekt np. reprezentuje całe drzewo z pliku konfiguracyjnego i ma główną metodę LoadFromFile. W tej metodzie otwierany jest plik konfiguracyjny, a nastepnie wołane są metody LoadFromTree każdego osadzonego obiektu, przekazując im konkretną gałąź drzewa konfiguracji. Każdy z tych obiektów ładuje swoje porcje danych.

Meritum – kilka osadzonych obiektów załaduje swoje porcje, ale jeden z nich nie będzie w stanie i walnie wyjątkiem. W jaki sposób obsłużycie ten błąd, tak aby użytkownik dostał komunikat i mógł pominąć ładowanie danej gałęzi lub całkowicie przerwać proces rozruchu?

Ty się zastanów co chcesz zrobić.
Czym innym jest pominięcie brakującej danej podczas odczytu i nie jest to sytuacja krytyczna, a czym innym brak istotnych danych dla aplikacji.
To pierwsze to na pewno nie wyjątek raczej ostrzeżenie do logu.
To drugie, to zawsze wyjątek.

I po trzecie - moim zdaniem, możesz całkowicie pominąć kwestie związane z powiadamianiem i oczekiwaniem decyzji od użytkownika.
Nawet jeśli dostarczysz mechanizm który będzie zbierał feedback od usera to i tak najprawdopodobniej będzie on (feedback, nie mechanizm) nieprawidłowy.
Wg mnie lepiej całkowicie darować sobie moment czekania na decyzję usera. A może i nawet powiadamiania...

Dlaczego?
Ano dlatego, że najpewniej user w przypadku problemu i tak nic z nim nie zrobi lub robi źle.
O ile Ty będziesz userem, to pewnie będziesz w stanie ocenić czy brak jakiejś konfiguracji jest krytyczny i nie można jej pominąć lub nie.
Ty tak, a inni? Nie sądzę.

Poza tym, co to za informacja "nie udało się załadować XYZ czy kontynuować"?
Kontynuujemy, ale nie załadowano - nie wiem - layoutu programu i nic nie widać.
Apka niby działa, ale co z tego?
Lepiej samemu w kodzie zdecydować co jest krytyczne i musi być, a co nie jest nie zostało załadowane i jedziemy z ustawieniami domyślnymi.

Pozwolicie wydostać się wyjątkowi poza główną metodę LoadFromFile czy obsłużycie go wewnątrz?


Ogólnie chciałbym się dowiedzieć jakie macie techniki pisania kodu realizującego taką kontrolę błędów i interakcję z użytkownikiem. Ja wiem, że najłatwiej byłoby komunikat wyświetlić wewnątrz LoadFromFile i w razie wciśnięcia przez użytkownika przycisku Abort po prostu dupnąć Halt i mieć problem z głowy, ale takie coś raczej nazwałbym potworkiem, a nie dobrą architekturą programu.

To można rozwiązać na wiele rożnych sposobów.
Najprostszy to implementacja interfejsu np. IConfiguration przez delegację dla każdej klasy, która posiada zapis/odczyt konfiguracji.
Implementacja może być dostarcza różna i w różny sposób - nawet przez kontener IoC.

No, zapraszam do dyskusji.

Tu chyba nie ma za bardzo o czym dyskutować, ponieważ wiele zależy od tego czego oczekujesz i jak to już zaimplementowałeś.
Możemy dyskutować o np. czymś co ja nazywam Generic Persistance Layer, ale...
To jest dość skomplikowane i większość ludzi nie widzi sensu w tym aby mieć jeden ogólny mechanizm na odczyt i zapis konfiguracji. Taki mechanizm który przynosi całkowicie przezroczystą obsługę konfiguracji hierarchicznej, gdzie każde node w konfiguracji może być przechowywany w osobnym pliku, jednym pliku dla wielu node'ów lub w bazie danych.

Mi to było potrzebne, to sobie zrobiłem.
Ale nie będę dyskutował o szczegółach, bo to nie ma większego sensu.

WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0
furious programming napisał(a):

Nie pasuje mi tu fabryka – to bez sensu. No bo jak, mam tworzyć fabrykę, która tylko raz w trakcie sesji stworzy tylko jednen obiekt?

Dlaczego jeden obiekt?
Sam pisałeś o całej masie obiektów, a więc...

Gdybym miał mieć cały zestaw obiektów do utworzenia, gdzie każdy obiekt ma wspólną klasę-rodzica, to by to miało ręce i nogi, ale tak to tylko zapaskudzę kod i dołożę sobie masę roboty.

Zatem nie rozumiesz jak fabryka może działać.
Ale to chyba nie o to tu chodzi....

Tutaj potrzeba czegoś na kształt callbacków, aby uniknąć implementowania logiki dotyczącej interakcji z użytkownikiem wewnątrz klas z danymi.

Tylko po co ta cała interakcja z userem podczas ładowania konfiguracji?

Ewentualnie coś w formie zdarzeń wstrzykiwanych z zewnątrz.

Przecież zdarzenia z definicji są "wstrzykiwane" z zewnątrz.
Nie wiem dokładnie jak to wygląda, ale raczej zrobiłbym to na obserwatorach lub delegatach.

I na pewno nie na sztandarowych zdarzeniach.

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0
wloochacz napisał(a):

Czym innym jest pominięcie brakującej danej podczas odczytu i nie jest to sytuacja krytyczna, a czym innym brak istotnych danych dla aplikacji.

Te przypadki odróżnia jedynie sposób interakcji z użytkownikiem – albo daję mu wybór, albo tylko informuję.

To pierwsze to na pewno nie wyjątek raczej ostrzeżenie do logu.
To drugie, to zawsze wyjątek.

Są trzy ogólne przypadki, które należy respektować:

  1. Brakuje mało istotnych danych i mogą one być resetowane (użycie konfiguracji domyślnej). Użytkownik nie musi wiedzieć, że czegoś program nie był w stanie ”określić” lub warunki środowiskowe się zmieniły i program musi się do nich dostosować. Info co najwyżej leci do logu i to wszystko.

  2. Brakuje istotnych danych, ale możliwa jest kontynuacja rozruchu. Program będzie działał stabilnie i przewidywalnie, ale użytkownik będzie musiał sobie coś od nowa ustawić, bo zostaną załadowane ustawienia domyślne, a na koniec sesji wadliwy plik zostanie nadpisany prawidłowymi danymi. Użytkownik dostaje komunikat ostrzeżenia/błędu i ma wybór co robić.

  3. Brakuje krytycznych danych, bez których program nie będzie w stanie działać prawidłowo. W takim przypadku kontynuacja rozruchu nie jest możliwa, więc użytkownik dostaje komunikat błędu, po zamknięciu którego program się wyłącza.

I po trzecie - moim zdaniem, możesz całkowicie pominąć kwestie związane z powiadamianiem i oczekiwaniem decyzji od użytkownika.

Mogę, ale nie chcę. Użytkownik po pierwsze musi dostać w twarz szczegółowym komunikatem błędu i musi mieć możliwość wyboru co robić. Takie mam wymagania, które dla mnie są logiczne i których wdrożenie pozwoli traktować użytkownika jak człowieka. ;)

A tak na poważnie – nie mam tutaj na myśli jakichś molochowych systemów, do których naprawy potrzeba wykwalifikowanego serwisanta analizującego logi i stacktrace, a raczej relatywnie małe projekty – np. coś pokroju Inkscape. Nie wyobrażam sobie, aby po wystąpieniu błędu program po cichu zamykał się, bez jawnego informowania użytkownika o problemie, nawet jeśli ów użytkownik i tak nie będzie w stanie samemu naprawić problemu.

Nawet jeśli dostarczysz mechanizm który będzie zbierał feedback od usera to i tak najprawdopodobniej będzie on (feedback, nie mechanizm) nieprawidłowy.

To brzmi jakby problem nie posiadał dobrego rozwiązania. A jest on raczej powszechny. Masa narzędzi użytkowych ma tego typu funkcjonalność i powiadamia użytkownika komunikatami błędów – dzięki temu wiadomo co jest nie tak i można sobie szybko wygooglać rozwiązanie, bez analizy logów czy posiadania jakiejkolwiek specjalistycznej wiedzy.

Rozwiązanie nie musi być wybitne – ma być proste w implementacji, skuteczne i łatwe w utrzymaniu. Tylko tyle (a może aż tyle).

Wg mnie lepiej całkowicie darować sobie moment czekania na decyzję usera. A może i nawet powiadamiania... […] Ano dlatego, że najpewniej user w przypadku problemu i tak nic z nim nie zrobi lub robi źle.

Ale przynajmniej zawsze dowie się w czym problem.

O ile Ty będziesz userem, to pewnie będziesz w stanie ocenić czy brak jakiejś konfiguracji jest krytyczny i nie można jej pominąć lub nie.

Po to właśnie chcę wyświetlić komunikat, w którym jasno i szczegółowo przedstawi się sprawę. Nie mam tutaj na myśli elaboratu, a krótki i merytoryczny komunikat pokroju: „Program nie był w stanie załadować pliku ”foo/bar/xyz” – plik ten nie istnieje na dysku lub jest uszkodzony. W celu naprawy danych programu zrób to i to i spróbuj ponownie.” I od razu wiadomo, że użytkownik powinien np. odpalić instalator i wybrać opcję naprawy, aby móc poprawnie uruchomić aplikację.

Poza tym, co to za informacja "nie udało się załadować XYZ czy kontynuować"?

Zgadzam się, ale to nie jest szczegółowy opis problemu. ;)

wloochacz napisał(a):

Dlaczego jeden obiekt?
Sam pisałeś o całej masie obiektów, a więc...

Co prawda klas jest kilkadziesiąt, ale w większości są to klasy niepowiązane ze sobą dziedziczeniem. Są też np. listy obrazów – tam masą obiektów są po prostu obrazy, wpakowane do jednej mapy generycznej.

Zatem nie rozumiesz jak fabryka może działać.

Racja – nie rozumiem, nie rozumiem sensu jej implementacji w moim przypadku. Wydaje się, że nakład pracy będzie niewspółmiernie duży w stosunku do zyskanych korzyści.

Tylko po co ta cała interakcja z userem podczas ładowania konfiguracji?

Konfig to tylko uproszczenie na potrzeby zilustrowania problemu. Ogólnie zależy mi na tym, aby o każdym krytycznym lub wartym uwagi problemie użytkownik się dowiedział, bez względu na to czy sam może coś poradzić czy nie.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 5x, ostatnio: flowCRANE
RD
  • Rejestracja:około 12 lat
  • Ostatnio:10 miesięcy
  • Postów:212
0

Dokładnie nad podobnym rzeczą się zastanawiałem. Gdy podczas rozruchu aplikacji ta łączyła się z serwerem dostawcy, ładowała piliki, dll itd.

Ja to rozwiązałem tak że wszystkie klasy oparłam na własnej klasie bazowej która podczas tworzenia, tworzyła wątek który to weryfikował jak długo klasa istnieje (stoper który generował wyjątek jak klasa nie zwolni się w ciągu np 5 sekund lub nie wykona procedury potwierdzające np UkończonoPowodzeniem; Wszystkie wyjątki programu kierowałem do własnego przechwytywania i tam wiedziałam która klasa wygenerowała wyjątek i co zrobić dalej. Jeżeli chciałem wrócić dalej do wykonywania to używałem wstawki z assemblera który robił nic innego jak polecenie goto w Destructor klasy która wygenerowała wyjątek i kod leciał dalej.

Może nie jest to najlepsze rozwiązanie ale działało :)

edytowany 1x, ostatnio: Rafał D
flowCRANE
Płeć Ci się myli? Popraw ten post. :P
RD
@furious programming: żeby życie miało smaczek raz być dziewczynką raz chlopaczkiem 🤣
RD
Nie idzie edytować postów używając smartphone? Nie mam takiej opcji
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0
Rafał D napisał(a):

Ja to rozwiązałem tak że wszystkie klasy oparłam na własnej klasie bazowej która podczas tworzenia, tworzyła wątek który to weryfikował jak długo klasa istnieje […]

To brzmi na overkill – czegoś takiego jak timeout nie było, że aż wątki pilnujące trzeba było tworzyć?

Jeżeli chciałem wrócić dalej do wykonywania to używałem wstawki z assemblera który robił nic innego jak polecenie goto w Destructor klasy która wygenerowała wyjątek i kod leciał dalej.

Assemblerem? Do destruktora?! Szacun. :D


Ogólnie chodzi o bardzo prostą rzecz, czyli oparcie całego procesu ładowania/zapisu danych na jednej logicznej/enumowej fladze (podawanej do każdej klasy ładującej/zapisującej) oraz zestawie zdarzeń obsługujących interakcję z użytkownikiem i manipulowanie wartością wspomnianej flagi. W ten sposób będzie można łatwo przerwać cały proces, a także podłączyć sobie odpowiedni zestaw zdarzeń pod dany proces (dla ładowania jeden, dla zapisu inny). Nie trzeba zbyt wiele kodu pisać.

Powoli szlifuję małego PoC-a, w którym szlifuję docelowe rozwiązanie. Jak go doszlifuję to podrzucę go tutaj.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
RD
  • Rejestracja:około 12 lat
  • Ostatnio:10 miesięcy
  • Postów:212
0

Wiem czasami mam szalone pomysły :) Już się o tym dowiedziałem na tym forum.

A tak na poważnie może Ci się to przyda. Komponenty Clever suite maja ciekawa rzecz która nie wiem jak dziala:) nie mam ich z kodem źródłowym.

Mamy taką procedurę

Kopiuj
Procedurę tform1.button1;
Begin
    Label1.caption := 'start
    Clftp1.put(filename);
    Label2.caption := 'koniec
End;

Klikamy przycisk i plik jest wysyłany na serwer i jak to wyśle to napis zmienia tekst. Niby nic szczególnego poza tym że po wykonaniu procedury put. Program się odmraża. Można klikać inne przyciski na formie wykonywać inny kod inne funkcje. Nawet zamknąć formę ale ta się zamknie gdy plik zostanie wysłany i label zmieni wartość.

Nie mam pojęcia jak to zrobili że program główny wykonuje czesc procedury potem można inne wykonywać A w między czasie po wysłaniu pliku wraca do wykonywania dalszej części kodu.

Tu nie wystarczy choćby application.processmessages;

edytowany 2x, ostatnio: flowCRANE
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0
Rafał D napisał(a):

Nie mam pojęcia jak to zrobili że program główny wykonuje czesc procedury potem można inne wykonywać A w między czasie po wysłaniu pliku wraca do wykonywania dalszej części kodu.

Zwykły wątek poboczny – dzięki temu główny wątek (ten dla UI) zostaje natychmiast odblokowany, dzięki czemu kolejka komunikatów jest przetwarzana bez opóźnień, co zapobiega zamrażaniu programu i zmuszaniu użytkownika do czekania, aż proces wysyłania zostanie zakończony.

Tu nie wystarczy choćby application.processmessages;

Application.ProcessMessages przeznaczony jest głównie dla aplikacji jednowątkowych, po to by móc przetwarzać kolejkę komunikatów podczas gdy wykonywane są operacje trwające dość długo. Dzięki temu można np. aktualizować interfejs po każdym ”kroku” wykonanym przez długo działający algorytm oraz w międzyczasie odbierać input użytkownika.

Należy jednak pamiętać, że tej metody nie powinno się stosować zamiast wątków pobocznych, bo ma spore ograniczenia. Jej usilne stosowanie potrafi nieźle napaskudzić w kodzie.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
WL
  • Rejestracja:około 21 lat
  • Ostatnio:około 2 miesiące
  • Postów:1082
0
furious programming napisał(a):
Rafał D napisał(a):

Nie mam pojęcia jak to zrobili że program główny wykonuje czesc procedury potem można inne wykonywać A w między czasie po wysłaniu pliku wraca do wykonywania dalszej części kodu.

Zwykły wątek poboczny – dzięki temu główny wątek (ten dla UI) zostaje natychmiast odblokowany, dzięki czemu kolejka komunikatów jest przetwarzana bez opóźnień, co zapobiega zamrażaniu programu i zmuszaniu użytkownika do czekania, aż proces wysyłania zostanie zakończony.

Mówisz?
To pokaż mi proszę jak zrobić to na zwykłym wątku pobocznym coś takiego, że "program główny wykonuje czesc procedury potem można inne wykonywać A w między czasie po wysłaniu pliku wraca do wykonywania dalszej części kodu.".

To wygląda jak wątkowe ShowModal, z tym że takie wątkowe ShowModal nie zawiesza głównej pętli komunikatów dla aplikacji i pozwala jej normalnie pracować.
I równocześnie zawiesza kod metody, która go wywołała.
I nie, tego się nie da zrobić na samym wątku pobocznym, ponieważ on nie będzie zwieszał kodu, który go powołał do życia..
To jest odrobinę bardziej skomplikowane...

Wygląda to trochę jak async/await wbudowane w same komponenty Clever.

Tu nie wystarczy choćby application.processmessages;

Nie, nie wystarczy.

Application.ProcessMessages przeznaczony jest głównie dla aplikacji jednowątkowych, po to by móc przetwarzać kolejkę komunikatów podczas gdy wykonywane są operacje trwające dość długo. Dzięki temu można np. aktualizować interfejs po każdym ”kroku” wykonanym przez długo działający algorytm oraz w międzyczasie odbierać input użytkownika.

Należy jednak pamiętać, że tej metody nie powinno się stosować zamiast wątków pobocznych, bo ma spore ograniczenia. Jej usilne stosowanie potrafi nieźle napaskudzić w kodzie.

Moim zdaniem on to wie, ale Ty nie zrozumiałeś o czym @Rafał D pisze ;-)

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Tuchów
  • Postów:12164
0
wloochacz napisał(a):

To pokaż mi proszę jak zrobić to na zwykłym wątku pobocznym coś takiego, że "program główny wykonuje czesc procedury potem można inne wykonywać A w między czasie po wysłaniu pliku wraca do wykonywania dalszej części kodu.".

Hmm… faktycznie nie zrozumiałem. ;)

Wygląda mi to albo na coś pokroju free-pascalowego QueueAsyncCall, albo na jakiś trik związany ze stosem – trudno powiedzieć. Mimo wszystko nie spotkałem się jeszcze z czymś takim, więc jeśli ktoś zna szczegóły działania tych komponentów to z chęcią bym się z nimi zapoznał.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)