| Autor |
Beitrag |
aim65
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: 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 Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am So 28.09.2008 um 16:40
|
|
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: 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 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: 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
      

Beiträge: 10185
Erhaltene Danke: 1261
W11x64
TP3 .. D7pro .. D10.2CE
|
Verfasst: So 28.09.08 16:17
Moin!
Du kannst mal eine Ereigniskette probieren, das könnte auch klappen:  - 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
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: 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 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: 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 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: So 28.09.08 17:49
So, hier ist der Auszug (hoffe, dass das mit den Tags klappt)
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; begin BitBlt(Form1.PaintBox1.Canvas.Handle, 0, 0, Dbmp.Width, Dbmp.Height, Dbmp.Canvas.Handle, 0, 0, SrcCopy); end;
procedure RedrawTrace; 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; 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; end;
procedure RedrawAll; begin DrawDiagramBox; RedrawDiagram; RedrawMarkers; RedrawTrace; DrawFrame(bsClear); end;
procedure DrawSegment(n,aVal: Integer); begin with Form1.PaintBox1.Canvas do begin Pen.Color := clTrace; LineTo(n-1,aVal+1); end; end;
procedure TForm1.StartSweep(StpNo: Integer); 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); Label22.Hide; DrawDiagramBox; ScrollBar3.Position := 0; ProgressBar1.Show; ProgressBar1.Position := 0; ProgressBar1.Step := 5; if Sweepflg then DrawShadowTrace else RedrawDiagram; if mCount >1 then RedrawMarkers; ClearMeasArrays; swpFreq := StartFreq *1000; Dly := mDelay/1000; fStep := (StopFreq - StartFreq) * 1000 / StpNo; ADC := Send_GetData(StartFreq*1000,BaseRef,25); if COMerr or (CTS <1)then begin CloseCOM; exit; end; ADC := CorrectData(ADC,delta,1); if logflg then MeasLog[1]:= ADC else MeasLin[1]:= ADC; tmp := ADC; PaintBox1.Canvas.MoveTo(1,ADC+1); case StpNo of 500: begin for n:= 2 to 501 do begin tmp := ADC; swpFreq := swpFreq + fStep; ADC := Send_GetData(swpFreq,BaseRef,Dly); ADC := CorrectData(ADC,delta,n); if datflg then ADC :=Round(tmp); 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 end; for n:= 1 to 501 do begin if logflg then MeasOld[n] := MeasLog[n] else MeasOld[n] := MeasLin[n]; end; UpdateMarkers; ProgressBar1.Hide; end; |
|
|
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: So 28.09.08 18:43
das einzige was mir daran auffällt ist das:
Delphi-Quelltext 1: 2: 3:
| if logflg and (tmp = 0) then exit; 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:
Delphi-Quelltext 1:
| if logflg or true then |
|
|
aim65 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: 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
      

Beiträge: 10185
Erhaltene Danke: 1261
W11x64
TP3 .. D7pro .. D10.2CE
|
Verfasst: So 28.09.08 22:16
Moin!
Ich habe mal versucht, das umzusetzen:
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 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.
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.  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 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: 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  .
In die Thread-Methodik muss wohl auch mal reingucken  . 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
      

Beiträge: 10185
Erhaltene Danke: 1261
W11x64
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 29.09.08 00:02
Moin!
aim65 hat folgendes geschrieben: | Das APM hat jetzt nämlich noch weitere Dreckeffekte gezeigt, z.B. bei mitlaufendem Taskmanager und Mausbewegungen im Grafikfeld . |
Bitte dran denken, dass der Inhalt einer Paint-Box volatil ist.  Oder behandelst du das OnPaint, indem du nochmal das aktuelle (letzte?) Bitmap zeichnest?  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 
      
Beiträge: 312
Win 9x, Win XP
Delphi 3pro, 7PE
|
Verfasst: Mo 29.09.08 16:21
Moin Narses
war gestern schon in der Klappe  .
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
|
|
|