![]() |
Delphi-Version: 6
OOP-Delphi-Anfänger
Hallo ,
wir haben im Informatikunterricht mit der Objektorientierten Programmierung in Delphi angefangen. Am Anfang haben wir die gnazen Begriffe geklärt (Instanzen,Objekte , Klassen und Methoden). Jetzt sollen wir folgendes machen : Der Lehrer will von uns , dass wir Mithilfe der OOP in Delphi ein Rechteck programmieren , das sich mit einem Buttonklick zeichnen lässt.(Nicht grad einfach). Bin so weit gekommen :
Code:
Und jetzt , da passiert nix sobald ich auf den Button drücke.
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) RechteckBTN: TButton; LoeschBTN: TButton; EndeBTN: TButton; PaintBox1: TPaintBox; private { Private-Deklarationen } public { Public-Deklarationen } end; TRechtecke = class // Hier ist die Klassenbeschreibung laenge:integer; breite:integer; private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; Rechteck: TRechtecke; implementation procedure rechteckmachen(); begin Form1.Paintbox1.Canvas.MoveTo(20,20); // Diese procedure soll irgendwie so eine Art Anfang sein für das Zeichnen. Form1.Paintbox1.Canvas.MoveTo(100,100); end; {$R *.dfm} end. Das Problem ist ja , ich kenn die Befehle garnicht , mit denen man so ein Rechteck zeichnen soll. Bitte nicht so kompliziertes sagen/empfehlen , hab nicht so viel Erfahrung mit OOP. Ist das erste Mal , dass ich mit Klassen etc arbeite und selbst Klassen erstelle. |
AW: OOP-Delphi-Anfänger
Ein Rechteck hat ja nicht nur eine Höhe und Breite sondern auch noch eine X- und Y-Position.
Also sieht deine Rechteckklasse so aus:
Delphi-Quellcode:
Jetzt soll dein Rechteck gezeichnet werden.
TRechteck = class // ein Rechteck im 2-dimensionalen Raum
private { Private-Deklarationen } public { Public-Deklarationen } XPos:integer; YPos:integer; Hoehe:integer; Breite:integer; end; Dazu benötigt man eine Zeichenfläche (TCanvas) und eine Methode die das Zeichnen übernimmt:
Delphi-Quellcode:
TRechteck = class // ein Rechteck im 2-dimensionalen Raum
private { Private-Deklarationen } public { Public-Deklarationen } procedure Zeichne(canvas:TCanvas); XPos:integer; YPos:integer; Hoehe:integer; Breite:integer; end; procedure TRechteck.Zeichne(canvas:TCanvas); begin canvas.Pen.Color := clBlack; // 1. Linie canvas.MoveTo(XPos, YPos); canvas.MoveTo(XPos, YPos+breite); // 2. Linie canvas.MoveTo(XPos+Hoehe, YPos+breite); // 3. und 4. Linie ist dein Job :-) end; |
AW: OOP-Delphi-Anfänger
Kleiner Schönheitsfehler:
Methoden müssen nach Feld/Variablendeklarationen kommen! Anderer Fehler: MoveTo() bewegt den "Stift" (ohne eine Linie zu ziehen) LineTo() hingegen zieht auch eine Linie! Und wer das Rad nicht neu erfinden will, der benützt Rectangle() |
AW: OOP-Delphi-Anfänger
Zitat:
|
AW: OOP-Delphi-Anfänger
Vielen Dank für den Ansatz.
Diese Fehler habe ich glaub ich gefunden , eins jedenfalls. Für den Anfang sollen wir die Methoden unter dem implentation-Teil schreiben. So sieht mein Code aus , ich kriege aber einen Identifiier redeclared für Xpos , YPos , Hoehe und Breite
Code:
FEHLER:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) RechteckBTN: TButton; LoeschBTN: TButton; EndeBTN: TButton; PaintBox1: TPaintBox; private { Private-Deklarationen } public { Public-Deklarationen } end; TRechteck = class Hoehe:integer; Breite:integer; XPos:integer; YPos:integer; private { Private-Deklarationen } public { Public-Deklarationen } XPos:integer; YPos:integer; Hoehe:integer; Breite:integer; end; end; var Form1: TForm1; Rechteck: TRechtecke; implementation procedure TRechteck.Zeichne(canvas:TCanvas); begin canvas.Pen.Color := clBlack; // 1. Linie canvas.MoveTo(XPos, YPos); canvas.MoveTo(XPos, YPos+breite); // 2. Linie canvas.MoveTo(XPos+Hoehe, YPos+breite); // 3. und 4. Linie ist dein Job :-) end; {$R *.dfm} end. [Error] Unit1.pas(31): Identifier redeclared: 'XPos' [Error] Unit1.pas(32): Identifier redeclared: 'YPos' [Error] Unit1.pas(33): Identifier redeclared: 'Hoehe' [Error] Unit1.pas(34): Identifier redeclared: 'Breite' [Error] Unit1.pas(37): 'IMPLEMENTATION' expected but ';' found [Error] Unit1.pas(45): '.' expected but 'IMPLEMENTATION' found [Fatal Error] Project1.dpr(5): Could not compile used unit 'Unit1.pas' |
AW: OOP-Delphi-Anfänger
Zitat:
@hotkey Eigne dir die Delphi Syntax an. Du hast die Methode Zeichne nicht definiert. Daher kennt die undeklarierte Methode auch die Elemente der übergeordnetten Klasse nicht! |
AW: OOP-Delphi-Anfänger
Zitat:
Wenn du die Oben weg nimmst, stimmt es wieder :) Edit: oder auch nicht. Das von Aphton auch noch. Gruß Teekeks |
AW: OOP-Delphi-Anfänger
Zitat:
Wie deklariere ich das bzw wie definiere ich das ? Könntes tdu mir bitte ein Tipp geben ? Edit : Zeichne : TRechteck; Ist das richtig ? EDIT 2 : Danke auch an Teekeks ! |
AW: OOP-Delphi-Anfänger
Zitat:
|
AW: OOP-Delphi-Anfänger
Also Zeichne:TRechteck funzt nicht.
Aber ich muss ihm doch sagen , dass diese procedure die Variablem Xpos etc aus der Klasse entnehmen soll. Da verstehe ich nicht , warum Zeichne:TRechteck nicht geht.. |
AW: OOP-Delphi-Anfänger
Zitat:
Zitat:
|
AW: OOP-Delphi-Anfänger
Ich komm echt nicht drauf :(
Was mir einfällt :
Code:
TRechteck = class // ein Rechteck im 2-dimensionalen Raum private { Private-Deklarationen } public { Public-Deklarationen } XPos:integer; YPos:integer; // Hier im Public-Teil werden ja die Variablen deklariert , das verstehe ich noch , hab die procedure aus dem public-teil entfernt. Hoehe:integer; Breite:integer; end; procedure TRechteck.Zeichne(canvas:TCanvas); // Diese procedure reicht doch oder nciht ? Zeichne gehört zu TRechteck und entnimmt von dort die Variablen und kann begin // ganz normal zeichnen ,oder nciht ? canvas.Pen.Color := clBlack; // 1. Linie canvas.MoveTo(XPos, YPos); canvas.MoveTo(XPos, YPos+breite); // 2. Linie canvas.MoveTo(XPos+Hoehe, YPos+breite); // 3. und 4. Linie ist dein Job :-) end; |
AW: OOP-Delphi-Anfänger
Es ist doch sinnlos, wenn du mit der Materie nicht vertraut bist.
Ich meins nicht böse, aber du ersparst allen - auch dir selbst - viel Mühe, wenn du zuerst die Grundlagen lernst. Zu deiner Frage - das eine ist ein Apfel, das andere ne Birne! Du versuchst, eine Variable zu definieren; gemeint war aber eine Methode! Methoden sind entweder Prozeduren/Funktionen, die sich in einer Klasse befinden! Edit: "Diese procedure reicht doch oder nciht ? Zeichne gehört zu TRechteck und entnimmt von dort die Variablen und kann (..)" Nein, eben nicht. Diese Procedure weiß ja nicht, wo sie hingehört, da der Klasse TRechteck nicht mitgeteilt wurde, dass sie ihr gehört (sozusagen) Du kannst dich ja auch nicht in ein Auto setzen, das keinen Sitz hat. |
AW: OOP-Delphi-Anfänger
Zitat:
Das ist das Prblem. Unser Lehrer hat gesagt wir sollen die Methoden im implementation-Teil reinprogrammieren. Die Delphi-Syntax ist mir ja klar , hatte ja vorher Records und so aber bei den Klassen muss ich mich noch an paar Sachen gewöhnen , bitte nciht böse sein. Aber ich muss das hinkriegen. EDIT : HAT GEFUNZT :D:D: Ich muss ads nur noch mit einem Button verknüpfen , mnelde mich bei Problemen. VIELEN DANK AN ALLE :D ja man :D |
AW: OOP-Delphi-Anfänger
Ok, also entweder hat dein Lehrer euch das falsch erklärt, oder du hast es falsch in Erinnerung.
Es muss, genauso wie die Variable in der Klasse deklariert wurden, auch in der Klasse deklariert sein. Verwechsle es nicht mit Variablendeklaration. |
AW: OOP-Delphi-Anfänger
Eine Methode gehört zu einer Klasse. Es darf daher nicht heißen:
Delphi-Quellcode:
sondern
function Zeichne: TRechteck;
Delphi-Quellcode:
procedure TRechteck.Zeichne;
|
AW: OOP-Delphi-Anfänger
Das wird ihn eventuell verwirren, da er die Implementation der Methode eh richtig hatte.
Jetzt wird er bei der Definition sicher einen Fehler begehen |
AW: OOP-Delphi-Anfänger
Wieso? Ich habe ja nur noch einmal auf den Unterschied zwischen Methode und "regulärer" Funktion hingewiesen.
|
AW: OOP-Delphi-Anfänger
Also mein Code sieht jetzt so aus
Code:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) RechteckBTN: TButton; LoeschBTN: TButton; EndeBTN: TButton; PaintBox1: TPaintBox; procedure RechteckBTNClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; TRechteck = class private { Private-Deklarationen } public { Public-Deklarationen } XPos:integer; YPos:integer; Hoehe:integer; Breite:integer; procedure Zeichne(canvas:TCanvas); end; var Form1: TForm1; Rechteck: TRechteck; Zeichne:TRechteck; implementation procedure TRechteck.Zeichne(canvas:TCanvas); begin canvas.Pen.Color:= clBlack; // 1. Linie canvas.MoveTo(XPos, YPos); canvas.MoveTo(XPos, YPos+breite); // 2. Linie canvas.MoveTo(XPos+Hoehe, YPos+breite); // 3. und 4. Linie ist dein Job :-) end; {$R *.dfm} begin TRechteck.Zeichne procedure TForm1.RechteckBTNClick(Sender: TObject); // Das hier ist der Button , wenn ich auf den klicke , soll die Prozedur // TRechteck.Zeichne aufgerufen werden , aber der Button " an sich " ist ja eine Procedure // Ich weiß , wenn ich mit TRechteck.Zeichne arbeiten will die Parameter eingeben muss aber // ich kann ja jetzt einfach unter Button-Procedu TRechteck.Zeichne(20,20) oder so eingeben begin end; end. |
AW: OOP-Delphi-Anfänger
Es wäre schön, wenn du für Delphi-Code auch die Delphi-Tags benutzen würdest :) (das ist der Knopf mit dem Helm direkt neben dem Code-Knopf)
Hier mal ein Beispiel-Code wie das dann funktionieren kann. Nicht wirklich fertig, aber davon solltest du den Rest ableiten können, bzw. etwas konkretere Fragen stellen können. (Manchmal muss man in die richtige Richtung gedreht werden ;) )
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class( TForm ) The_PaintBox : TPaintBox; btn_Rechteck : TButton; // Im Object-Inspector das Ereignis "OnClick" von "btn_Rechteck" mit "btn_RechteckClick" verbinden procedure btn_RechteckClick( Sender : TObject ); private { Private-Deklarationen } public { Public-Deklarationen } end; TRechteck = class hoehe, breite : Integer; // Zeichnet relativ zur aktuellen Stiftposition auf "aCanvas" das Rechteck procedure ZeichneAuf( aCanvas : TCanvas ); end; var Form1 : TForm1; implementation {$R *.dfm} { TRechteck } procedure TRechteck.ZeichneAuf( aCanvas : TCanvas ); begin // Jedes Zeichnen mit "LineTo" setzt auch die Stiftposition "PenPos" aCanvas.LineTo( aCanvas.PenPos.X + Self.breite, aCanvas.PenPos.y ); // Linie nach rechts aCanvas.LineTo( aCanvas.PenPos.X, aCanvas.PenPos.y + Self.hoehe ); // Linie nach unten aCanvas.LineTo( aCanvas.PenPos.X - Self.breite, aCanvas.PenPos.y ); // Linie nach links aCanvas.LineTo( aCanvas.PenPos.X, aCanvas.PenPos.y - Self.hoehe ); // Linie nach oben end; { TForm1 } procedure TForm1.btn_RechteckClick( Sender : TObject ); var MyRechteck : TRechteck; begin // Diese Methode soll aufgerufen werden, wenn der Button "btn_Rechteck" gedrückt wurde // Die Stiftposition in der PaintBox setzen The_PaintBox.Canvas.MoveTo( 50, 40 ); // Instanz vom Rechteck erzeugen MyRechteck := TRechteck.Create; // Werte zuweisen MyRechteck.hoehe := 50; MyRechteck.breite := 80; // Zeichen-Methode aufrufen MyRechteck.ZeichneAuf( The_PaintBox.Canvas ); // Instanz wieder freigeben MyRechteck.Free; end; end. |
AW: OOP-Delphi-Anfänger
Zitat:
Nun zum Nächsten - Instanzierung. Eine Klasse beschreibt etwas (~Designplan). Die Instanz selbst ist die "Umsetzung" (sozusagen). Du musst dir vorher nun eine Instanz vom Typ "TRechteck" erstellen. Definier einfach mal provisorisch global (dh. unter Form1: TForm1) MeinRechteck: TRechteck; Nun muss Speicher für die Instanz alloziert werden -> MeinRechteck := TRechteck.Create; das könntest du z.B. ins OnCreate() Ereignis der Form packen! Der Speicher muss natürlich wieder freigegeben werden, daher auch ein MeinRechteck.free aufrufen - am besten im OnDestroy() Ereignis der Form! So, wenn du das dann hast, musst du nur noch mehr die Zeichenmethode der Klasse durch die Instanz aufrufen -> MeinRechteck.Zeichne(Canvas). Übrigens, Canvas ist eine "Variable" der übergeordnetten Klasse (die TForm1 wäre). Mein Vorschlag - hau das ins OnPaint() Ereignis der Form! Sollte anschließend klappen EDIT: Ach zu dem hier noch etwas: Zitat:
Bevor zu zeichnest, musst du bei der Instanz die Werte für XPos, YPos, ... usw richtig setzen! MeinRechteck.XPos := 100; (zb) (und kein "TRechteck.Zeichne", sondern "MeinRechteck.Zeichne"...) |
AW: OOP-Delphi-Anfänger
Vielen Dank für diese Richtungszuweisung :D
Das geht leider nciht so schnell , ich muss mich erstmal mit der OOP anfreunden :D nicht so einfach das Ganze.
Delphi-Quellcode:
Das mit der Paintbox musste ich ändern , denn der hatte das als uncleared gesetzt.
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class( TForm ) // Im Object-Inspector das Ereignis "OnClick" von "btn_Rechteck" mit "btn_RechteckClick" verbinden procedure btn_RechteckClick( Sender : TObject ); private { Private-Deklarationen } public { Public-Deklarationen } end; TRechteck = class hoehe, breite : Integer; PaintBox1: TPaintBox; // Zeichnet relativ zur aktuellen Stiftposition auf "aCanvas" das Rechteck procedure ZeichneAuf( aCanvas : TCanvas ); end; var Form1 : TForm1; PaintBox1: TPaintBox; implementation {$R *.dfm} { TRechteck } procedure TRechteck.ZeichneAuf( aCanvas : TCanvas ); begin // Jedes Zeichnen mit "LineTo" setzt auch die Stiftposition "PenPos" aCanvas.LineTo( aCanvas.PenPos.X + Self.breite, aCanvas.PenPos.y ); // Linie nach rechts // Das verstehe ich nicht: Was macht diese Zeile hier genau ? Warum nach rechts ? aCanvas.LineTo( aCanvas.PenPos.X, aCanvas.PenPos.y + Self.hoehe ); // Linie nach unten aCanvas.LineTo( aCanvas.PenPos.X - Self.breite, aCanvas.PenPos.y ); // Linie nach links aCanvas.LineTo( aCanvas.PenPos.X, aCanvas.PenPos.y - Self.hoehe ); // Linie nach oben end; { TForm1 } procedure TForm1.btn_RechteckClick( Sender : TObject ); var MyRechteck : TRechteck; begin // Diese Methode soll aufgerufen werden, wenn der Button "btn_Rechteck" gedrückt wurde // Die Stiftposition setzen PaintBox1.Canvas.MoveTo( 50, 40 ); // Instanz erzeugen MyRechteck := TRechteck.Create; // Werte zuweisen MyRechteck.hoehe := 50; MyRechteck.breite := 80; // Zeichen-Methode aufrufen MyRechteck.ZeichneAuf( PaintBox1.Canvas ); // Instanz wieder freigeben MyRechteck.Free; end; end. Bekomme trotzdem diese Fehlermeldung , aber nicht unter dem Quelltext , sondern als kleines Fensterchen: ""Exception EClassNotFound in Module Project1.exe at 00216D Class TPaintBox not found"" |
AW: OOP-Delphi-Anfänger
Bleib lieber bei deiner Version, es ist nämlich nur noch mehr ein Schritt bis zum sehbaren Ergebnis!
|
AW: OOP-Delphi-Anfänger
Liste der Anhänge anzeigen (Anzahl: 1)
Klatsche dir auf das Formular einen Button und vergib den Namen "btn_Rechteck"
Klatsche dir auf das Formular eine PaintBox und vergib den Namen "The_PaintBox" Mach einen Doppelklick auf den Button. Jetzt kopiere meinen QuellText dort hinein. |
AW: OOP-Delphi-Anfänger
Okay , aber ich will ja verstehen was da vor sich geht :
aCanvas.LineTo( aCanvas.PenPos.X + Self.breite, aCanvas.PenPos.y ); // Linie nach rechts Warum geht diese Linie nach rechts , woran erkenne ich das ? |
AW: OOP-Delphi-Anfänger
Wenn man zum aktuellen horizontalem Wert (x) etwas hinzufügt und zum aktuellen vertikalen Wert (y) nichts hinzufügt, wo kommt man dann wohl raus?
|
AW: OOP-Delphi-Anfänger
Okay , das wär abgehakt , dankeschön.
Was hat self.breite für eine Funktion ? |
AW: OOP-Delphi-Anfänger
Delphi-Quellcode:
ist immer die Referenz auf die Instanz der Methode.
Self
Hier also die Referenz auf die aktuelle TRechteck-Instanz. Self ist zwar nicht zwingend erforderlich, kann aber den Code verständlicher machen, bzw. einem fehlerhaften Bezug vorbeugen :) Hier in diesem Fall ist es egal, ob man es hinschreibt oder nicht
Delphi-Quellcode:
procedure TRechteck.ZeichneAuf( aCanvas : TCanvas );
begin aCanvas.LineTo( aCanvas.PenPos.X + {Self.}breite, aCanvas.PenPos.y ); end; |
AW: OOP-Delphi-Anfänger
Alles klar vielen Dank.
Damit "erkennt" man sozusagen auf was sich diese "breite" bezieht , habe ich das richtig interpretiert ? |
AW: OOP-Delphi-Anfänger
Zitat:
Achtung, jetzt nicht verwirren lassen ... das ist jetzt reine Spielerei, könnte einem aber mal unter die Füße kommen:
Delphi-Quellcode:
procedure TRechteck.ZeichneAuf( aCanvas : TCanvas );
// lokale Variablen var hoehe, breite : integer; begin // weil diese Methode gleichlautende lokale Variablen wie die Klasse Felder hat, // muss man in dieser Methode für den Zugriff auf die Klassen-Felder das Self voransetzen! // den lokalen Variablen wird ein Wert zugewiesen hohe := Self.hoehe - 10; breite := Self.breite - 10; end; |
AW: OOP-Delphi-Anfänger
Alles klar vielen Dank.
Kurze Frage noch : Was passiert wenn man mehrere Subklassen (Vererbung) hat , mer Objekte mehr Instanzen , kann man self dann immernoch benutzen ? |
AW: OOP-Delphi-Anfänger
Zitat:
denn die Klassenbeschreibung wird vererbt ... und keine Instanzen doppelt und dreifach erzeugt.
Delphi-Quellcode:
ist prinzipiell das gleiche wie
// Vererbung
TFoo = class FooData : integer; end; TFooSon = class( TFoo ) FooSonData : integer; end;
Delphi-Quellcode:
// Ohne Vererbung
TFooSon = class FooData : integer; FooSonData : integer; end; |
AW: OOP-Delphi-Anfänger
Alles klar vielen Dank , sehr nützliche Infos.
Danke an alle , die mir die Richtung gezeigt haben. Schönen Abend noch. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:27 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz