lazarus-ccr/components/industrialstuff/source/indgnoumeter.pas
2019-04-22 16:12:52 +00:00

563 lines
15 KiB
ObjectPascal

{**********************************************************************
GnouMeter is a meter which can display an integer or a float value (Single).
Just like a progress bar or a gauge, all you have do do is to define
the Minimum and maximum values as well as the actual value.
Above the meter, one can display the name of the data being measured (optional)
and its actual value with its corresponding unit.
The minimum and maximum values are respectively shown at the bottom and the
top of the meter with their corresponding units.
The meter is filled with the color ColorFore and its background color
is defined by the ColorBack Property.
THIS COMPONENT IS ENTIRELY FREEWARE
Author: Jérôme Hersant
jhersant@post4.tele.dk
***********************************************************************}
unit indGnouMeter;
{$mode objfpc}{$H+}
interface
uses
Classes, Controls, Graphics, SysUtils,
LMessages, Types, LCLType, LCLIntf;
const
DEFAULT_BAR_THICKNESS = 5;
DEFAULT_GAP_TOP = 20;
DEFAULT_GAP_BOTTOM = 10;
DEFAULT_MARKER_DIST = 4;
DEFAULT_MARKER_SIZE = 6;
type
TindGnouMeter = class(TGraphicControl)
private
fValue: Double;
fColorFore: TColor;
fColorBack: TColor;
fSignalUnit: ShortString;
fValueMax: Double;
fValueMin: Double;
fDigits: Byte;
fIncrement: Double;
fShowIncrements: Boolean;
fGapTop: Word;
fGapBottom: Word;
fBarThickness: Word;
fMarkerColor: TColor;
fMarkerDist: Integer;
fMarkerSize: Integer;
fShowMarker: Boolean;
//Variables used internally
TopTextHeight: Word;
LeftMeter: Integer;
DisplayValue: String;
DrawStyle: integer;
TheRect: TRect;
//End of variables used internally
function IsBarThicknessStored: Boolean;
function IsGapBottomStored: Boolean;
function IsGapTopStored: Boolean;
function IsMarkerDistStored: Boolean;
function IsMarkerSizeStored: Boolean;
procedure SetValue(val: Double);
procedure SetColorBack(val: TColor);
procedure SetColorFore(val: TColor);
procedure SetSignalUnit(val: ShortString);
procedure SetValueMin(val: Double);
procedure SetValueMax(val: Double);
procedure SetDigits(val: Byte);
procedure SetTransparent(val: Boolean);
function GetTransparent: Boolean;
procedure SetIncrement(val: Double);
procedure SetShowIncrements(val: Boolean);
procedure SetGapTop(val: Word);
procedure SetGapBottom(val: Word);
procedure SetBarThickness(val: Word);
procedure SetMarkerColor(val: TColor);
procedure SetMarkerDist(val: Integer);
procedure SetMarkerSize(val: Integer);
procedure SetShowMarker(val: Boolean);
procedure DrawTopText;
procedure DrawMeterBar;
procedure DrawIncrements;
function ValueToPixels(val: Double): integer;
procedure DrawValueMax;
procedure DrawValueMin;
procedure DrawMarker;
protected
procedure CMTextChanged(var {%H-}Message: TLMessage); message CM_TEXTCHANGED;
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Align;
property Caption;
property Visible;
property ShowHint;
property Value: Double read fValue write SetValue;
property Color;
property Font;
property ParentColor;
property ColorFore: Tcolor read fColorFore write SetColorFore default clRed;
property ColorBack: Tcolor read fColorBack write SetColorBack default clBtnFace;
property SignalUnit: ShortString read fSignalUnit write SetSignalUnit;
property ValueMin: Double read fValueMin write SetValueMin;
property ValueMax: Double read fValueMax write SetValueMax;
property Digits: Byte read fDigits write SetDigits;
property Increment: Double read fIncrement write SetIncrement;
property ShowIncrements: Boolean read fShowIncrements write SetShowIncrements default true;
property Transparent: Boolean read GetTransparent write SetTransparent default true;
property GapTop: Word
read fGapTop write SetGapTop stored IsGapTopStored;
property GapBottom: Word
read fGapBottom write SetGapBottom stored IsGapBottomStored;
property BarThickness: Word
read fBarThickness write SetBarThickness stored IsBarThicknessStored;
property MarkerColor: TColor read fMarkerColor write SetMarkerColor;
property MarkerDist: Integer
read fMarkerDist write SetMarkerDist stored IsMarkerDistStored;
property MarkerSize: Integer
read fMarkerSize write SetMarkerSize stored IsMarkerSizeStored;
property ShowMarker: Boolean read fShowMarker write SetShowMarker default true;
end;
implementation
constructor TindGnouMeter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csReplicatable, csSetCaption];
Width := 100;
Height := 200;
fColorFore := clRed;
fColorBack := clBtnFace;
fMarkerColor := clBlue;
fValueMin := 0;
fValueMax := 100;
fIncrement := 10;
fShowIncrements := True;
fShowMarker := True;
fValue := 0;
fGapTop := Scale96ToFont(DEFAULT_GAP_TOP);
fGapBottom := Scale96ToFont(DEFAULT_GAP_BOTTOM);
fBarThickness := Scale96ToFont(DEFAULT_BAR_THICKNESS);
fMarkerDist := Scale96ToFont(DEFAULT_MARKER_DIST);
fMarkerSize := Scale96ToFont(DEFAULT_MARKER_SIZE);
fSignalUnit := 'Units';
end;
destructor TindGnouMeter.Destroy;
begin
inherited Destroy;
end;
procedure TindGnouMeter.CMTextChanged(var Message: TLMessage);
begin
Invalidate;
end;
procedure TindGnouMeter.DoAutoAdjustLayout(
const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion);
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
begin
DisableAutosizing;
try
if IsBarThicknessStored then
FBarThickness := Round(FBarThickness * AXProportion);
if IsGapBottomStored then
FGapBottom := Round(FGapBottom * AYProportion);
if IsGapTopStored then
FGapTop := Round(FGapTop * AYProportion);
if IsMarkerDistStored then
FMarkerDist := Round(FMarkerDist * AXProportion);
if IsMarkerSizeStored then
FMarkerSize := Round(FMarkerSize * AXProportion);
finally
EnableAutoSizing;
end;
end;
end;
function TindGnouMeter.IsBarThicknessStored: Boolean;
begin
Result := FBarThickness <> Scale96ToFont(DEFAULT_BAR_THICKNESS);
end;
function TindGnouMeter.IsGapBottomStored: Boolean;
begin
Result := FGapBottom <> Scale96ToFont(DEFAULT_GAP_BOTTOM);
end;
function TindGnouMeter.IsGapTopStored: Boolean;
begin
Result := FGapTop <> Scale96ToFont(DEFAULT_GAP_TOP);
end;
function TindGnouMeter.IsMarkerDistStored: Boolean;
begin
Result := FMarkerDist <> Scale96ToFont(DEFAULT_MARKER_DIST);
end;
function TindGnouMeter.IsMarkerSizeStored: Boolean;
begin
Result := FMarkerSize <> Scale96ToFont(DEFAULT_MARKER_SIZE);
end;
procedure TindGnouMeter.SetValue(val: Double);
begin
if (val <> fValue) and (val >= fValueMin) and (val <= fValueMax) then
begin
fValue := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetColorFore(val: TColor);
begin
if val <> fColorFore then
begin
fColorFore := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetColorBack(val: TColor);
begin
if val <> fColorBack then
begin
fColorBack := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetSignalUnit(val: ShortString);
begin
if val <> fSignalUnit then
begin
fSignalUnit := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetValueMin(val: Double);
begin
if (val <> fValueMin) and (val <= fValue) then
begin
fValueMin := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetValueMax(val: Double);
begin
if (val <> fValueMax) and (val >= fValue) then
begin
fValueMax := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetDigits(val: Byte);
begin
if (val <> fDigits) then
begin
fDigits := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetIncrement(val: Double);
begin
if (val <> fIncrement) and (val > 0) then
begin
fIncrement := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetShowIncrements(val: Boolean);
begin
if (val <> fShowIncrements) then
begin
fShowIncrements := val;
Invalidate;
end;
end;
function TindGnouMeter.GetTransparent: Boolean;
begin
Result := not (csOpaque in ControlStyle);
end;
procedure TindGnouMeter.SetTransparent(Val: Boolean);
begin
if Val <> Transparent then
begin
if Val then
ControlStyle := ControlStyle - [csOpaque]
else
ControlStyle := ControlStyle + [csOpaque];
Invalidate;
end;
end;
procedure TindGnouMeter.SetGapTop(val: Word);
begin
if (val <> fGapTop) then
begin
fGapTop := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetGapBottom(val: Word);
begin
if (val <> fGapBottom) then
begin
fGapBottom := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetBarThickness(val: Word);
begin
if (val <> fBarThickness) and (val > 0) then
begin
fBarThickness := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetMarkerColor(val: TColor);
begin
if (val <> fMarkerColor) then
begin
fMarkerColor := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetMarkerDist(val: Integer);
begin
if (val <> fMarkerDist) then
begin
fMarkerDist := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetMarkerSize(val: Integer);
begin
if (val <> fMarkerSize) then
begin
fMarkerSize := val;
Invalidate;
end;
end;
procedure TindGnouMeter.SetShowMarker(val: Boolean);
begin
if (val <> fShowMarker) then
begin
fShowMarker := val;
Invalidate;
end;
end;
procedure TindGnouMeter.DrawIncrements;
var
i: Double;
PosPixels: Word;
begin
if fShowIncrements then
begin
with Canvas do
begin
i := fValueMin;
while i <= fValueMax do
begin
PosPixels := ValueToPixels(i);
pen.color := clGray;
MoveTo(LeftMeter + BarThickness + 3, PosPixels - 1);
LineTo(LeftMeter + BarThickness + 7, PosPixels - 1);
pen.color := clWhite;
MoveTo(LeftMeter + BarThickness + 3, PosPixels);
LineTo(LeftMeter + BarThickness + 7, PosPixels);
i := i + fIncrement;
end;
end;
end;
end;
procedure TindGnouMeter.DrawMarker;
var
v: Integer;
dx, dy: Integer;
begin
if fShowMarker then
begin
v := ValueToPixels(fValue);
with Canvas do
begin
dx := FMarkerSize;
dy := round(FMarkerSize * sin(pi/6));
// 3D edges
Pen.Color := clWhite;
Brush.Style := bsClear;
MoveTo(LeftMeter - FMarkerDist + 1, v);
LineTo(LeftMeter - FMarkerDist - dx - 1, v - dy - 1);
LineTo(LeftMeter - FMarkerDist - dx - 1, v + dy + 1);
Pen.Color := clGray;
LineTo(LeftMeter - FMarkerDist + 1, v);
// Triangle
Pen.Color := fMarkerColor;
Brush.Color := fMarkerColor;
Brush.Style := bsSolid;
Polygon([
Point(LeftMeter - FMarkerDist, v),
Point(LeftMeter - FMarkerDist - dx, v - dy),
Point(LeftMeter - FMarkerDist - dx, v + dy)
]);
end;
end;
end;
procedure TindGnouMeter.DrawTopText;
begin
with Canvas do
begin
DisplayValue := Caption;
Brush.Style := bsClear;
TheRect := ClientRect;
DrawStyle := DT_SINGLELINE + DT_NOPREFIX + DT_CENTER + DT_TOP;
Font.Style := [fsBold];
TopTextHeight := DrawText(Handle, PChar(DisplayValue),
Length(DisplayValue), TheRect, DrawStyle);
Font.Style := [];
TheRect.Top := TopTextHeight;
DisplayValue := FloatToStrF(Value, ffFixed, 8, fDigits) + ' ' + fSignalUnit;
TopTextHeight := TopTextHeight + DrawText(Handle, PChar(DisplayValue),
Length(DisplayValue), TheRect, DrawStyle);
TopTextHeight := TopTextHeight + fGapTop;
end;
end;
procedure TindGnouMeter.DrawValueMin;
begin
with Canvas do
begin
TheRect := ClientRect;
TheRect.Left := LeftMeter + BarThickness + Scale96ToFont(10);
TheRect.Top := TopTextHeight;
TheRect.Bottom := Height - fGapBottom + Scale96ToFont(6);
Brush.Style := bsClear;
DrawStyle := DT_SINGLELINE + DT_NOPREFIX + DT_LEFT + DT_BOTTOM;
DisplayValue := FloatToStrF(ValueMin, ffFixed, 8, fDigits) + ' ' + fSignalUnit;
DrawText(Handle, PChar(DisplayValue), Length(DisplayValue),
TheRect, DrawStyle);
end;
end;
procedure TindGnouMeter.DrawValueMax;
begin
with Canvas do
begin
TheRect := ClientRect;
TheRect.Left := LeftMeter + BarThickness + Scale96ToFont(10);
TheRect.Top := TopTextHeight - Scale96ToFont(6);
Brush.Style := bsClear;
DrawStyle := DT_SINGLELINE + DT_NOPREFIX + DT_LEFT + DT_TOP;
DisplayValue := FloatToStrF(ValueMax, ffFixed, 8, fDigits) + ' ' + fSignalUnit;
DrawText(Handle, PChar(DisplayValue), Length(DisplayValue),
TheRect, DrawStyle);
end;
end;
procedure TindGnouMeter.DrawMeterBar;
begin
with Canvas do
begin
Pen.Color := fColorBack;
Brush.Color := fColorBack;
Brush.Style := bsSolid;
Rectangle(LeftMeter, ValueToPixels(fValueMax), LeftMeter +
fBarThickness, ValueToPixels(fValueMin));
Pen.Color := fColorFore;
Brush.Color := fColorFore;
Brush.Style := bsSolid;
Rectangle(LeftMeter + 1, ValueToPixels(fValue), LeftMeter +
fBarThickness, ValueToPixels(fValueMin));
Pen.color := clWhite;
Brush.Style := bsClear;
MoveTo(LeftMeter + fBarThickness - 1, ValueToPixels(fValueMax));
LineTo(LeftMeter, ValueToPixels(fValueMax));
LineTo(LeftMeter, ValueToPixels(fValueMin) - 1);
Pen.color := clGray;
LineTo(LeftMeter + fBarThickness, ValueToPixels(fValueMin) - 1);
LineTo(LeftMeter + fBarThickness, ValueToPixels(fValueMax));
if (fValue > fValueMin) and (fValue < fValueMax) then
begin
Pen.color := clWhite;
MoveTo(LeftMeter + 1, ValueToPixels(fValue));
LineTo(LeftMeter + fBarThickness, ValueToPixels(fValue));
Pen.color := clGray;
MoveTo(LeftMeter + 1, ValueToPixels(fValue) - 1);
LineTo(LeftMeter + fBarThickness, ValueToPixels(fValue) - 1);
end;
end;
end;
function TindGnouMeter.ValueToPixels(val: Double): integer;
var
factor: Double;
begin
Result := 0;
if fValueMax > fValueMin then
begin
Factor := (Height - fGapBottom - TopTextHeight) / (fValueMin - fValueMax);
Result := Round(Factor * val - Factor * fValueMax + TopTextHeight);
end;
end;
procedure TindGnouMeter.Paint;
begin
LeftMeter := (Width div 2) - Scale96ToFont(10) - fBarThickness;
with Canvas do
begin
if not Transparent then
begin
Brush.Color := Self.Color;
Brush.Style := bsSolid;
FillRect(ClientRect);
end;
Brush.Style := bsClear;
DrawTopText;
DrawValueMin;
DrawValueMax;
DrawMeterBar;
DrawMarker;
DrawIncrements;
end;
end;
end.