[Delphi] Problem z kilkoma formularzami w aplikacji

0

Piszę program - Tetrisa ;)
Mój program składa się <ort>na razie </ort>z trzech formularzy.
Form1 to główna część gry.
Form2 to okno z wynikami.
Form3 to menu, z 3. opcjami : Play, Hi-Scores i Exit.

Domyślnym formularzem projektu jest Form3.

Mam problem z uruchamianiem/zamykaniem Form1.
Pod przycisk (obrazek właściwie) mam przypisany kod:

procedure TForm3.PlayBtnClick(Sender: TObject);
begin
Form3.Hide;
Form1.Show;
Form1.Timer1.Enabled:=True;
end;

Wtedy działa najlepiej, ale nie dobrze.
Okno bez problemu się wyświetla, a także zamyka.
Ale kiedy drugi raz podczas działania aplikacji nacisnę Play, to mam błąd EAccessViolation.
Przy TForm1.FormClose mam dopisane Form1.Free. Gdy tego nie było to ten sam błąd miałem przy zamykaniu okna.

Dodam, że przy Form2 już takich problemów nie mam.
Mam po prostu

Form2:=TForm2.Create(Application);
Form2.ShowModal;
Form2.Free;

i działa. Przy Form1 to samo nie działa (oczywiście w Opcjach projektu poustawiane jest dobrze).

Nie mam już żadnych pomysłów co z tym zrobić.

0

Może zamiast :

Form3.Hide;
Form1.Show;

Daj :

Form3.Close;
Form1.Show;

A co do drugiego problemu to , przy Form2 nie masz takiego problemu jak przy Form1 ,bo tam tworzysz formę i robisz free ,a przy zamykaniu form1 nie możesz zrobić free ,bo i tak jak zamkniesz formę to zrobi się to automatycznie ...

Pozdrawiam ;]

0

Jak dam Form3.Close; to zamknie mi form zanim otworzy sie Form1 ;)

0
rakoczyn napisał(a)

Jak dam Form3.Close; to zamknie mi form zanim otworzy sie Form1 ;)

No to daj odwrotnie:

Form1.Show;
Form3.Close;
0

Niestety też nie działa.
Okienko otwiera się na ułamek sekundy, po czym aplikacja się wyłącza.

0
rakoczyn napisał(a)

Domyślnym formularzem projektu jest Form3.

Więc jak go zamkniesz, to zamkniesz całą aplikację. Proste.

0

Problem rozwiązany.

Powodem było to, że użyłem procedury Form1.Close, a później miały się wykonać jeszcze jakieś operacje na zmiennych.
Zastosowałem zmienną logiczną i od niej uzależniłem wykonanie tych instrukcji, a Form1.Close dałem na sam koniec.

EDIT:

A jednak nie do końca.
Form sie zamyka dobrze, ale jeśli otworzę go ponownie podczas działania aplikacji, to mam błąd.
Prześledziłem to dobrze, i EAccess Violation występuje przy próbie rysowania, dokładnie tu:

Form1.PaintBox1.Canvas.Draw((x-1)*20,(y-1)*20,klocek);

klocek to TBitmap.

Podczas FormCreate robie Create dla każdego klocka, a podczas Close wszystkie zwalniam przez .Free

Na dodatek, jeśli klikne OK w komunikacie o błędzie i potem wznowie działanie programu, to moge dowolnie zamykać i otwierać bez żadnych błędów.

Naprawdę nikt z doświadczonych programistów nie wie jak zaradzić na mój problem?

0
  1. głównej formy nie można zamykać bo ubijesz całą aplikację ( główną formę możesz wybrać np w Project\Settings )
  2. rysowanie w OnCreate wywala błąd? sprawdź, bo nie pamiętam, czy przypadkiem nie ma zdarzenia typu OnAfter...
  3. opisz dokładniej jak te okienka się motają to może zarzuci ktoś sensowniejszym sposobem
0
  1. Głównej formy nie zamykam, tylko ukrywam Form3.Hide
    Procedura po kliknięciu przycisku wygląda tak:
procedure TForm3.PlayBtnClick(Sender: TObject);
begin
Form1:=TForm1.Create(Application);
Form1.Timer1.Enabled:=True;
Form1.Show;
Form3.Hide;
end;

Dla wyjaśnienia: Głównym formularzem nadal jest Form3. Form 1 i Form3 są tworzone przy uruchamianiu aplikacji.
W procedurze jeszcze rez jest Form1.Create, ponieważ jak go zamknę, i chce jeszcze raz otworzyć to musi być, a nie przeszkadza wcześniej.

  1. Gdzie ma być takie zdarzenie? Bo nigdzie nie widziałem.

  2. Sytuacja wygląda tak:
    Aplikacja się uruchamia, pokazuje się Form3 (Menu).
    Klikam w przycisk Play, wykonywana jest w/w procedura.
    Po zakończeniu działania, wykonuje sie Form1.Close, lub zamykany jest przyciskiem X.
    Jeszcze raz klikam na przycisk. Wtedy otrzymuje komunikat:

Project Tetris.exe raised exception class EAccess Violation with message 'Access violation at address 0046ADB1 in module 'Tetris.exe'. Read of address 00000160'.
Process stopped. Use Step or Run to continue.

Jak pisałem wcześniej, po Stepowaniu programu, okazało się, że wyjątek ten powoduje rysowanie. Oczywiście przy pierwszym uruchomieniu (kiedy błędu nie ma) wykonuje się ta sama procedura rysowania.

Normalnie aplikacja po prostu działa dalej, a spod Delphi się pauzuje i jak wznowie działanie, to wszystko działa dobrze.
Nie jest to więc błąd poważny, ale że jest to mój projekt na zaliczenie, wolałbym aby go nie było.

W ostateczności wystarczy aby nie wyświetlał się ten komunikat, tylko aplikacja dalej działała.

0

W procedurze jeszcze rez jest Form1.Create, ponieważ jak go zamknę, i chce jeszcze raz otworzyć to musi być, a nie przeszkadza wcześniej.

Czy aby na pewno?
Załóżmy, że mam dwie formy: Form1 (główna) i Form2. Na obie wkładam po jednym Button'ie. Dla Button'a formy głównej kod:

procedure TForm1.ButtonF1Click(Sender: TObject);
begin
 Form2.Show;
end;

a dla Buttona drugiego:

procedure TForm2.ButtonF2Click(Sender: TObject);
begin
 Form2.Close;
end;

Działa za każdym razem i nie trzeba ponownie tworzyć formy.

poza tym tworzenie Formy powinno być w kodzie projektu (Project\View Source) tak przynajmniej uważam.

0

Jeśli usunę Form1:=TForm1.Create(Application); to też mam błąd EAccess Violation, ale przy Form1.Show;

Kod projektu wygląda tak:

Application.Initialize;
  Application.Title:='Tetris v0.4 beta';
  Application.CreateForm(TForm3, Form3);
  Application.CreateForm(TForm1, Form1);
  Application.Run;
  Application.HintColor:=0;
  Application.HintPause:=500;
  Application.HintHidePause:=500;
0

Jeżeli w onCreate formy tworzysz klocki a w onClose formy je zwalniasz, to nie dziwne że się wywraca, ponieważ forma towrzy się RAZ a zamknąć można ją WIELE razy, czyli przy pierwszym otwarciu będzie OK a przy drugim już NIE MA KLOCKÓW, Twórz i rysuj klocki w OnShow albo onPaint.

0

Zostawiłem klocki tak jak były, zwalniane są przy zamknięciu formy i ponownie tworzone.
Problemem okazała się chyba procedura Form1.Free; w OnClose Form1. Nie wiem skąd się tam wzięła, chyba miałem jakieś inne błędy i dlatego tego spróbowałem a potem zostawiłem.

Mam teraz mały problem z optymalizacją ;)
Na początku program zajmuje w pamięci ok 1,8MB.
Przy każdym uruchomieniu Form1 rozmiar ten zwiększa się o ok. 1MB, za to przy zamknięciu, zwalniane jest jakieś 650KB.
Jednak to już całkiem inny problem.

Dzięki za naprowadzenie mnie na właściwą drogę.

0

przecież otwarte okno coś musi zajmować =]

otwórz sobie choćby i notatnik i patrz na zmianę zużycia pamięci przy minimalizacji/przywracaniu
jak schowany to ze 400KB zajmuje a jak okno na wierzchu to ponad 1MB

ale o ile zrozumiałem to cały czas puchnie?

miałem kiedyś z czymś takim problem, może taki sam jak Twój
w Timerku pacnąłem rysowanie, w procedurze obsługi zdarzenia była zmienna lokalna TBitmap
tworzyłem, zwalniałem ale program puchł i puchł aż tu nagle komunikat ( od windowsa )
coś w stylu " brak miejsca w magazynie na wykonanie operacji"

jak ten TBitmap zmieniłem na zmienną globalną problem zniknął ponieważ tworzenie ( i zwalnianie ) było tylko raz więc nie zaśmiecałem tego "magazynu" =]

0

No tak, cały czas 'puchnie'.

Moja sekcja deklaracyjna wygląda tak:

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Grids, XPMan, jpeg;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    PaintBox1: TPaintBox;
    Timer1: TTimer;
    Panel2: TPanel;
    XPManifest1: TXPManifest;
    Digit5: TImage;
    Digit3: TImage;
    Digit4: TImage;
    Digit2: TImage;
    Digit1: TImage;
    Image1: TImage;
    Digit6: TImage;
    Leveld1: TImage;
    Leveld2: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
    procedure FormPaint(Sender: TObject);
   private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TScore=packed Record
   Name : String[15];
   Score : Integer;
  end;

var
  Form1: TForm1;
  plansza : array[0..11,1..19] of shortint;
  block_act  : array[1..4,1..4] of byte;
  block_next  : array[1..4,1..4] of byte;
  block_temp  : array[1..4,1..4] of byte;

  block_empty  : TBitmap;
  block_aqua   : TBitmap;
  block_blue   : TBitmap;
  block_green  : TBitmap;
  block_orange : TBitmap;
  block_red    : TBitmap;
  block_violet : TBitmap;
  block_yellow : TBitmap;


  wiersz,kolumna : shortint;
  l : byte;
  id_act,stan_act : byte;
  id_next         : byte;

  score : integer;
  level : byte;
  rekord, rec_tmp : TScore;
  Rec : Array[1..11] of TScore;

  plik : file of TScore;

  tlo  : TJpegImage;
  Btlo : boolean;
  start:boolean;

Jak widać, mam kilka komponentów Image, ale one chyba nie powodują tego?
Mam też TBitmap zwalniane przy Form1.Close, a także TJpegImage, także zwalniane przy zakmnięciu.

0

przeprowadźmy śledztwo =]

nie masz żadnych obiektów tworzonych lokalnie ( tylko w danej procedurze ) ?
czy jak tylko okno jest otwarte i coś się tam ryzuje to puchnie czy dopiero jak otwierasz/zamykasz okno ?

0

a! i gdzie tworzysz tego TBitmap? w OnCreate czy OnShow ?
bo jak zwalniasz w OnClose to czy wywołanie Form.Close nie wywoła tego zwalniania? przez co tworzenie musiało by być przy każdym wyskoczeniu okna przez co obiekt był by tworzony tylko na dane pokazanie okna i mamy puchnięcie

właśnie się delphi zainstalowało to posprawdzam =]

0

Wszyskie zmienne typu TBitmap i TJPEGImage są tworzone w OnCreate, i zwalniane w OnClose.

Aplikacja puchnie tak:
Włączam program, na początku jest menu, zajmuje w pamięci 1 876 K.
Naciskam PLAY, pokazuje sie kolejno okno (Form1), Teraz program zajmuje ok. 3MB.
Zamykam okno, część pamięci się zwalnia ale program zajmuje teraz 2 344 K, jakieś 400K więcej.
Daje znowu PLAY, i program zajmuje 3 296 K. Zamykam - 2 624 K. O 300 więcej.
I tak w kółko.

Jedyna zmienna lokalna typu TBitmap jest w tej procedurze:

procedure rysuj(x,y,kolor:byte);
var klocek : TBitmap;
begin
  case kolor of
  0 : klocek:=block_empty;
  1 : klocek:=block_aqua;
  2 : klocek:=block_blue;
  3 : klocek:=block_green;
  4 : klocek:=block_orange;
  5 : klocek:=block_red;
  6 : klocek:=block_violet;
  7 : klocek:=block_yellow;
  else klocek:=block_empty; end;

Form1.PaintBox1.Canvas.Draw((x-1)*20,(y-1)*20,klocek);


end;

Oczywiście w innych procedurach też mam lokalne zmienne, ale głównie typu byte lub boolean.

0

hm, w tej metodzie rysuj jakoś dziwnie traktujesz klocka =]
bo ta zmienna to w sumie jest wskaźnik na obiekt, wywołanie

klocek:=TBitmap.Create();

stworzy nowy obiekt i podłączy go do klocka
a skoro do klocka przypisujesz inny obiekt to klocek wskazuje już na inny obiekt, czyli tego klocka to w ogóle nie tworzysz =]

ale to użycie 8 osobnych obiektów jest trochę nie tego, nie możesz użyć np TImageList ( ImageList1.Add() i wczytujesz lub po prostu na stałe wmontować ) ?

wtedy np:

procedure rysuj(x,y,kolor:byte);
begin
  if kolor>7 then
    kolor:=0;
  ImageList1.Draw(Form1.PaintBox1.Canvas,(x-1)*20,(y-1)*20,kolor);
end;

a pokaż jak chowasz/pokazujesz formę ze spadającymi klockami ( o ile się dobrze domyślam =] ) bo podejrzewam że tu może być ten problemik ukryty =]

0

No tak, ten klocek traktuje jako taki wskaźnik ;) nie miałem pomysłu jak to zrobić inaczej, więc wykombinowałem tak. Spróbuje z tym ImageList.

procedure TForm1.FormCreate(Sender: TObject);
var i,j : byte;
begin
 Btlo:=false;
 start:=false;
 score:=0;
 level:=1;
 
  kolumna:=4;
  wiersz:=1;
  l:=0;

  For i:=1 to 10 do
   For j:=1 to 18 do
    plansza[i,j]:=0;

  For i:=0 to 11 do
   plansza[i,19]:=-1;

  For i:=1 to 19 do
  begin
   plansza[0,i]:=-1;
   plansza[11,i]:=-1;
  end;


  block_empty:=TBitmap.Create;
  block_aqua:=TBitmap.Create;
  block_blue:=TBitmap.Create;
  block_green:=TBitmap.Create;
  block_orange:=TBitmap.Create;
  block_red:=TBitmap.Create;
  block_violet:=TBitmap.Create;
  block_yellow:=TBitmap.Create;

  block_empty.LoadFromResourceName(hInstance,'empty');
  block_aqua.LoadFromResourceName(hInstance,'aqua');
  block_blue.LoadFromResourceName(hInstance,'blue');
  block_green.LoadFromResourceName(hInstance,'green');
  block_orange.LoadFromResourceName(hInstance,'orange');
  block_red.LoadFromResourceName(hInstance,'red');
  block_violet.LoadFromResourceName(hInstance,'violet');
  block_yellow.LoadFromResourceName(hInstance,'yellow');



  tlo:=TJPEGRes.Create;
  tlo.LoadFromResource('bg_main');

  nextBlockPreview:=TJPEGRes.Create;
  digitImg:=TJPEGRes.Create;


  //  test
odswiez;
//id_act:=1;
losuj_klocek(1);
stan_act:=1;
get_block(id_act,stan_act,1);

losuj_klocek(2);
get_block(id_next,1,2);
rysuj_next;


rysuj_klocek;
//Timer1.Enabled:=True;


end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Timer1.Enabled:=False;
block_empty.Free;
block_aqua.Free;
block_blue.Free;
block_green.Free;
block_orange.Free;
block_red.Free;
block_violet.Free;
block_yellow.Free;



tlo.Free; 

Form3.Show;

end;
0

chodziło mi o to jak wywołujesz Form1 =]

jak wywalasz obrazki w OnClose a tworzysz w OnCreate to musi to być sparowane żeby nie było wypadku że nie masz utworzonych obrazków więc prawdopodobnie gdzieś masz Form1.Create czy coś takiego

dobrze zgaduję ? =]

0
procedure TForm3.PlayBtnClick(Sender: TObject);
begin
PlaySound('snd_beep',hInstance, SND_ASYNC + SND_RESOURCE);

Form1:=TForm1.Create(Application);

Form1.Show;
Form1.Timer1.Enabled:=True;

Form3.Hide;
end;
0

no i masz =D to samo jak w tym co mówiłem o chwilowym tworzeniu bitmapy =]
niech forma tak jak zazwyczaj tworzy się na starcie aplikacji i tylko operuj na Show i Hide zamiast Create i Destroy =D
tylko musisz się trochę wziąć do roboty i dodać resetowanie gry =D pewnie operujesz na timerkach itd ? jak tak to przejdź na wątki i wtedy twórz i niszcz ten wątek co coś tam robi =] przy wątkach jakoś nie zauważyłem "tycia", a jak nie chcesz sobie ułatwić wątkami to "po ludzku" czyść odpowiednie zmienne i wyłączaj timery =D

0

btw tworzenie formy i potem włączanie timerka jest bezsensu trochę =] nie lepiej dać Enabled na True jako domyślne =D

0

Forma jest tworzona standardowo, na początku aplikacji :)
Form1.Destroy nie mam nigdzie, jedynie Close.
Wątki to na razie raczej nie na moje umiejętności ;)
Enabled nie moge dać na True, bo wtedy on będzie działał od utworzenia, a ja chce dopiero od pokazania sie formy.

Z resztą chyba to oleje ;) Przyrost nie jest aż taki duży.
Po dodaniu zasobów do exe'ka program na starcie zajmuje ok 9MB, więc tym bardziej proporcjonalnie te 300kB to mało.

0

whaha=D

Form1:=TForm1.Create(Application);

tworzysz ale nie niszczysz? =D bawiłeś się w robienie stosu czy kolejki w starym paszczaku? tam przez jedną zmienna miało się dostęp do wielu tego samego typu =D tu tworzysz, nie niszczysz, więc sobie gdzieś tam w ramie siedzi i pierdzi coraz więcej form =D

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