| | Anzeige von Analogwerten : Analog
Aufgabe
Analoge Anzeigen von Werten der A-Ausgänge (EX bzw. Ey) der fischertechnik
Interfaces.
Das mit den Werten ist ja einfach : ft.Analogs(ftiAEX) bzw. ftiGetAnalog(0)
bei umFishEx. Aber wie kommt man zu einem Analoginstrument. Alternative 1 : Bild
besorgen, Zeiger drüber zaubern. Geht, meist sogar mit variabler Größe, aber
wie siehts aus mit unterschiedlicher Skalenteilung und Beschriftung ? Das wär
dann Alternative 2 : selber zeichnen. Und das wird hier gezeigt. Die Quellen
sind in AnaInstr.ZIP zu finden. Zusätzlich sind
noch umFishU.ZIP
erforderlich.
Die ZeichenRoutine : anaShowDisplay
procedure anaShowDisplay(dsp: TPaintBox; aWert: Integer);
const
kStrich = 5; lStrich = 8; anzStriche = 21; sWinkel = 1.05; iRadius = 75;
zRadius = 40; zStrich = 48; zBereich = 1000; zTeilung = 250;
var
dWinkel, aWinkel: Single; sWert: Integer; dText: string;
x, y: Integer; x1, y1, x2, y2: Single;
i: LongInt;
function xD(x: Single): Integer;
begin
Result := Round(dsp.ClientWidth * (x / 100) + dsp.ClientWidth / 2);
end;
function yD(y: Single): Integer;
begin
Result := dsp.ClientHeight - Round(dsp.ClientHeight * ((y-20) / 80));
end;
begin
dsp.Canvas.Brush.Color := clWhite;
dsp.Canvas.Pen.Color := clWhite;
dsp.Canvas.Pen.Width := 1;
dsp.Canvas.Rectangle(0, 0, dsp.ClientWidth, dsp.ClientHeight);
dsp.Canvas.Pen.Color := clBlack;
// --- Minor Ticks ------------
aWinkel := sWinkel;
dWinkel := sWinkel / (anzStriche - 1);
for i := 1 to anzStriche do
begin
x1 := Cos(aWinkel) * iRadius;
y1 := Sin(aWinkel) * iRadius;
x2 := x1 + Cos(aWinkel) * kStrich;
y2 := y1 + Sin(aWinkel) * kStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xD(x2),yD(y2));
aWinkel := aWinkel + dWinkel;
end;
// --- Major Ticks --------------------
aWinkel := sWinkel;
sWert := zBereich;
dWinkel := sWinkel / ((anzStriche - 1) / 5);
for i := 1 to Round((anzStriche -1)/5+1) do
begin
x1 := Cos(aWinkel) * iRadius;
y1 := Sin(aWinkel) * iRadius;
x2 := x1 + Cos(aWinkel) * lStrich;
y2 := y1 + Sin(aWinkel) * lStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xD(x2),yD(y2));
dsp.Canvas.TextOut(xD(x2)-7,yD(y2)-12,IntToStr(sWert));
aWinkel := aWinkel + dWinkel;
sWert := sWert - zTeilung;
end;
// ----- Analog Wert -------------------------------
dsp.Canvas.Pen.Color := clRed;
aWinkel := sWinkel + ((zBereich-aWert) / zBereich) * sWinkel;
x1 := Cos(aWinkel) * zRadius;
y1 := Sin(aWinkel) * zRadius;
x2 := x1 + Cos(aWinkel) * zStrich;
y2 := y1 + Sin(aWinkel) * zStrich;
dsp.Canvas.MoveTo(xD(x1),yD(y1));
dsp.Canvas.LineTo(xd(x2),yD(y2));
// ----- Digital Wert ----------------------------
dText := 'Wert : ' + IntToStr(aWert);
x := (dsp.ClientWidth div 2) - (dsp.Canvas.TextWidth(dText) div 2);
y := dsp.ClientHeight - 24;
dsp.Canvas.TextOut(x, y, dText);
end;
Auf anhieb und auch wenn man näher hinsieht, kein fischertechnik, aber viel
Schule, überall Sin/Cos. So schlimm ist es nun auch wieder nicht (bei Visual
Basic ist es aber einfacher). Der Reihe
nach :
- anaShowDisplay wurde in eine separate Unit gestellt, um die
Wiederverwendung zu erleichtern.
- Im ersten Teil werden Konstanten definiert, die im eigentlichen
Zeichenteil verwendet werden. Grund : die Konstanten können leichter
geändert werden (statt 5mal 123 suchen) außerdem können sie bei einer
späteren Erweiterung als Variablen herausgereicht werden.
kStrich/lStrich : Länge des kurzen/lange Skalenstrichs
anzStrich : Anzahl Skalenstriche
sWinkel : Skalenwinkel in Radiant (die Skala ist ein Kreissegment)
iRadius : innerer Radius der Skala
zRadius : innerer Radius des Zeigers
zStrich : Länge des Zeigers
zBereich : Anzeigebereich (größter Wert, Beginn bei 0)
zTeilung : Teilung der "Major Ticks" (der langen Striche)
dWinkel : Delta Winkel (Schritt von Skalenstrich zu -strich)
aWinkel : Anfangswinkel ( !! alle Winkel in Radiant : Pi = 180°)
sWert : Skalenwert
dText : Digitaler Anzeigewert als String.
- dsp.Canvas.Brush.Color ...
Gezeichnet wird auf das Canvas (Leinwand) einer PaintBox (AufrufParameter
dsp). Die muß erstmal etwas zurecht gerückt werden. (Rectangle löschst
sie). Eine Eigenart des Canvas sind seine Koordinaten : Linke obere Ecke ist
0,0 gemessen wird ausschließlich in Pixel. anaShowDisplay geht aber (wie
auch die VB-Version) von einer Zeichenfläche -50 / + 50 für x und 20 / 100
für y aus, deswegen sind zum echten Zeichnen auf den Canvas die
Umrechnungsfunktionen xD / yD erforderlich.
- Minor Ticks
Zuerst werden die kleinen Striche gezeichnet und zwar von rechts nach links
(weil die sin/cos-Werte dann leichter zu handhaben sind) einer nach dem
anderen. Und von innen nach außen, zuerst wird die Koordinate innen (x1/y1)
bestimmt und dann die äußere durch Addieren des Längenanteils.
Cos(aWinkel)*iRadius berechnet dabei den x-Wert der inneren Strichkoordinate
(bezogen wird sich dabei auf einen auf picAnalog gar nicht mehr
darstellbaren Punkt 0,0. .MoveTo geht zum Ausgang des Striches (ohne zu
Zeichnen) und LineTo zeichnet.
-
Major Ticks
Ist eigentlich das gleiche, nur länger, Jeder fünfte Skalenstrich wird
über"gemalt". Außerdem wird er beschriftet. Mit TextOut wird
beschriftet.
- Analog Wert
Zeichnen der Zeigerposition mit dem aktuellen Wert. Dazu wird erstmal der
Analogwert (aWert) in einen Winkel (Radiant) umgerechnet (prozentualer
Anteil von zBereich). Anschließend der Zeigerstrich (wie Skalenstrich, nur
rot).
- Digital Wert
Damit die Phantasie in Grenzen bleibt wird zusätzlich noch der aWert als
numerischer Wert mit TextOut angezeigt (schön mittig).
Das wars mit anaShowDisplay. Das Besorgen der anzuzeigenden Analogwerte macht
man mit links und in einer Endlosschleife (Abbruch : ESC-Taste) :
Die AnzeigeSchleife cmdAction ...
procedure TfrmMain.cmdActionClick(Sender: TObject);
begin
if ftiOpenInterface('COM1') = ftiFehler then begin
MessageDlg('InterfaceProblem : COM1', mtInformation, [mbOK], 0);
exit;
end;
cmdAction.Enabled := false;
cmdEnde.Enabled := false;
lblHinweis.Visible := true;
repeat
anaShowDisplay(Self.picAnalog, ftiGetAnalog(1));
ftiWaitForTime(100);
until ftiFinish(1);
ftiCloseInterface;
lblHinweis.Visible := false;
cmdAction.Enabled := true;
cmdEnde.Enabled := true;
anaShowDisplay(Self.picAnalog, 0);
cmdEnde.SetFocus;
end;
Das Meiste ist zur
Verriegelung des Programms während des Betriebs der Abfrageschleife (repeat ...
until) erforderlich (deren Abbruch über ESC-Taste oder E1 = true durch Finish).
Noch mehr Verriegelung in :
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if not cmdAction.Enabled then begin
ftiNotHalt := true;
CanClose := false;
end;
end;
CanClose = true lehnt den Ende-Wunsch (Klick auf das x rechts oben) schlicht
ab, setzt aber ftiNotHalt = true (Finish kümmert sich auch darum) und leitet
ein Schleifenende ein. Beim nächsten Klick aufs x klppst dann.
Wenn die Form verdeckt wurde, wird das AnalogDisplay gelöscht, zur
Restauration :
procedure TfrmMain.FormPaint(Sender: TObject);
begin
anaShowDisplay(picAnalog, 0);
end;
Das Resize
procedure TfrmMain.FormResize(Sender: TObject);
var
WDelta, SDelta: Integer;
begin
if Sperre then exit;
Sperre := true;
if WindowState = wsMinimized then exit;
if Width < WMin then Width := WMin;
if Height < SMin then Height := SMin;
WDelta := ClientWidth - WFest - picAnalog.Width;
SDelta := ClientHeight - SFest - picAnalog.Height;
cmdAction.Left := cmdAction.Left + WDelta;
cmdAction.Top := cmdAction.Top + SDelta;
cmdEnde.Left := cmdEnde.Left + WDelta;
cmdEnde.Top := cmdEnde.Top + SDelta;
lblHinweis.Top := lblHinweis.Top + SDelta;
picAnalog.Width := picAnalog.Width + WDelta;
picAnalog.Height := picAnalog.Height + SDelta;
anaShowDisplay(picAnalog, 0);
Sperre := false;
end;
Wo nun schon anaShowDisplay ein relatives Koordinatensystem bietet, so kann
man ja auch mal verschiedene Größen der Form und damit auch der Analoganzeige
probieren (es geht bis zum FullScreen). Der Trick beim Resize ist, sich ein
(oder auch mehrere) Control auszusuchen, dem die Vergrößerung der Form zugute
kommt (oder wos dann weniger wird). Das ist hier natürlich die Analoganzeige
picAnalog. Dazu wird die bereinigte Größenänderung WDelta / SDelta gegenüber
der vorherigen Anzeige bestimmt. Das geschieht durch Abziehen der alten Werte
für die unveränderlichen Controls (WFest / SFest) und der alten Größe von
picAnalog von den neuen Formmaßen. Die wird dann auf die alten Maße von
picAnalog aufaddiert (wurde die Form kleiner ist .Delta negativ, dann wird
abgezogen).
Sperre verhindert einen erneuten Aufruf wenn Resize bei der Arbeit ist, if
Width / Height verhindern ein Verkleinern unter ein Mindestmaß.
Die Werte für WFest / SFest werden in FormCreate festgelegt :
procedure TfrmMain.FormCreate(Sender: TObject);
begin
WFest := ClientWidth - picAnalog.Width;
WMin := 200;
SFest := ClientHeight - picAnalog.Height;
SMin := 250;
anaShowDisplay(picAnalog, 0);
end;
Mögliche Erweiterungen
Eher mühsam :
Anzeige der gemessenen Werte (Bereich 0 - 1024) in beliebigen Wertebereichen
z.B. 0 - 100° C. Dazu muß dann noch kräftig umgerechnet werden.
Anpassung von Strichstärke und Schriftgröße an besonders große
Analoganzeigen.
Eher was für Spezialisten :
Umwandlung in eine Komponente
|