Wartezimmer mit Hilfe von TList

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
das_karlchen
Beiträge: 4
Registriert: Sa 30. Jan 2016, 14:01

Wartezimmer mit Hilfe von TList

Beitrag von das_karlchen »

Hallo zusammen,

wir haben eine Einführung in OOP hinter uns und sollen nun ein Wartezimmer programmieren (scheint ein gern gewähltes Beispiel). Ich habe nun eine Klasse TPatient angelegt welche Name, Geburtsdatum und Krankenkasse enthält.
Wir sollen nun mit TList arbeiten und neue Patienten dort aufnehmen und sobald sie vom Wartezimmer ins Sprechzimmer zum Arzt gehen, sollen sie aus der Liste des Wartezimmers entfernt werden.
Ich habe nun eine Liste:TList; und sie mit Liste:=TList.Create; erzeugt. In meinem Formular habe ich einen Button für die Patientenaufnahme und die Ausgabe der Warteliste. Aber irgendwie klappt die Verbindung zwischen der Tlist und der Klasse TPatient nicht. Wenn ich zwei Patienten eingebe, bekomm ich zwar in der Liste zwei Patienen ausgeben, allerdings nur die Werte des letzten Patientens.

Folgendes ist mir einfach nicht klar und ich finde keine Antwort:
Kann der Pointer die Klasse selbst sein? Also z.b. A:^TPatient;?

Type sieht wie folgt aus:

Code: Alles auswählen

type
 
  { TFWartezimmer }
 
  TFWartezimmer = class(TForm)
    EName: TEdit;
    EVorname: TEdit;
    BAufnehmen: TButton;
    BAusgabe: TButton;
    MAusgabe: TMemo;
    BDrannehmen: TButton;
    EGeburtsdatum: TEdit;
    EKrankenkasse: TEdit;
    LName: TLabel;
    LVorname: TLabel;
    Lgeburtsdatum: TLabel;
    LKasse: TLabel;
    procedure BDrannehmenClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BAusgabeClick(Sender: TObject);
    procedure BAufnehmenClick(Sender: TObject);
    procedure Aufnehmen(P:TPatient);
    procedure Ausgabe(P:TPatient);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
     //P:TPatient;
     Liste:TList;
     Nr : Integer;
  end;
 
var
  FWartezimmer: TFWartezimmer;
  P:TPatient;
  A:^TPatient;    
Aktuell sieht meine Prozedur für den Button Aufnahme wie folgt aus:

Code: Alles auswählen

procedure TFWartezimmer.BAufnehmenClick(Sender: TObject);
begin
   New(A);
   Aufnehmen(P);
   A^:=P;
   Liste.Add(A);
   Nr:=Liste.Count-1;
end;
Aufnahme(P) liest aus Eingabefeldern den Inhalt ein für Name, Geburtsdatum,...

Bei dem Button der Ausgabe hab ich folgendes:

Code: Alles auswählen

procedure TFWartezimmer.BAusgabeClick(Sender: TObject);
VAR //i:Integer;
  Nr:Integer;
begin
   for Nr:=0 to Liste.Count-1 do
   begin
     A:=Liste.Items[Nr];
     Ausgabe(P);
   end;
end;  
 
Ich bin völlig überfragt. Ich hab auch das Tutorial(http://www.delphi-treff.de/tutorials/vcl/tlist/) durch, aber es hilft mir leider nicht.
Vielleicht kann mir hier jemand helfen. Ich bin leider wirklicher Anfänger und kenn mich mit OOP nicht gut aus.

Vielen Dank,
das_karlchen

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von theo »

Das Objekt ist ein Pointer. Das muss man nicht über einen Extra Pointer ansprechen.

Ein Beispiel:

Code: Alles auswählen

type
 
TPatient = class
  private
   fProp1:String;
  public
   constructor Create(id:String);
  end;
.............
 
constructor TPatient.Create(id: String);
begin
  fProp1:=id;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var list:TList;
  i: Integer;
begin
 list:=TList.Create;
 list.add(TPatient.Create('eins'));
 list.add(TPatient.Create('zwei'));
 list.add(TPatient.Create('drei'));
 For i:=0 to list.Count-1 do ShowMessage(TPatient(list[i]).fProp1);
 list.free; //Die Patients müsste man auch noch freen, dafür gäbe es auch TObjectList http://www.freepascal.org/docs-html/3.0.0/fcl/contnrs/tobjectlist.html
end;    

das_karlchen
Beiträge: 4
Registriert: Sa 30. Jan 2016, 14:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von das_karlchen »

Danke für deine Antwort, aber leider komme ich damit nicht weiter.

In meinen Unterlagen ist diese "Constructor" auch drin, aber Lazarus gibt mir jedes mal eine Fehlermeldung. Hier mal meine Unit in der meine Klasse TPatient drin steht:

Code: Alles auswählen

UNIT mTPatient;
 
{$MODE Delphi}
 
interface
 
//--------------------  ggf Uses-Liste einfügen !  --------------------
//uses ....;
 
type
  TPatient = class
  private
    { Private-Deklarationen }
    Name : String;
    Vorname : String;
    Geburtsdatum : String;
    Krankenkasse : String;
  public
    { Public-Deklarationen }
    //constructor Create;
    procedure SetName (pName: String);
    procedure SetVorname (pVorname: String);
    procedure SetGeburtsdatum (pGeburtsdatum: String);
    procedure SetKrankenkasse (pKrankenkasse: String);
    function GetName : String;
    function GetVorname : String;
    function GetGeburtsdatum : String;
    function GetKrankenkasse : String;
   end;
 
implementation
 
//+---------------------------------------------------------------------
//|         TPatient: Methodendefinition 
//+---------------------------------------------------------------------
 
procedure TPatient.SetName (pName: String);
begin
   Name:=pName;
end;
 
function TPatient.GetName : String;
begin
   result:=Name;
end;
 
procedure TPatient.SetVorname (pVorname: String);
begin
   Vorname:=pVorname;
end;
 
function TPatient.GetVorname : String;
begin
   result:=Vorname;
end;
 
procedure TPatient.SetGeburtsdatum (pGeburtsdatum: String);
begin
   Geburtsdatum:=pGeburtsdatum;
end;
 
function TPatient.GetGeburtsdatum : String;
begin
   result:=Geburtsdatum;
end;
 
procedure TPatient.SetKrankenkasse (pKrankenkasse: String);
begin
   Krankenkasse:=pKrankenkasse;
end;
 
function TPatient.GetKrankenkasse : String;
begin
   result:=Krankenkasse;
end;
 
 
end.
ich hab "constructor create" auskommentiert, weil ich sonst den Fehler "mTPatient.pas(20,17) Error: Forward declaration not solved "constructor TPatient.Create;" erhalte.

Meine Wartezimmer Unit sieht momentan so aus:

Code: Alles auswählen

unit U_Wartezimmer;
 
{$MODE Delphi}
 
interface
 
uses
  LCLIntf, LCLType, {LMessages, Messages,} SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, mTPatient;
 
type
 
  { TFWartezimmer }
 
  TFWartezimmer = class(TForm)
    EName: TEdit;
    EVorname: TEdit;
    BAufnehmen: TButton;
    BAusgabe: TButton;
    MAusgabe: TMemo;
    BDrannehmen: TButton;
    EGeburtsdatum: TEdit;
    EKrankenkasse: TEdit;
    LName: TLabel;
    LVorname: TLabel;
    Lgeburtsdatum: TLabel;
    LKasse: TLabel;
    procedure BDrannehmenClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BAusgabeClick(Sender: TObject);
    procedure BAufnehmenClick(Sender: TObject);
    procedure Aufnehmen(P:TPatient);
    procedure Ausgabe(P:TPatient);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
     P:TPatient;
     Liste:TList;
     Nr : Integer;
  end;
 
var
  FWartezimmer: TFWartezimmer;
  P:TPatient;
  A:^TPatient;
 
implementation
 
{$R *.lfm}
 
procedure TFWartezimmer.FormCreate(Sender: TObject);
begin
   Liste:=TList.Create;
   Nr := 0;
   P:=TPatient.Create;
 
end;
 
procedure TFWartezimmer.Aufnehmen(P:TPatient);
begin
   P.SetName(FWartezimmer.EName.Text);
   P.SetVorname(FWartezimmer.EVorname.Text);
   P.SetGeburtsdatum(FWartezimmer.EGeburtsdatum.Text);
   P.SetKrankenkasse(FWartezimmer.EKrankenkasse.Text);
end;
 
procedure TFWartezimmer.Ausgabe(P:TPatient);
begin
   FWartezimmer.MAusgabe.Lines.Add(P.GetName);
   FWartezimmer.MAusgabe.Lines.Add(P.GetVorname);
   FWartezimmer.MAusgabe.Lines.Add(P.GetGeburtsdatum);
   FWartezimmer.MAusgabe.Lines.Add(P.GetKrankenkasse);
end;
 
procedure TFWartezimmer.BAufnehmenClick(Sender: TObject);
begin
   // Hier den Quelltext zum Einfügen der Patientendaten in die Liste erstellen.
   New(A);
   Aufnehmen(P);
   A^:=P;
   Liste.Add(A);
   Nr:=Liste.Count-1;
end;
 
procedure TFWartezimmer.BAusgabeClick(Sender: TObject);
VAR //i:Integer;
  Nr:Integer;
begin
   // Hier den Quelltext zur Ausgabe aller Patientendaten erstellen.
   for Nr:=0 to Liste.Count-1 do
   begin
     P:=Liste.Items[Nr];
     Ausgabe(P);
   end;
end;
 
procedure TFWartezimmer.BDrannehmenClick(Sender: TObject);
begin
   // Hier den Quelltext zum Entferenen der Daten des ersten Patienten
   // aus der Liste erstellen.
end;
 
end.
Ich bin einfach überfragt. Und weiß nicht, wo ich ansetzen muss.

Warf
Beiträge: 2121
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Wartezimmer mit Hilfe von TList

Beitrag von Warf »

"mTPatient.pas(20,17) Error: Forward declaration not solved "constructor TPatient.Create;
Der Fehler besagt schlicht weg, dass du im interface Teil der Unit, in der Klassendefinition zwar diese Methode angegeben hast, sie im implementation Teil nicht implementierst. Da du es aber einfach auskommentieren kannst benötigst du den Konstruktor anscheinend sowieso nicht

Zu deinem Problem, statt A: ^TPatient
und New(A) verwende lieber TPatient als Zeiger selbst:

Code: Alles auswählen

A:=TPatient.Create;
...
Liste.Add(Pointer(A));
 
// Aus liste auslesen:
TPatient(Liste[i]).SetName("Arnold");
 
Mal ganz davon abgesehen dass euer Lehrer euch wenigstens mal properties und TObjectlist sowie den as Operator beibringen sollte, das ist ja grauenvoll wie der euch Pascal beibringt, so etwas

Code: Alles auswählen

procedure TPatient.SetName (pName: String);
begin
   Name:=pName;
end;
 
function TPatient.GetName : String;
begin
   result:=Name;
end;
Sollte es schon lange nicht mehr geben, und bei den Namenkonventionen hat er auch nicht aufgepasst, Felder von Klassen werden mit F (für Field) gekennzeichnet, und in Pascal werden Variablen sowieso groß geschrieben, also FName statt Name, und PName statt pName, noch besser wäre sogar etwas aussagekräftigeres wie NewName oder so.

Sieht für mich nach einem klassischen Fall eines (ex-)Java Programmierers aus. Deren Programmierstil ist versaut fürs Leben. Vielleicht hat er auch die letzen 15-20 Jahre Programmiersprachen Innovation in einer Höhle verbracht

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von theo »

@Warf: Jetzt übertreibst du aber.
"F" für Field ist schon gut, aber ob das groß oder klein ist, spielt für das Problem von das_karlchen nun wirklich keine Rolle.
Ich schreibe es immer klein, weil für mich die Variable so leserlicher wird.
Also fUser statt FUser oder fForward statt FForward.

Ja, ich weiß, dass es in der LCL anders ist. Für mich ist das nicht gut.

Warf
Beiträge: 2121
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Wartezimmer mit Hilfe von TList

Beitrag von Warf »

Ja gut über Groß und Kleinschreibung kann man sich streiten, dennoch diese Nomenklaturen, auch z.B.

Code: Alles auswählen

EName: TEdit;
    EVorname: TEdit;
    BAufnehmen: TButton;
    BAusgabe: TButton;
    MAusgabe: TMemo;
    BDrannehmen: TButton;
    EGeburtsdatum: TEdit;
    EKrankenkasse: TEdit;
    LName: TLabel;
    LVorname: TLabel;
    Lgeburtsdatum: TLabel;
    LKasse: TLabel;
Sind grauenvoll, E Steht für Error/Exception, und wird selbst auch in vielen anderen Programmiersprachen so verwendet, L steht in C Ähnlichen Sprachen für Long, was für Pascal Programmierer allerdings nicht so wichtig ist.

Ich behaupte einfach mal, das das zum einen Didaktisch sehr wenig Sinn hat. Und kein Programmierer der noch bei Verstand ist und halbwegs Wartbaren und leserlichen Code Erzeugen will würde diese Namen wählen

das_karlchen
Beiträge: 4
Registriert: Sa 30. Jan 2016, 14:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von das_karlchen »

Ich danke euch sehr für eure Mühe, aber ich verstehs einfach nicht. Ich hab meine Aufnahme und Ausgabe Prozeduren jetzt verworfen und sie direkt in die Prozeduren der jeweiligen Buttons geschrieben.
Auch mit euren Hilfen geht esleider immer noch nicht verschiedene Patienten einzutragen. Der Counter "Nr" zählt zwar hoch, kontrollier ich mittels einer Ausgabe, aber wenn ich bei die Button zur Patientenausgabe drücke, kommt nur der Letzte Patient, obwohl auch da der Counter zählt (Extra Zeile in der TMemo-Ausgabe).

Code: Alles auswählen

unit U_Wartezimmer;
 
{$MODE Delphi}
 
interface
 
uses
  LCLIntf, LCLType, {LMessages, Messages,} SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, mTPatient;
 
type
 
  { TFWartezimmer }
 
  TFWartezimmer = class(TForm)
    Edit1: TEdit;
    EName: TEdit;
    EVorname: TEdit;
    BAufnehmen: TButton;
    BAusgabe: TButton;
    MAusgabe: TMemo;
    BDrannehmen: TButton;
    EGeburtsdatum: TEdit;
    EKrankenkasse: TEdit;
    LName: TLabel;
    LVorname: TLabel;
    Lgeburtsdatum: TLabel;
    LKasse: TLabel;
    procedure BDrannehmenClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BAusgabeClick(Sender: TObject);
    procedure BAufnehmenClick(Sender: TObject);
    //procedure Aufnehmen(P:TPatient);
    //procedure Ausgabe(P:TPatient);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
     P:TPatient;
     Liste:TList;
     Nr : Integer;
  end;
 
var
  FWartezimmer: TFWartezimmer;
  P:TPatient;
  A:^TPatient;
 
implementation
 
{$R *.lfm}
 
procedure TFWartezimmer.FormCreate(Sender: TObject);
begin
   Liste:=TList.Create;
   Nr := 0;
   P:=TPatient.Create;
 
end;
 
{procedure TFWartezimmer.Aufnehmen(P:TPatient);
begin
   TPatient(Liste[Nr]).SetName(FWartezimmer.EName.Text);
   TPatient(Liste[Nr]).SetVorname(FWartezimmer.EVorname.Text);
   TPatient(Liste[Nr]).SetGeburtsdatum(FWartezimmer.EGeburtsdatum.Text);
   TPatient(Liste[Nr]).SetKrankenkasse(FWartezimmer.EKrankenkasse.Text);
end;
 
procedure TFWartezimmer.Ausgabe(P:TPatient);
begin
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetName);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetVorname);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetGeburtsdatum);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetKrankenkasse);
end;   }
 
procedure TFWartezimmer.BAufnehmenClick(Sender: TObject);
begin
   // Hier den Quelltext zum Einfügen der Patientendaten in die Liste erstellen.
   Liste.Add(Pointer(P));
   TPatient(Liste[Nr]).SetName(FWartezimmer.EName.Text);
   TPatient(Liste[Nr]).SetVorname(FWartezimmer.EVorname.Text);
   TPatient(Liste[Nr]).SetGeburtsdatum(FWartezimmer.EGeburtsdatum.Text);
   TPatient(Liste[Nr]).SetKrankenkasse(FWartezimmer.EKrankenkasse.Text);
   Nr:=Liste.Count-1;
   Edit1.Text:=IntToStr(Nr);
end;
 
procedure TFWartezimmer.BAusgabeClick(Sender: TObject);
VAR Nr:Integer;
begin
   // Hier den Quelltext zur Ausgabe aller Patientendaten erstellen.
   for Nr:=0 to Liste.Count-1 do
   begin
     P:=Liste.Items[Nr];
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetName);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetVorname);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetGeburtsdatum);
   FWartezimmer.MAusgabe.Lines.Add(TPatient(Liste[Nr]).GetKrankenkasse);
   FWartezimmer.MAusgabe.Lines.Add(IntToStr(Nr));
   end;
end;
 
procedure TFWartezimmer.BDrannehmenClick(Sender: TObject);
begin
   // Hier den Quelltext zum Entferenen der Daten des ersten Patienten
   // aus der Liste erstellen.
end;
 
end.
Dateianhänge
Eingabe des zweiten Patientens (Nr = 1)
Eingabe des zweiten Patientens (Nr = 1)
Eingabe des ersten Patientens (Nr = 0)
Eingabe des ersten Patientens (Nr = 0)

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von theo »

Ja ist halt schwierig so.
Sag doch deinem Lehrer, er soll euch das noch einmal erklären. :wink:

Indem du immer den gleichen Pointer (Objekt) in die Liste "addest", erstellst du natürlich keinen neuen TPatient.
Du hast nur einen Patienten, und den "addest" du immer wieder.
Einen neuen Patienten erhältst du nur mit "TPatient.Create;"

Versuch doch erst mal mein Beispiel http://www.lazarusforum.de/viewtopic.php?p=82919#p82919 zu verstehen, bevor du weiter gehst mit deinem Vorhaben.

wp_xyz
Beiträge: 5153
Registriert: Fr 8. Apr 2011, 09:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von wp_xyz »

Ohne das jetzt im Detail zu überblicken, fällt mir auf, dass du mit unnötigen globalen Variablen arbeitest. P - das verwendest du zum einen als vordefinierten Patienten, der in FormCreate erzeugt wird und in BtnAufnehmen der Liste hinzugefügt wird, gleichzeitig als (gar nicht verwendete) Variable, in die in BAusgabe die Listenitems eingelesen werden. Das muss schief gehen.
--> Entferne P aus der Var-Liste. Und A kannst du auch gleich wegtun, das brauchst du auch nicht.
--> Entferne auch Nr aus der deklaration der Klasse TFWartezimmer

Den P würde ich dann lokal in der BAufnehmenClick-Methode deklarieren, dort erzeugen, mit Werten befüllen und in der Liste speichern. Danach brauchst du P nicht mehr --> daher lokal. Auch die Nr wird nur hier benötigt --> lokal!

Code: Alles auswählen

 
procedure TFWartezimmer.BAufnehmenClick(Sender: TObject);
var
  P: TPatient;
  Nr: Integer;  
begin
   // Hier den Quelltext zum Einfügen der Patientendaten in die Liste erstellen.
  P := TPatient.Create;
  P.SetName(FWartezimmer.EName.Text);
  P.SetVorname(... // usw).
  Nr := Liste.Add(P);  // Add gibt den Index des eingefügten Pointers zurück
  Edit1.text := InttoStr(Nr);
end;
In BAusgabe, wiegesagt, braucht du P gar nicht. Du köntest aber, zur Schreibvereinfachung und verbesserten Lesbarkeit, durchaus eine Variable P verwenden. Aber bitte wieder lokal, damit du damit nichts anderes kaputt machen kannst:

Code: Alles auswählen

 
procedure TFWartezimmer.BAusgabeClick(Sender: TObject);
VAR 
  Nr:Integer; 
  P: TPatient;
begin
   // Hier den Quelltext zur Ausgabe aller Patientendaten erstellen.
   for Nr:=0 to Liste.Count-1 do
   begin
     P:=TPatient(Liste.Items[Nr]);  // du musst eine Typumwandlung nehmen, denn die Liste speichert nur Pointer
    FWartezimmer.MMausgabe.Lines.Add(P.GetName);
    F.Wartezimmer.MMAusgabe.Lines.Add(P.GetVorname);
    // etc
  end;
end;

das_karlchen
Beiträge: 4
Registriert: Sa 30. Jan 2016, 14:01

Re: Wartezimmer mit Hilfe von TList

Beitrag von das_karlchen »

Ich danke euch für eure schnelle Hilfe.

@wp_xyz: Besonderer Dank an dich, deine Hilfe hat mich zur Lösung gebracht. Die anderen Funktionen, des Löschens habe ich zum Glück dann alleine geschafft.

Antworten