Jak wyczyścić zmienną typu TPanel?

2015-01-21 21:23
0

Wydaje mi się, że z każdym dniem coraz bardziej się rozwijam i wpadam na pomysły o których wcześniej nie myślałem, a które w dużym stopniu ułatwiają mi napisanie tego co chce.
Do tej pory jeśli miałem menu główne i przyciski na nim. To po kliknięciu w któryś ustawiałem JakiśPanel.Visible := True i PanelMenuGlowne.Visible := False; Dodatkowo, aby powrócić to na każdym Panelu umieszczałem przycisk Powrót. Dziś postanowiłem spróbować zrobić to z jednym przyciskiem Powrót który w "drzewku" projektu byłby na poziomie tych paneli. Po naciśnięciu w któryś z przycisków menu głównego dodatkowo do zmiennej globalnej ZmiennaPanel typu TPanel przypisuje który Panel będzie akurat włączony. Później po naciśnięciu "Powrót" robię ZmiennaPanel.Visible := False i PanelMenuGlowne.Visible := True. Pierwsza kwestia czy jest to zrobione poprawnie.
I główne pytanie, w jaki sposób "wyczyścić" ZmiennaPanel po naciśnięciu przycisku "Powrót"?

Pozostało 580 znaków

2015-01-21 21:31

Pisałem Ci wcześniej, że takie kombinacje prędzej czy później przyniosą kupę problemów, ale nie posłuchałeś;

I główne pytanie, w jaki sposób "wyczyścić" ZmiennaPanel po naciśnięciu przycisku "Powrót"?

To zależy co rozumiesz przez słowo "wyczyścić"; Jeżeli chodzi Ci o usunięcie wartości z adresem na konkretną instancję komponentu, to możesz do zmiennej wpisać Nil:

ZmiennaPanel := nil;

Jeśli będziesz potrzebował sprawdzić, czy w tej zmiennej jest zapisany wskaźnik na jakiś komponent, to możesz użyć zwykłego porównania do Nil, albo funkcji Assigned:

if ZmiennaPanel = nil then

{ lub }

if Assigned(ZmiennaPanel) then

Pozostało 580 znaków

2015-01-21 21:38
0
furious programming napisał(a):

Pisałem Ci wcześniej, że takie kombinacje prędzej czy później przyniosą kupę problemów, ale nie posłuchałeś;

To znaczy jakie kombinacje?

edytowany 1x, ostatnio: dani17, 2015-01-21 21:38

Pozostało 580 znaków

2015-01-21 21:44
1

Kombinacje z ukrywaniem i pokazywaniem paneli; Przy okazji:

To po kliknięciu w któryś ustawiałem JakiśPanel.Visible := True i PanelMenuGlowne.Visible := False;

Jeśli w jakiejś metodzie pokazujesz jakiś komponent, a w innej go chowasz (czyli w danej metodzie zawsze używasz tego samego stanu dla Visible), to czytelniej jest użyć metod Show i Hide, np.:

procedure ShowMenuPanel();
begin
  MyPanel.Show();
end;

procedure HideMenuPanel();
begin
  MyPanel.Hide();
end;

Właściwość Visible nadaje się w przypadku, gdy stan potrzebujemy wstępnie obliczyć (albo zanegować), czyli jeśli nie sprawdzamy czy komponent jest widoczny czy nie, np.:

procedure ReversePanelVisibleState();
begin
  MyPanel.Visible := not MyPanel.Visible;
end;

Ale to tak przy okazji.


Pozostało 580 znaków

2015-01-21 22:27
0

Wiem, że powinno się zakładać jeden wątek na każdy problem, jednak nie chce zakładać już trzeciego dzisiaj, szczególnie, że pytanie w dużym stopniu dotyczy tego samego.
A więc sposób w który chciałem to zrobić nie zadziałał. Na początku przycisk Powrót się pojawiał. Później po jego wciśnięciu i następnie tego samego przycisku nadal było wszystko dobrze. Ale po naciśnięciu Powrót, a później innego przycisku, Powrót już się nie pojawiał.

W związku z tym próbuję zrobić coś takiego jak dynamiczne tworzenie komponentów. Przycisk tworzy się właściwie i działa właściwie. Pytanie, jak go później usunąć? Czy w ogóle trzeba to robić?

Pozostało 580 znaków

2015-01-21 23:33
2

Chyba chodzi Tobie o Przycisk.Free? I generalnie zasada jest taka, że wszystko co utworzyłes, a co nie jest już potrzebne powinno się zwolnić. Ale o tym już pisałem.


Pozostało 580 znaków

2015-01-22 00:32
1
dani17 napisał(a)

Pytanie, jak go później usunąć?

@olesio napisał Ci jak to zrobić - tak samo jak każdy inny obiekt; Jednak pamiętaj, że aby móc coś zwolnić - musisz mieć do tego dostęp; Więc jeśli tworzysz zwykły obiekt (jakiejś klasy, ale nie komponent), to rezultat zwracany przez konstruktor musisz sobie gdzieś zapisać, np. to pola klasy; Jeśli tworzysz komponent, to też dobrze mieć do niego referencję wrzuconą np. do pola, czy zmiennej;

Czy w ogóle trzeba to robić?

W przypadku zwykłego obiektu, jeśli nie zapiszesz sobie referencji w zmiennej - nie będziesz mógł go zwolnić, więc będzie wyciek pamięci; Komponentu pamiętać nie musisz, o ile poprawnie nadasz mu podczas tworzenia komponent rodzica; Wtedy taki komponent zostanie automatycznie zwolniony w destruktorze klasy formularza;

Tą zaletą trzymania referencji do dynamicznie tworzonych komponentów jest fakt, iż masz do nich bezpośredni dostęp; Jeśli referencji nie będziesz posiadał - pozostaną Ci kombinacje, takie jak np. szukanie komponentu po nazwie, za pomocą FindComponent; A nie jest ani wygodne, ani szybkie.


Pozostało 580 znaków

2015-01-22 09:32
0

W moim przypadku chodzi o przycisk. Chciałem w zdarzeniu onClick dodać Sender.Free. Oczywiście w ten sposób to nie działa. Zrozumiałem, że mam coś zapisać do zmiennej. Tylko tak na prawdę czym są te referencje i do zmiennej jakiego typu je zapisać?

Próbowałem też z użyciem Self, zamiast Sender. Ale efekt był taki sam. Więc ostatecznie zrobiłem to tak Form.FindComponent('PrzyciskPowrot').Free;
W przypadku Self które umieszczam w zdarzeniu onClick dynamicznie utworzonego przycisku, ku mojemu zaskoczeniu odnosi się on do całego formularza. Choć pewnie coś źle zrozumiałem. Jeśli używam Sender to faktycznie odnosi się do przycisku. Jednak w ten sposób nie mogę go zwolnić bo pokazuje się błąd. 'External: SIGSEGV' At address 73746E65.

Tak jak napisałem, udało się z użyciem FindComponent. Jednak @furious programming z tego co wyczytałem w innym temacie, pisałeś że nie jest to najlepsze rozwiązanie i stosujesz je tylko w ostateczności.

Idę dalej. Tworzę 6 paneli dynamicznie. Które umieszczam w globalnej tablicy typu TPanel. Po kliknięciu w przycisk chciałbym je wyczyścić. Spróbowałem to zrobić z takim kodem jaki jest poniżej, jednak i w tym przypadku nie działa to tak jak bym chciał. Teoretycznie program jest zbudowany prawidłowo, nie wyświetla się żaden błąd, jednak tak na prawdę ten kod nie działa, bo te panele nadal istnieją. Co powoduje błąd w momencie próby utworzenia paneli z takimi samymi nazwami, ponieważ takie nazwy już istnieją, chociaż powinny być moim zdaniem zwolnione.

Aha. Naciśnięcie przycisku wywołuje procedurę UsunPanele.


procedure UsunPanele;
var
  I: Integer;
begin
  for I := 1 to 6 do
    begin
      Form.Panele[I].Parent := Nil;
      Form.FindComponent(Form.Panele[I].Name).Free;
    end;
end;

Form.PrzyciskiMenu[I].Parent := Nil; - działa prawidłowo. Ta druga część nie przynosi żadnego efektu, jakby nie odnajdywało Paneli o takich nazwach, które te Panele mają. Mimo, że po dodaniu linijki ShowMessage(Form.Panele[I].Name); wyświetla się 6 komunikatów z nadanymi nazwami.

Oczywiście mogę to rozwiązać podając nazwę każdego, ale to się moim zdaniem trochę mija z celem. Poza tym chciałbym wiedzieć czy można to usunąć bez nadawania nazwy.

edytowany 8x, ostatnio: dani17, 2015-01-22 15:50

Pozostało 580 znaków

2015-01-22 15:47
1

Chciałem w zdarzeniu onClick dodać Sender.Free. Oczywiście w ten sposób to nie działa.

Ja bym proponował Sendera nie ruszać i traktować go tylko do odczytu :]

Tylko tak na prawdę czym są te referencje i do zmiennej jakiego typu je zapisać?

Po prostu wskaźnik na zarezerwowany blok pamięci dla obiektu; Jeśli zadeklarujesz zmienną tak:

var
  Foo: TPanel;

To zmienna Foo jest wskaźnikiem na obiekt klasy TPanel; Możesz do takiej zmiennej wpisać Nil, co sprawi, że zmienna nie będzie wskazywać na żaden obiekt; Możesz też do niej wpisać wskazanie na istniejący komponent:

Foo := TPanel(Sender);

i od tej pory zmienna Foo będzie posiadać wskazanie na panel, który został przekazany w argumencie Sender; Z tego co pisałeś wcześniej, potrzebujesz zapamiętać jakiś panel, więc w ten sposób możesz to zrobić;

Tak jak napisałem, udało się z użyciem FindComponent. Jednak @furious programming z tego co wyczytałem w innym temacie, pisałeś że nie jest to najlepsze rozwiązanie i stosujesz je tylko w ostateczności.

Mniej więcej tak, ale nie dokładnie; Zauważ, że FindComponent szuka komponentu na całym formularzu, na podstawie jego nazwy; Jest to operacja dość kosztowna, tym bardziej jeśli komponentów na formularzu jest dużo, a wywołań tej metody potrzebnych jest wiele; No i pamiętaj też, że ta metoda nie znajdzie komponentu, który został utworzony dynamicznie i nie została mu nadana nazwa;

Staram się unikać tej metody, bo jeżeli np. tworzę jakieś komponenty dynamicznie, to wpisuję je do jakiejś listy czy macierzy, tak abym w każdej chwili miał do nich bezpośredni dostęp; Jeśli takiego dostępu nie posiadam, to jedyne co pozostaje to albo przerobienie kodu, albo użycie FindComponent;

Co powoduje błąd w momencie próby utworzenia paneli z takimi samymi nazwami, ponieważ takie nazwy już istnieją, chociaż powinny być moim zdaniem zwolnione.

Na jednym formularzu nie mogą istnieć dwa komponenty o tej samej nazwie, przy czym wielkość liter nie ma znaczenia; Dlatego też musisz używać unikalnych identyfikatorów, albo nie nadawać ich wcale i zapamiętać te komponenty np. w macierzy;

Wracając do problemu - obstawiam, że w ogóle nie nadałeś komponentom nazw, dlatego też metoda FindComponent ich nie znajduje; Poza tym korzystasz z niebezpiecznej konstrukcji przy zwalnianiu tych paneli:

Form.FindComponent(Form.Panele[I].Name).Free;

Jeżeli komponent nie zostanie znaleziony, metoda FindComponent zwróci Nil, na którym wywołasz metodę Free, co spowoduje pojawienie się wyjątku o naruszeniu pamięci (SIGSEGV); Musisz ten kod zabezpieczyć:

procedure TForm1.DeletePanels();
var
  pnlToken: TPanel;
  intToken: Integer;
begin
  for intToken := 1 to 6 do
  begin
    pnlToken := Panele[intToken];

    if Assigned(pnlToken) then
      pnlToken.Free();
  end;  
end;

Poza tym Twoja metoda UsunPanele w ogóle nie jest metodą, bo nie jest składową klasy formularza! Obstawiam, że masz kupkę zmiennych i procedur globalnych, więc przenieś je do wnętrza klasy formularza, aby mieć łatwy dostęp do jego zawartości;

Oczywiście mogę to rozwiązać podając nazwę każdego, ale to się moim zdaniem trochę mija z celem. Poza tym chciałbym wiedzieć czy można to usunąć bez nadawania nazwy.

Jeżeli komponentom nie nadasz nazw, to FindComponent ich nie znajdzie; Dlatego też wrzuć sobie je do macierzy i odwołuj się do nich bezpośrednio, bez szukania ich na formularzu; To jedyny sensowny sposób w Twoim przypadku.


Pozostało 580 znaków

2015-01-22 16:25
0
furious programming napisał(a):

Poza tym Twoja metoda UsunPanele w ogóle nie jest metodą, bo nie jest składową klasy formularza! Obstawiam, że masz kupkę zmiennych i procedur globalnych, więc przenieś je do wnętrza klasy formularza, aby mieć łatwy dostęp do jego zawartości;.

Zmienne miałem w klasie, a procedur faktycznie nie, co trochę mi utrudniało. Jednak jak już pisałem ja najlepiej się uczę po prostu próbując coś robić i tak na prawdę dopiero dziś trafiłem na klasy. Poczytałem trochę na ten temat, ale tak na prawdę na razie to jest mało. Jeszcze wieczorem zamierzam doczytać więcej, ale póki co chciałbym poprawić chociaż ten kod który już mam napisany. Dlatego mam jedno pytanie. Już ostatnie, bo trochę za bardzo już odchodzimy od tematu. W której sekcji tak na prawdę powinienem umieścić zarówno procedury, jak i zmienne?

Pozostało 580 znaków

2015-01-22 16:33
1

W której sekcji tak na prawdę powinienem umieścić zarówno procedury, jak i zmienne?

Zmienne jako składowe klasy, nazywane są polami i deklaruje się je z reguły w sekcji Private; Dzięki temu dostęp do nich będzie możliwy tylko i wyłącznie z metod danej klasy - z zewnątrz pozostaną niewidoczne;

Natomiast procedury i funkcje jako składowe klasy, zwane są metodami; W większości, bo proceduralne i funkcyjne metody służące właściwościom do modyfikacji/odczytu pól nazywane są różnie - setter i getter (to raczej wywodzi się z Javy), metoda dostępowa i zmieniająca, akcesor i mutator itd.; Do tego dochodzą konstruktory, destruktory i zdarzenia, ale zbyt dużo tego by wszystko szczegółowo opisać;

Metody deklarować można w różnych sekcjach - w sekcji Private będą do dyspozycji tylko i wyłącznie wewnątrz tej klasy, w sekcji Public będą widoczne z zewnątrz (w sekcji Published także); Jest jeszcze sekcja Protected, ale póki co nią się nie zajmuj - dowiesz się o niej podczas uczenia się o OOP w Delphi.


Pozostało 580 znaków

Liczba odpowiedzi na stronę

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

Robot: Bingbot