| Autor |
Beitrag |
Dibelius
      
Beiträge: 64
|
Verfasst: Sa 27.09.08 18:15
Hallo,
ich arbeite an einem kleinen Editor, der String Files erstellen, sowie modifizieren soll.
Die String Files sind nach folgendem Schema aufgebaut:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| PREFIX_1:Label1 "String Value 1" End
PREFIX_1:Label2 "String Value 2 /n auch mit Zeilenumbrüchen" End
PREFIX_2:Label1 "String Value 1 /n auch mit Zeilenumbrüchen" End |
usw.
Nun wäre meine erste Frage, wie ich diese einzelnen Angaben aus der String File in das Programm laden kann.
Dabei sollen
PREFIX_1
--Label1
--Label2
PREFIX_2
--Label1
wie aus obigem Beispiel angegeben so im TreeView erscheinen. Der Doppelpunkt ist sozusagen der Delimeter. Auf erster Ebene sollen alle möglichen Prefixes auf ein einziges reduziert werden als Knoten im TreeView. Darunter als Childs die jeweiligen Labels. Die Labels können ebenfalls gleich sein, aber natürlich variiert dann der Präfix immer.
Man kann sich das von der Struktur her ähnlich wie eine INI-File vorstellen, was die Bereiche, Labels und Values angeht.
Wenn ich später auf ein Label klicke, soll der dazugehörige String innerhalb der Anführungszeichen in einem Memo ausgegeben werden... aber das krieg ich (hoffentlich) noch selbst irgendwie hin.
Mein größtes Problem hab ich im Moment mit dem TreeView. Ich hab absolut keine Idee, wie ich das Laden der Daten bewerkstelligen soll.
Ich hoffe, dass mir hier jemand nen Tipp geben kann
... und bitte verständlich, ich bin Delphi-Noob ^^
|
|
Lannes
      
Beiträge: 2352
Erhaltene Danke: 4
Win XP, 95, 3.11, IE6
D3 Prof, D4 Standard, D2005 PE, TurboDelphi, Lazarus, D2010
|
Verfasst: Sa 27.09.08 22:53
Hallo,
in der Delphi-Library findest Du eine passende Funktion:
Node-Pfad (String) im TreeView einfügen oder TreeView mit einer Liste von Node-Pfaden füllen
Den Doppelpunkt kannst Du als Delimeter angeben.
Delphi-Quelltext 1:
| AddTreeNodePathDupIgnore(TreeView1,'PREFIX_1:Label1',':',ReNode,AddSort); |
Der Parameter ReNode gibt Dir einen Zeiger auf den angelegten Node zurück, an den kannst Du dann in der Eigenschaft Data Deine Texte anhängen.
_________________ MfG Lannes
(Nichts ist nicht Nichts) and ('' <> nil ) and (Pointer('') = nil ) and (@('') <> nil )
|
|
Dibelius 
      
Beiträge: 64
|
Verfasst: Di 07.04.09 12:17
Sorry, dass ich hierzu gar nichts mehr geschrieben hab, aber ich hatte das Projekt erstmal auf Eis gelegt, aber nun bin ich wieder voll dabei.
Vielen Dank erstmal für diese überaus praktische Funktion. Ich werd sie wohl noch etwas umbauen müssen, damit im Falle von mehr als einem Delimiter immer nur der erste berücksichtigt wird. Ich brauche ja wie gesagt nur 2 Ebenen im TreeView. Aber das bekomm ich hoffentlich noch selbst hin
Nun hab ich erstmal ein anderes Problem mit den Pointern. Ich muss gestehen, dass ich vorher noch nie mit Pointern gearbeitet hab und ich da nur mühsam durchsteige, wie es funktioniert. Aus der Delphi-Hilfe hab ich mir erstmal rausgezogen, wie man vorgehen muss, wenn man zu einem TreeView Nodes hinzufügen und einen Pointer auf den jeweiligen Node setzt, der bsp. auf Elemente eines Records zeigt. Das passiert dort natürlich ohne deine praktische Funktion. Erstmal hab ich gerade ein Problem mit dem Pointer und der Wertzuweisung, also hier erstmal mein Code soweit:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| type PStrData = ^TStrData; TStrData = record Lbl: string; Value: string; end;
var aStrData: array of ^TStrData; |
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| procedure TForm1.ReadStrFile(const FileName: string); var F: TextFile; Str: string; Index: integer; StrData: TStrData; StrDataPtr: PStrData; ReNode: TTreeNode; begin Index:=0; New(StrDataPtr); ReNode := TTreeNode.Create(Tree.Items); AssignFile(F, FileName); Reset(F); while not EOF(F) do begin Readln(F, Str); if (Trim(Str)<>'') and (Pos('//',Str)<>1) then begin SetLength(aStrData, Length(aStrData)+1); if ((Trim(Str) <> 'End') or (Trim(Str) <> 'END')) and (Pos('"', Str) <> 0) then begin with aStrData[Index]^ do begin Value := Str; Value := Trim(Value); Value := Copy( Value, 2, Length(Value)-2 ); Value := StringReplace(Value,'\n',#13#10, [rfReplaceAll]); Value := StringReplace(Value,'\t','_', [rfReplaceAll]); Value := StringReplace(Value,'\\','\', [rfReplaceAll]); Value := StringReplace(Value,'\"','"', [rfReplaceAll]); end; end else begin if (Trim(Str) <> 'End') and (Trim(Str) <> 'END') and (Length(Str) <> 0) then begin with aStrData[Index]^ do begin Lbl := Str; Lbl := Trim(Lbl); Lbl := StringReplace(Lbl,' ','',[rfReplaceAll]); if Pos(':', Lbl) = 0 then begin eName.Text := UpperCase(Lbl); AddTreeNodePathDupIgnore(Tree, Lbl, ':', ReNode, AddSort); end else begin Prefix := UpperCase( Copy(Lbl,1,Pos(':',Lbl)-1) ); Name := Copy(Lbl,Pos(':',Lbl)+1,Length(Lbl)); cmbPrefix.Text := Prefix; eName.Text := Name; AddTreeNodePathDupIgnore(Tree, UpperCase(Lbl), ':', ReNode, AddSort); end; end; end; end; end; end; CloseFile(F); end; |
Erstmal meldet Delphi folgendes:
[Hinweis] Unit1.pas(256): Auf 'StrDataPtr' zugewiesener Wert wird niemals benutzt --> 1. markierte Zeile
Das Programm lässt sich zwar compilen, aber ich hab die ReadStrFile-Prozedur testweise im FormCreate drin und bekomme beim Start sofort einen Exception Error - um hier Missverständnisse auszuschließen: die Textdatei selbst wird gefunden. Delphi springt dann in die FormCreate, wo ich ich die ReadStrFile-Prozedur aufrufe.
| Zitat: | | Der Parameter ReNode gibt Dir einen Zeiger auf den angelegten Node zurück, an den kannst Du dann in der Eigenschaft Data Deine Texte anhängen. |
Wie gesagt, mit Pointern hab ich es noch nicht so sehr... aber wenn ich es richtig verstehe, dann in der Art?
Delphi-Quelltext 1: 2: 3: 4:
| var ReNode: TTreeNode; begin AddTreeNodePathDupIgnore(...); ReNode.Data := 'Mein Text'; |
/edit: gefundene Fehler schonmal ausgebessert
/edit2: per Einzelanweisung hab ich rausbekommen, an welcher Stelle genau es zum Fehler kommt:
Delphi-Quelltext 1: 2: 3:
| with aStrData[Index]^ do begin Lbl := Str; |
Ich möchte doch eigentlich damit im ersten Schleifendurchlauf aStrData[0]^.Lbl den Wert von Str zuweisen... oder wo ist da jetzt mein Denkfehler?
|
|
JayEff
      
Beiträge: 2971
Windows Vista Ultimate
D7 Enterprise
|
Verfasst: Di 07.04.09 14:27
Dibelius hat folgendes geschrieben : | | Ich muss gestehen, dass ich vorher noch nie mit Pointern gearbeitet hab |
Doch, hast du, jede Klasse und jede Komponente ist intern ein Zeiger
Ich verstehe nicht ganz, was dein StrData soll, wieso muss das ein Pointer sein?
Auf jeden Fall hast du deinem Pointer nirgens Speicher reserviert. Lass doch den Pointerkram weg, so wie ich das sehe, würde dir ein ganz normales Array of String bzw Array of array[0 .. 1] of String reichen?
_________________ >+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.
|
|
Dibelius 
      
Beiträge: 64
|
Verfasst: Di 07.04.09 14:57
Alles, was ich bisher so aus der Delphi-Hilfe oder hier aus anderem Forenposts zu dem Thema zusammengesucht habe, nutzt als Grundlage Pointer, die auf die Daten(blöcke) hinter dem jeweiligen Baumknoten zeigen - so hab ich das zumindest verstanden.
Nunja, ich hab meine alte Prozedur erstmal über den Haufen geworfen und hab quasi nochmal bei 0 angefangen... diesmal ohne dieses komische aStrData Array, bei dem ich selbst nicht so ganz wusste, was ich mit dem überhaupt wollte ^^
Jetzt werden die Daten zwar erstmal roh aus der Datei ausgelesen, aber das spielt erstmal keine Rolle. Das Thema "Baumknoten mit entspr. Daten verknüpfen" ist mir erstmal wichtiger.
Hier mein aufbereiteter Code:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| type PStrData = ^TStrData; TStrData = record Lbl: string; Value: string; end;
var StrData: ^TStrData; |
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40:
| procedure TForm1.ReadStrFile(const FileName: string); var F: TextFile; Str: string; begin AssignFile(F, FileName); Reset(F);
Tree.Items.Clear; Tree.Items.BeginUpdate; while not EOF(F) do begin Readln(F, Str); if Length(Trim(Str)) = 3 then Str := StringReplace(Str,'end','End',[rfIgnoreCase]); if (Trim(Str) <> '') and (Pos('//',Str) <> 1) and (Trim(Str) <> 'End') then begin if (Pos('"', Str) = 0) then begin New(StrData); StrData^.Lbl := Str; Tree.Items.AddObject(nil, Str, StrData); eName.Text := PStrData(Tree.Items[0].Data)^.Lbl; end else begin New(StrData); StrData^.Value := Str; end end; end; Tree.Items.EndUpdate; Tree.FullExpand;
CloseFile(F); end; |
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| procedure TForm1.TreeClick(Sender: TObject); begin Edit1.Text := PStrData(Tree.Selected.Data)^.Lbl; Memo1.Clear; Memo1.Lines.Add( PStrData(Tree.Selected.Data)^.Value ); end; |
Was mir aktuell noch Probleme macht ist folgendes:
erste Zeile, die den Suchkriterien entspricht wird ausgelesen --> Label wird gefunden
die nächste Zeile wird ausgelesen --> Value wird gefunden, aber wohin damit? Im Baum will ich es ja dem Knoten vom zugehörigen Label zuordnen.
|
|
JayEff
      
Beiträge: 2971
Windows Vista Ultimate
D7 Enterprise
|
Verfasst: Di 07.04.09 15:58
Dibelius hat folgendes geschrieben : | | Alles, was ich bisher so aus der Delphi-Hilfe oder hier aus anderem Forenposts zu dem Thema zusammengesucht habe, nutzt als Grundlage Pointer, die auf die Daten(blöcke) hinter dem jeweiligen Baumknoten zeigen |
Ahh, tut mir leid, ich hab mich mit dem TreeView (insbesondere mit der Data-Eigenschaft) noch nicht auseinandergesetzt, vermtl. hast du damit schon ganz recht
Ich schau mir das mal an, dir geht's im speziellen um das hier: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| if (Trim(Str) <> '') and (Pos('//',Str) <> 1) and (Trim(Str) <> 'End') then begin if (Pos('"', Str) = 0) then begin New(StrData); StrData^.Lbl := Str; Tree.Items.AddObject(nil, Str, StrData); eName.Text := PStrData(Tree.Items[0].Data)^.Lbl; end else begin New(StrData); StrData^.Value := Str; end | da das so nicht klappt, oder? Ich kenn mich leider auch nicht perfekt damit aus, aber versuch mal, der Data-Eigenschaft sowas zuzuweisen (einfach mal testen ob das hinhaut):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| type TStrData = record Lbl: string; Value: string; end;
var str: TStrData; begin str.Lbl := 'asd'; str.Value := 'asd2'; Tree.Items[0].Data := @str; |
Wenn das geht, müsstest du damit weiterkommen, wenn nicht,  , sorry.
_________________ >+++[>+++[>++++++++<-]<-]<++++[>++++[>>>+++++++<<<-]<-]<<++
[>++[>++[>>++++<<-]<-]<-]>>>>>++++++++++++++++++.+++++++.>++.-.<<.>>--.<+++++..<+.
|
|
Dibelius 
      
Beiträge: 64
|
Verfasst: Di 07.04.09 17:19
ah, ich glaub, ich hab's nun fast:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| if (Trim(Str) <> '') and (Pos('//',Str) <> 1) and (Trim(Str) <> 'End') then begin if (Pos('"', Str) = 0) then begin Lbl := Trim( StringReplace(Str,' ','',[rfReplaceAll]) ); AddTreeNodePathDupIgnore(Tree, Lbl, ':', ReNode, AddSort); end else begin New(StrData); StrData^.Lbl := Lbl; StrData^.Value := ConvertStrValueToText(Str); if not AddTreeNodePathDupIgnore(Tree, Lbl, ':', ReNode, AddSort) then begin ReNode.Selected := true; ReNode.Data := StrData; end; end end; |
Jetzt bekomm ich beim Selektieren des entsprechenden Knotens die korrekten Werte zurück
Nur eine Sache noch: ich bekomme einen Zugriffsfehler, wenn ich einen Knoten auf erster Ebene anwählen möchte. Kann ich das irgendwie verhindern?
Ich dachte irgendwie an sowas...
Delphi-Quelltext 1: 2: 3: 4: 5:
| procedure TForm1.TreeClick(Sender: TObject); begin if (Tree.Selected <> nil) and (not Tree.Selected.Parent.Selected) then ... end; |
|
|
Chemiker
      
Beiträge: 194
Erhaltene Danke: 14
XP, Vista 32 Bit, Vista 64 Bit, Win 7 64 Bit, Win 10, Win 11
BDS 2006, RAD Studio 2009+C++, Delphi 13, VS 2010 Prof.
|
Verfasst: Di 07.04.09 19:45
Hallo Dibelius,
Du brauchst aber nicht mit einem Pointer zu arbeiten, viel einfacher ist das mit einem Object zu realisieren.
Mal ein Beispiel:
Das Object:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66:
| unit uBeanstandungsClass;
interface
uses Classes;
type TBeanstandung = Class(TObject) private Fnummer:String; FText:String; procedure Getnummer(const Value: String); procedure GetText(const Value: String); function SetNummer: String; function SetText: String; public constructor Create(); destructor Destroy; override;
property Nummer: String Read SetNummer Write Getnummer; property Text: String Read SetText Write GetText; End;
implementation
constructor TBeanstandung.Create; begin inherited Create; end;
destructor TBeanstandung.Destroy; begin
inherited; end;
procedure TBeanstandung.Getnummer(const Value: String); begin If Value<> Fnummer then begin Fnummer:= Value; end; end;
procedure TBeanstandung.GetText(const Value: String); begin If Value<> FText then begin FText:= Value; end; end;
function TBeanstandung.SetNummer: String; begin Result:= FNummer; end;
function TBeanstandung.SetText: String; begin Result:= FText; end;
end. |
Und nun das Programm:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107:
| unit uTreeViewDemoMitObj;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, uBeanstandungsClass;
type TForm3 = class(TForm) TreeView1: TTreeView; Label1: TLabel; Label2: TLabel; Edit1: TEdit; Edit2: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); procedure TreeView1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private public BeaObj1: TBeanstandung;
function TVNodeInsert (Nod: TTreeNodes; var NeueNode: TTreeNode; const strElternKnoten: String; const strKnoten: String; Obj: TObject): boolean; end;
var Form3: TForm3;
implementation
{$R *.dfm}
function TForm3.TVNodeInsert (Nod: TTreeNodes; var NeueNode: TTreeNode; const strElternKnoten: String; const strKnoten: String; Obj: TObject): boolean; var TempNode: TTreeNode; begin result:= FALSE; TempNode:= Nod.GetFirstNode; while TempNode <> NIL do begin If (CompareText(TempNode.Text, StrElternKnoten)=0) then begin NeueNode:= Nod.AddChildObject(TempNode,StrKnoten,Obj); Result:= TRUE; end; TempNode:= TempNode.getNext; end; end;
procedure TForm3.Button1Click(Sender: TObject); var Node: TTreeNode; begin BeaObj1:= TBeanstandung.Create; BeaObj1.Nummer:= Edit1.Text; BeaObj1.Text:= Edit2.Text; TVNodeInsert (TreeView1.Items, Node,'Root Eintrag', BeaObj1.Nummer, BeaObj1); end;
procedure TForm3.FormCreate(Sender: TObject); begin If TreeView1.Items.GetFirstNode=NIL then begin TreeView1.Items.AddFirst (Nil,'Root Eintrag'); end; end;
procedure TForm3.FormDestroy(Sender: TObject); var i: Integer; begin for I := 0 to TreeView1.Items.Count - 1 do begin BeaObj1:= TreeView1.Items[i].Data; if BeaObj1<>NIL then begin FreeAndNil(BeaObj1); end; end; end;
procedure TForm3.TreeView1Click(Sender: TObject); begin if TreeView1.Selected <> NIL then begin Label1.Caption:= ''; Label2.Caption:= ''; If TreeView1.Selected.Data <> NIL then begin Label1.Caption:= TBeanstandung(TreeView1.Selected.Data).Nummer; Label2.Caption:= TBeanstandung(TreeView1.Selected.Data).Text; end; end; end;
end. |
Bis bald Chemiker
|
|
Dibelius 
      
Beiträge: 64
|
Verfasst: Di 07.04.09 23:18
Vielen Dank, Chemiker. Aber es läuft ja nun soweit auch mit den Pointern. Das einzige Problem ist wie gehabt noch, dass ich eine Zugriffsverletzung bekomme, wenn ich ein Item auf 1. Ebene anwähle.
Sollte es jetzt gar nicht anders machbar sein, werde ich deine Variante nochmal in Betracht ziehen, aber mir ist atm nicht danach, noch einmal bei 0 anzufangen.
|
|
Lannes
      
Beiträge: 2352
Erhaltene Danke: 4
Win XP, 95, 3.11, IE6
D3 Prof, D4 Standard, D2005 PE, TurboDelphi, Lazarus, D2010
|
Verfasst: Di 07.04.09 23:21
_________________ MfG Lannes
(Nichts ist nicht Nichts) and ('' <> nil ) and (Pointer('') = nil ) and (@('') <> nil )
|
|
Chemiker
      
Beiträge: 194
Erhaltene Danke: 14
XP, Vista 32 Bit, Vista 64 Bit, Win 7 64 Bit, Win 10, Win 11
BDS 2006, RAD Studio 2009+C++, Delphi 13, VS 2010 Prof.
|
Verfasst: Di 07.04.09 23:54
Hallo Dibelius,
Delphi-Quelltext 1: 2: 3:
| procedure TForm3.TreeView1Click(Sender: TObject); begin if TreeView1.Selected <> NIL then |
damit wird überhaupt ein Knoten ausgewählt. Ist der Wert <> NIL so ist ein Knoten angewählt.
Mit:
Delphi-Quelltext 1:
| If TreeView1.Selected.Data <> NIL then |
Kann man feststellen ob der Knoten einen Data Wert besitzt, diese Prüfung sollte man beim Anhängen von Daten eigentlich immer durchführen, damit kein Fehler auftritt.
Man kann natürlich die Auswahl weiter eingrenzen wie Lannes es vorgeschlagen hat.
Bis bald Chemiker
|
|
|