Autor Beitrag
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1448

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Mi 02.01.13 23:30 
Hallo,
ich habe die Absicht für ein größeres Objekt mit einer Vielzahl von Formularen eine einheitliche Prozedur zum Speichern von Paintboxgrafiken zu schreiben. Solange ich mich auf Standardtypen (bmp, jpg, gif, png) beschränke geht alles gut.

Nun möchte ich aber auch die Grafik als Vektorgrafik/Metafile speichern. Die Zeichenroutinen befinden sich in den jeweiligen Units und werden durch Übergabe des MetafileCanvas aufgerufen.
Mein Problem ist nun, dass ich der Speichermethode

ausblenden volle Höhe Delphi-Quelltext
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 bildspeichernpb(pb:tpaintbox; ????? zeichenroutine(canvas:tmetafilecanvas):procedure ??????);
var datei:tfilestream;
     speicherdialog:tsavedialog;
     Bitmap: TBitmap;
     myrect,birect:trect;
     mymetafile:tmetafile;
     metacanvas:tmetafilecanvas;
begin
    //Bitmap erzeugen und Inhalt der Paintbox kopieren
    Bitmap := TBitmap.Create;
    Bitmap.Width := pb.Width;
    Bitmap.Height := pb.Height;
    birect.left:=0;
    birect.right:=pb.width;
    birect.top:=0;
    birect.bottom:=pb.height;
    myrect:=birect;
    bitmap.canvas.CopyRect(biRect,pb.Canvas, MyRect);

    speicherdialog:=TSaveDialog.Create(nil);
    With speicherdialog do
    begin
      //Speicherdialog initialisieren
      Title:='Abbildung speichern';
      Filter:='Abbildung (*.bmp)|*.bmp|Abbildung,16 Farben (*.bmp)|*.bmp|Abbildung (*.jpg)|*.jpg|GIF-Abbildung (*.gif)|*.GIF|PNG-Grafik (*.png)|*.png|Vektorgrafik (*.wmf)|*.wmf';
      Filename:='';
      Filterindex:=4;
      Defaultext:='gif';
      InitialDir:='c:\eigene dateien';
      Options:=[ofOverwritePrompt,ofHideReadOnly,ofEnableSizing];

      If Execute Then
      begin
        if filterindex=2 then Bitmap.PixelFormat := pf4bit;
        if filterindex<3 then
        begin
          datei:=tfilestream.create(filename,fmcreate);
          bitmap.savetostream(datei);
          datei.free;
        end;
        if filterindex=3 then savejpg(bitmap,filename,100,false);
        if filterindex=4 then gifsave(bitmap,filename);
        if filterindex=5 then pngsave(bitmap,filename);

        if filterindex=6 then //Vektorgrafik-Datei 
        begin
           MyMetafile := TMetafile.Create;
           mymetafile.enhanced:=true;
           mymetafile.height:=pb.height;
           mymetafile.width:=pb.width;
           metacanvas:=TMetafileCanvas.Create(MyMetafile, 0);
           zeichenroutine (metacanvas) // !!!!!!!!!!! die Zeichenroutine der aufrufenden Paintbox
           metacanvas.Free;
           datei:=tfilestream.create(filename,fmcreate);
           mymetafile.savetostream(datei);
           datei.free;
           mymetafile.free;
        end;
     end;

    speicherdialog.free;
    Bitmap.Free;
end;

irgendwie die Methode zeichenroutine(canvas:tmetafilecanvas) übergeben muss.
In der EE-Suche habe ich bisher leider nichts gefunden. Vielleicht habe ich auch nur die falschen Schlagwörter gesucht.

Meine Frage ist nun, gibt es eine Möglichkeit, irgendwie die Methode beim Aufrufen der Prozedur bildspeichernpb zu übergeben.
Ein Eintrag der ganzen Units in die uses-Liste der Unit mit der Speicherprozedur geht leider nicht, da es dann zu Überkreuzungen kommt. Ich habe auch schon als Alternative erwogen, mymetafile immer vor dem Auruf der Speichermethode zu erzeugen und als Parameter zu übergeben. Da die Grafiken teilweise aufwendig sind, wäre das aber nur eine Notlösung.

Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 03.01.13 00:08 
Das sollte über einen neuen Typ gehen, wenn ich das richtig verstehe:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
type TMyProc = procedure(aString: String);
//...
procedure Proc1(aString: String);
begin
    ShowMessage(aString);
end;

// Funktion dieses Typs aufrufen
procedure runProc(aProc: TMyProc; s: String);
begin
    aProc(s);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
    runProc(Proc1, 'Procedure 1 ausgeführt');
end;

_________________
We are, we were and will not be.

Für diesen Beitrag haben gedankt: Mathematiker
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 03.01.13 00:25 
Noch erwähnenswert ist die Unterform mit "of object".

Die braucht man, wenn man nicht eine freifliegende Funktion, sondern eine Methode übergeben möchte:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
type TMyProc = procedure(aString: Stringof object;

type
  TIrgendwas = class
    procedure Proc1(aString: String);
  end;

//...
procedure TIrgendwas.Proc1(aString: String);
begin
    ShowMessage(aString);
end;

Der Rest (also Aufruf und Verwendung als Parameter/Variable) ist dann aber identisch.

Eine Deklaration, die sagen würde "mir egal ob Objekt oder nicht" gibt es leider nicht. Das hat damit zu tun, dass der generierte Code grundlegend anders aussieht. Die Entscheidung muss man dem Compiler also von Hand abnehmen ;)

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."

Für diesen Beitrag haben gedankt: Mathematiker
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 03.01.13 09:33 
Der Vollständigkeit halber, bei neueren Delphiversionen (D2009+) geht es noch einfacher mit anonymen Methoden:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
  TExample = reference to procedure(const AValue: string);

procedure DoSomething(const CallExampleRef: TExample);
begin
  CallExampleRef('blub');
end;

// Aufruf:
DoSomething(
  procedure(const AValue: string)
  begin
    ShowMessage(AValue);
  end
  );
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: Do 03.01.13 10:26 
Warum nicht ein fertiges TGraphic-Object übergeben?
Als konkrete Klasse könnte dann z.B. auch ein TMetafile für Vektorgrafiken verwendet werden.
bummi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1248
Erhaltene Danke: 187

XP - Server 2008R2
D2 - Delphi XE
BeitragVerfasst: Do 03.01.13 12:21 
Sorry falls ich etwas off topic sein sollte.
Ich löse diese Anforderungen, wenn man es vorher nicht gleich mitberücksichtigt hat, gerne (etwas unüblich) durch das Unterschieben eines Canvas in der OnPaintroutine.

Pseudocode:
If assigned(FOutputCanvas) then c := FOutputCanvas else c := Canvas;

das ganze Painting findet dann auf c statt und ein Ausdruck oder Export beschränkt sich darauf:

FOutputCanvas zu setzen (Metafilecanvas/PrinterCanvas/was auch immer)
Invalidate aufzurufen
FOutputCanvas wieder zurückzusetzen

das Canvas könnte auch manipuliert sein bezüglich Offset/Zoom/Rotation.

_________________
Das Problem liegt üblicherweise zwischen den Ohren H₂♂
DRY DRY KISS
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1448

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Do 03.01.13 16:03 
Hallo,
Danke allen für die guten Tipps.
Ich habe Gausis und Martoks Hinweise getestet und es funktioniert absolut perfekt.
Damit kann ich jetzt auch noch die Übergabe der Paintbox entfernen und nur noch deren Abmessungen weitergeben, d.h.
ausblenden volle Höhe Delphi-Quelltext
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:
type
     tmetaproc = procedure(canvas:TCanvas) of object;
...
procedure bildspeichernmeta(breite,hoehe:integer;aproc:tmetaproc);
var Bitmap: TBitmap;
    f:tfilestream;
    sd:tsavedialog;
    mymetafile:tmetafile;
    canvas:tmetafilecanvas;
begin
    sd:=TSaveDialog.Create(nil);
    With sd do
    begin
      Title:='Abbildung speichern';
      Filter:='Abbildung (*.bmp)|*.bmp|Abbildung,16 Farben (*.bmp)|*.bmp|Abbildung (*.jpg)|*.jpg|GIF-Abbildung (*.gif)|*.GIF|PNG-Grafik (*.png)|*.png|Vektorgrafik (*.wmf)|*.wmf';
      Filename:='';
      Filterindex:=4;
      Defaultext:='gif';
      InitialDir:='c:\eigene dateien';
      Options:=[ofOverwritePrompt,ofHideReadOnly,ofEnableSizing];
      If Execute Then
      begin
        if filterindex<>6 then
        begin
          Bitmap := TBitmap.Create;
          Bitmap.Width := breite;
          Bitmap.Height := hoehe;
          aproc(bitmap.canvas);
          if filterindex=2 then Bitmap.PixelFormat := pf4bit;
          if filterindex<3 then
          begin
            f:=tfilestream.create(filename,fmcreate);
            bitmap.savetostream(f);
            f.free;
          end;
          if filterindex=3 then savejpg(bitmap,filename,100,false);
          if filterindex=4 then gifsave(bitmap,filename);
          if filterindex=5 then pngsave(bitmap,filename);
          bitmap.free;
        end
        else
        begin
          MyMetafile := TMetafile.Create;
          mymetafile.enhanced:=true;
          mymetafile.height:=hoehe;
          mymetafile.width:=breite;
          canvas:=TMetafileCanvas.Create(MyMetafile, 0);
          aproc(canvas);
          canvas.Free;
          f:=tfilestream.create(filename,fmcreate);
          mymetafile.savetostream(f);
          f.free;
          mymetafile.free;
        end;
      end;
    end;
    sd.free;
end;

Das ist genau das, was ich gesucht habe.
Damit ergeben sich auch weitere Möglichkeiten. Zum Beispiel kann ich das Kopieren in die Zwischenablage und das Drucken der Grafiken auf ähnliche Art jeweils in einer Routine zusammenfassen.

Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Fr 04.01.13 13:06 
Damit das richtig rund wird, sollten aber zumindest noch Ressourcen-Schutzblöcke verwendet werden. Die If-Abfragen könnte man durch case ersetzen. Und was machst Du, wenn als Methodenzeiger nil übergeben wurde?