Autor Beitrag
aim65
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 14:43 
Hallo,

nach langer Zeit (und ergebnisloser Suche) suche ich eine Lösung für folgendes Problem:

In einer PaintBox wird mit einer For/To-Schleife eine Kurve aus 500 einzelnen Messpunkten geschrieben. Der Vorgang wird mit einem Start-Button gestartet und wiederholt sich solange bis er mit einem Stop-Button beendet wird. Ich möchte, dass beim Klick auf dem Stop-Button sich dessen Farbe (SpeedButton) sofort verändert, während die jeweilige Kurve bis zum Ende geschrieben wird.

Geht natürlich mit Application.ProcessMsg, ABER: hier entstehen neben der Kurve unschöne Geraden (zum Top der PaintBox) im Diagram, die beim Beenden des jeweiligen Durchlaufs wieder verschwinden. Ich krieg' das einfach nicht weg.

Frage: wie kann ich die obige Reaktion hervorrufen ohne Application.ProcMsg verwenden zu müssen?

MfG Horst


Moderiert von user profile iconNarses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am So 28.09.2008 um 16:40
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: So 28.09.08 15:41 
ich würde an deiner stelle nicht an der anzeige des problems rumbasteln sonder das problem beheben
es gibt mehrere möglichkeiten:
1) Alles in einen Thread auslagern und zuerst auf eine temporäre Bitmap zeichnen
2) Mit Application.ProcessMessages arbeiten und zuerst auf temporäre Bitmap zeichnen
3) Fehler suchen, warum die Linie nicht so ist wie sie ist.
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 16:10 
Hallo Flamefire,

vielen Dank für Deine Antwort.
zu 1a:
habe mit Auslagerung in einen anderen Thread keine Erfahrung, muss da mal näher reingucken.
zu 1b + 2:
mit Bitmap zu zeichnen, habe ich auch schon versucht - änderte nix. (Ich benutze Bitmap + bitblt ohnehin zum flackerfreien Refresh). Ein reines Zeichnen auf eine Bitmap und Darstellung der fertigen Kurve löst natürlich das Problem, aber ich will ja gerade einen kontinuierlichen Aufbau der Kurve wie bei einem analogen Gerät.
zu 3:
irgendein Tip, wo ich da suchen soll? Das Programm läuft sonst unter allen Betriebsbedingungen astrein.

Ich hatte die Hoffnung, dass man ein solches Ereignis noch anders abfangen kann.

Habe mal einen Screenshot angehängt. Diese Geraden verschwinden, wenn die Schleife durchlaufen ist. Die Kurve selbst wird unverfälscht dargestellt.

MfG Horst
Einloggen, um Attachments anzusehen!
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: So 28.09.08 16:17 
Moin!

Du kannst mal eine Ereigniskette probieren, das könnte auch klappen: :idea:
  • Eigene Fensternachricht mit Handler anlegen, die einen Messpunkt zeichnet (entweder aus den Parametern oder über eine (klassen-)globale Variable die Position merken)
  • Zähler-/Indexvariable auf den ersten Messpunkt setzen, erste Fensternachricht an sich selbst senden
  • Im Handler zuerst prüfen, ob die Abbruchbedinung gesetzt ist; wenn ja, nix mehr tun; wenn nein, Punkt zeichnen und nächste Nachricht an sich selbst schicken
  • Im Stop-Button eine entsprechende Variable setzen, die dann im Handler verfügbar ist
cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: So 28.09.08 16:26 
ok dann wäre wichtig, wie du zeichnest und was du bei stop machst
es wird also der quelltext der dafür zuständig ist gebraucht
sonst kann man nicht mal vermuten, wo der fehler sein könnte, da es zuviele zeichenmethode gibt
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 17:10 
Uih, was habe ich da losgetreten - und ich dachte, dass es da eine einfache Lösung gäbe...
Im Prinzip ist es ja eigentlich eine kosmetische Sache.

@Narses
Ich bin als Gelegenheitsprogrammierer und Noob schon zu lange raus: hast Du vielleicht ein Beispiel für Deine Anregung "Fensternachricht mit Handler anlegen" (oder wo ich dafür ein Tutorial finde)?

@Flamefire
lass mir ein paar Minuten, ich such mal die procs zusammen, die dafür infrage kommen können. Aber vorab: Die Messpunkte werden in einen Array geschrieben und gleichzeitig Punkt für Punkt direkt in die PaintBox via LineTo geschrieben.
Für den Refresh werden dann die Kurven in eine Bitmap geschrieben und mit bitblt transferiert. Spielt aber hier wohl keine Rolle.
Die Stop-Taste hat ganz einfach die Funktion des Rücksetzens der Start-Taste beim kontinuierlichen Wobbeln (Beim Einzel-Durchlauf erfolgt das automatisch). Also nix besonderes.

Erstmal vielen Dank für Eure Mühe.
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 17:49 
So, hier ist der Auszug (hoffe, dass das mit den Tags klappt)

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:
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:
procedure RedrawDiagram;    //This transfers the bitmap to the PaintBox
begin
  BitBlt(Form1.PaintBox1.Canvas.Handle, 0, 0, Dbmp.Width, Dbmp.Height,
                    Dbmp.Canvas.Handle, 0, 0, SrcCopy);
end;

procedure RedrawTrace; //Redraw any measurement trace for Form1.Repaint
var i,tmp: Integer;
begin
  tmp := 0;
  for i := 1 to 501 do   tmp := tmp + Round(MeasLog[i]);
  if logflg and (tmp = 0) then exit;    //suppress display of zero-db line
  if logflg then Dbmp.Canvas.MoveTo(0,Round(MeasLog[1])+1)
            else Dbmp.Canvas.MoveTo(0,Round(MeasLin[1])+1);
  with Dbmp.Canvas do begin
    Pen.Color := clTrace;
    for i := 2 to 501 do
      begin
        if logflg then LineTo(i-1,Round(MeasLog[i])+1)
                  else LineTo(i-1,Round(MeasLin[i])+1);
      end;
  end;
  RedrawDiagram;      //transfer Bitmap to PaintBox
end;

procedure RedrawAll;      //This routine basically replaces the PB-Repaint
begin
  DrawDiagramBox;         //generates the diagram on the bitmap
  RedrawDiagram;          //transfers the bitmap to the PaintBox
  RedrawMarkers;
  RedrawTrace;
  DrawFrame(bsClear);     //cosmetic bugfix V. 2.3.xx
end;

procedure DrawSegment(n,aVal: Integer);    //draws a line between two..
begin                                      //measurement points
    with Form1.PaintBox1.Canvas do begin   //this works also w/o flicker...
    Pen.Color := clTrace;                  //instead "with Dbmp.Canvas do.."
    LineTo(n-1,aVal+1);                    //vertical correction
  end;
end;

//The build-up of the trace with single segments can still be done...
//using the PaintBox routine without causing any flicker
procedure TForm1.StartSweep(StpNo: Integer);   //<<<==== Das ist die Hauptroutine
var fStep, swpFreq: Double;
    Dly : Single;
    n,ADC,tmp,delta: Integer;
begin
  if CTS <1 then begin SpeedButton5Click(self);
    Application.MessageBox(PChar(cmErr),'Fehler', 48); exit; end;
  delta:= StrToInt(Form2.AEdit1.Text); //ßßßßßßßßßßß log span (normally 410)
  Label22.Hide;                        //reset data loss info
  DrawDiagramBox;
  ScrollBar3.Position := 0;            //reset any amplitude offset
  ProgressBar1.Show;
  ProgressBar1.Position := 0;
  ProgressBar1.Step := 5;
  if Sweepflg then DrawShadowTrace     //for multiple sweeps only
              else RedrawDiagram;      //clear the display area
  if mCount >1 then RedrawMarkers;
  ClearMeasArrays;
  swpFreq := StartFreq *1000;
  Dly := mDelay/1000;
  fStep := (StopFreq - StartFreq) * 1000 / StpNo;
  ADC := Send_GetData(StartFreq*1000,BaseRef,25);  //wait 25ms at start
  if COMerr or (CTS <1)then begin
     CloseCOM; exit; end;                  //avoid hang-up in loop
  ADC := CorrectData(ADC,delta,1);         //ßßßßßßßßßßßßßßßßßßßßß
  if logflg then MeasLog[1]:= ADC
            else MeasLin[1]:= ADC;
  tmp := ADC;                              //save ADC reading
  PaintBox1.Canvas.MoveTo(1,ADC+1);        //this works also w/o flicker
  case StpNo of
    500: begin
      for n:= 2 to 501 do
        begin
          tmp := ADC;                        //save previous ADC reading
          swpFreq := swpFreq + fStep;
          ADC := Send_GetData(swpFreq,BaseRef,Dly);
          ADC := CorrectData(ADC,delta,n);   //for 500 points
          if datflg then ADC :=Round(tmp);   //data loss, take last value
          if logflg then MeasLog[n]:= ADC
                    else MeasLin[n]:= ADC;
          DrawSegment(n,ADC);
          if n mod 25 = 0 then ProgressBar1.StepIt;
        end;
    end;
    250: begin
    //....   usw, usw für 250 und 125 Messpunkte
    //....
  end;                   //... end of case
  for n:= 1 to 501 do    //transfer current Measurement to shadow array
    begin
      if logflg then MeasOld[n] := MeasLog[n]
                else MeasOld[n] := MeasLin[n];
    end;
  UpdateMarkers;
  ProgressBar1.Hide;
end;
//-----------------------  End of Sweep procedures  -------------------
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: So 28.09.08 18:43 
das einzige was mir daran auffällt ist das:
ausblenden Delphi-Quelltext
1:
2:
3:
  if logflg and (tmp = 0) then exit;    //suppress display of zero-db line
  if logflg then Dbmp.Canvas.MoveTo(0,Round(MeasLog[1])+1)
            else Dbmp.Canvas.MoveTo(0,Round(MeasLin[1])+1);


was ist logflg?
Wo wird das gesetzt?

Zum testen vl mal als immer true werten:
ausblenden Delphi-Quelltext
1:
if logflg or true then					
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 19:55 
Sorry, ist schon blöd, wenn da Variable/Flags im Text auftauchen, die mit dem Problem eigentlich nichts zu tun haben und daher zur Verwirrung führen.

"logflg" wird unter "Bertiebsart / Log" -RadioButton (siehe Screenshot) gesetzt. Es definiert, ob die Rohdaten vom Analog-Digitalwandler ("ADC") des Messgerätes vom logarithmischen oder Lineardetektor stammen. Sie werden damit dem entsprechenden Array ("MeasLog[]" bzw. "MeasLin[]") zugewiesen.

"tmp" ist die Summe der Werte im Log-Array (siehe Loop darüber). Wenn tmp Null ist, soll keine Kurve gezeichnet werden ("exit").

Aber ich sehe gerade, eigentlich ist hier "logflg" nicht notwendig. Na ja, Gürtel+Hosenträger, schadet ja nichts.

In der Zwischenzeit habe ich den Verdacht, dass die Wurzel des Übels evtl. in der Neuzeichnung der DiagrammBox für das FormRepaint-Event liegt. Habe aber noch keine Ahnung was ich da ändern müsste.
Bin für jede Idee dankbar.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: So 28.09.08 22:16 
Moin!

Ich habe mal versucht, das umzusetzen:
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:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

const
  CM_PLOT = WM_USER +1;

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    BtnStart: TButton;
    BtnStop: TButton;
    procedure CMPLOT(var Msg: TMsg); message CM_PLOT;
    procedure BtnStartClick(Sender: TObject);
    procedure BtnStopClick(Sender: TObject);
  private
    FX: Integer;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CMPLOT(var Msg: TMsg);
begin
  if (BtnStop.Tag = 0) then begin
    PaintBox1.Canvas.Pixels[FX,PaintBox1.Height div 2+Trunc(PaintBox1.Height *0.5*sin(2*pi*FX/PaintBox1.Width))] := Random($1000000);
    FX := (FX +1) mod PaintBox1.Width;
    Application.ProcessMessages;
    PostMessage(Handle,CM_PLOT,0,0);
  end
  else
    BtnStart.Enabled := TRUE;
end;

procedure TForm1.BtnStartClick(Sender: TObject);
begin
  BtnStart.Enabled := FALSE;
  BtnStop.Tag := 0;
  BtnStop.Enabled := TRUE;
  FX := 0;
  PostMessage(Handle,CM_PLOT,0,0);
end;

procedure TForm1.BtnStopClick(Sender: TObject);
begin
  BtnStop.Tag := 1;
  BtnStop.Enabled := FALSE;
end;

end.
Allerdings: ohne ein APM bin ich auch nicht ausgekommen. :? Entweder ballert der Code die MessageQueue deartig schnell voll, dass keine externen Ereignisse mehr durchkommen, oder ich habe die Funktionsweise von PostMessage() doch noch nicht so ganz verstanden. Nunja, nimmt man das APM raus, ist das Programm so ausgelastet, dass das Formular nicht mehr bedienbar ist. :nixweiss:

Ich sehe in der aktuellen Konstruktion allerdings keinen wirklichen Hinderungsgrund, man schafft so eine mit maximaler Geschwindigkeit laufende Ereigniskette. Wenn die Dialogelemente, die man während des Zeichnens nicht verwenden können darf, deaktiviert sind, sollte auch APM nix "schlimmes" ausrichten können.

Es könnte nicht schaden, auch noch die threadbasierte Lösung zu testen, wo dann in einem Synchronize()-Aufruf das Bitmap gezeichnet wird. :idea: Das wäre dann auch die "saubere" Lösung bzgl. APM. :)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: So 28.09.08 23:27 
Hallo Narses,

vielen Dank für das Beispiel. Wie ich sehe, muss ich mich wohl mal intensiv mit dem Message-Handling auseinandersetzen.
Das APM hat jetzt nämlich noch weitere Dreckeffekte gezeigt, z.B. bei mitlaufendem Taskmanager und Mausbewegungen im Grafikfeld :shock: .
In die Thread-Methodik muss wohl auch mal reingucken :lupe: . Habe ich noch nie angewandt.

Na ja, das Program läuft insgesamt hervorragend (CPU-Auslastung incl. Datentransfer <15%), so dass ich da erst mal mit diesem kleinen Schönheitsfehler leben kann. "Stop" wird ja zuverlässig ausgeführt.

Ich werde daher diesen Thread vorerst als gelöst markieren. Falls noch irgendwelche Ideen auftauchen sollten, bin natürlich dankbar.

Also, vielen Dank an Euch beide für die Hilfe.

MfG Horst
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 29.09.08 00:02 
Moin!

user profile iconaim65 hat folgendes geschrieben:
Das APM hat jetzt nämlich noch weitere Dreckeffekte gezeigt, z.B. bei mitlaufendem Taskmanager und Mausbewegungen im Grafikfeld :shock: .
Bitte dran denken, dass der Inhalt einer Paint-Box volatil ist. :idea: Oder behandelst du das OnPaint, indem du nochmal das aktuelle (letzte?) Bitmap zeichnest? :nixweiss: Wenn nicht, dann kann eine Paintbox schon merkwürdige Effekte zeigen, wenn du andere Objekte darüber ziehst (z.B. ein anderes Fenster).

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
aim65 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 312

Win 9x, Win XP
Delphi 3pro, 7PE
BeitragVerfasst: Mo 29.09.08 16:21 
Moin Narses

war gestern schon in der Klappe :mrgreen: .

Das Refreshen der PaintBox (bei Abdeckung durch Fenster, usw.) funktioniert im Prog unter allen möglichen Einstellungen problemlos - siehe oben die procedure RedrawAll, die beim OnPaint-Event aufgerufen wird.

Da die Linien immer vom jeweilig geschriebenen Kurvensegment zum Rand und zurück geschrieben werden, werde ich mir mal die einzelnen PaintBox- und Bitmap-Routinen näher angucken. Na ja, wie ich schon sagte, ein dummes aber kein dringendes Problem. Sonst geht ja alles super.

Nochmals, vielen Dank
Horst