Firemonkey 2D/3D Graph Chart Demo Project

I release the project that draws charts using various Firemonkey's  graphical components and objects.
The Demo Project reads the data from the SQLite Database Table and draws a graph chart.

It has the following features.

  • Demo Project built with Delphi Berlin update2.
  • Multi Platform Support: Windows / Mac OS X / Android / iOS.
  • Demo uses SQLite but uses FireDAC, so all databases can be linked.
  • Database Table Generates graphs that match the aspect ratio automatically, regardless of the number of records.
  • The Demo draws two charts, but you can easily draw multiple charts simultaneously.
  • Graph range is based on maximum value of record value and can be specified at runtime.
  • 2D Graph can draw on all Firemonkey's visible objects (TControl). Demo created above Rectangle object.
  • The total size of the 2D Graph is automatically set to match the size of the dynamically generated TLayout.
  • 3D Graph can be changed by mouse or touch Viewpoint.
  • 3D Graph size is set as camera Z coordinate value.
  • The 3D Graph range is automatically set to match the size of the dynamically generated TLayout3D.
  • The front and back positions of the screen reference of the 3D chart which is generated redundant are designated as the Z coordinate value of TLayout3D.
  • For Demo, 3D Graph Chart can be easily called from other projects using TFrame.

 2D chart creation method


 3D chart creation method

Sample data (SQLite Table)









Draw Line any direction with TPath

Draw Line any direction with TPath

TLine object of firemonkey can draw a line to one direction.
If you want to draw line to any direction, you can use TPath.
TPath can be used drawing a curve normally.
But it is simple for line.



Sample Source

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects, FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Rectangle1: TRectangle;
    Button2: TButton;
    Button1: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    procedure Draw_Line(pBase: TControl; x1, y1, x2, y2,thikness:single; color:Cardinal );
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Draw_Line(pBase: TControl; x1, y1, x2, y2,thikness:single; color:Cardinal );
var
  LinePath : TPath;
  p1, p2 : TPointF;
begin
  LinePath := TPath.Create(pBase);
  LinePath.Parent := pBase;

  // p1 and p2 number is a only Direction
  // Drawing line of length is set by width and height of TPath.
  if ( x1 < x2 ) and ( y1 < y2 ) then
  begin
    p1.X := 0;     p1.Y := 0;        //  \
    p2.X := 100;   p2.Y := 100;

    LinePath.Position.X := x1;
    LinePath.Position.Y := y1;
    LinePath.Width  := x2 - x1;
    LinePath.Height := y2 - y1;
  end
  else if y1 = y2 then
  begin
    p1.X := 0;    p1.Y := 0;        //  -
    p2.X := 100;  p2.Y := 0;

    LinePath.Position.X := x1;
    LinePath.Position.Y := y1;
    LinePath.Width  := x2 - x1;
    LinePath.Height := 100;  // any value
  end
  else if x1 = x2 then
  begin
    p1.X := 0;   p1.Y := 0;        //  |
    p2.X := 0;   p2.Y := 100;

    LinePath.Position.X := x1;
    LinePath.Position.Y := y1;
    LinePath.Width  := 100;  // any value
    LinePath.Height := y2- y1
  end
  else if ( x1 < x2 ) and ( y1 > y2 ) then
  begin
    p1.X := 100;  p1.Y := 0;        //  /
    p2.X := 0;    p2.Y := 100;

    LinePath.Width  := x2 - x1;
    LinePath.Height := y1 - y2;

    LinePath.Position.X := x1;
    LinePath.Position.Y := y1 - LinePath.Height;
  end;

  LinePath.Stroke.Thickness := thikness;
  LinePath.Stroke.Color := color;
  LinePath.Data.MoveTo( p1 );
  LinePath.Data.LineTo( p2 );
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  Draw_Line( Rectangle1, 0,0, 500,300, 2, $FFFF0000 );
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Draw_Line( Rectangle1, 0,0, 500,0, 4, $FFF0000FF);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Draw_Line( Rectangle1, 0,0, 0,300, 6, $FFFF00FF);
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  Draw_Line( Rectangle1, 0,300, 500,0, 4, $FF00FF00 );
end;



System Layout built with Delphi Firemonkey

KOREA JOBWORLD 119 Center




Major Function

- http protocol communication with Adobe Flash apps(6 User) by WebBroker.
- My-SQL database control
- Firemonkey Screen Alarm Application in MS-Windows.
- FastReport Print Solution.
- Print Server of Mobile through Tethering Server.
- Android Tablet App
- RS-232C Serial Communication for Multi Screen Controll Device.

Rad Studio 10.1 Berlin Dialog API Sample Source

Simplified Dialog API

Several procedures and functions to show dialog boxes are now deprecated, and have been replaced by the procedures and functions of the new IFMXDialogServiceAsync and IFMXDialogServiceSync platform services that provide support for synchronous and asynchronous dialog boxes, respectively.
The following table summarizes the API changes:

Deprecated Members New Members
  • FMX.Dialogs.MessageDlg
  • FMX.Dialogs.MessageDlgPos
  • FMX.Dialogs.MessageDlgPosHelp
  • FMX.Platform.IFMXDialogService.MessageDialog
  • FMX.Dialogs.InputBox
  • FMX.Dialogs.InputQuery
  • FMX.Platform.IFMXDialogService.InputQuery
Docwiki Link


Sample Source

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.ListBox, FMX.Layouts,
  FMX.Platform, FMX.StdCtrls, FMX.Controls.Presentation, FMX.DialogService, FMX.ScrollBox, FMX.Memo;

type
  TForm1 = class(TForm)
    ToolBar1: TToolBar;
    Label1: TLabel;
    ListBox1: TListBox;
    ListBoxItem1: TListBoxItem;
    ListBoxItem2: TListBoxItem;
    ListBoxItem3: TListBoxItem;
    ListBoxItem4: TListBoxItem;
    ListBoxItem5: TListBoxItem;
    ListBoxItem6: TListBoxItem;
    Memo1: TMemo;
    procedure ListBoxItem1Click(Sender: TObject);
    procedure ListBoxItem2Click(Sender: TObject);
    procedure ListBoxItem3Click(Sender: TObject);
    procedure ListBoxItem4Click(Sender: TObject);
    procedure ListBoxItem5Click(Sender: TObject);
    procedure ListBoxItem6Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

// uses  FMX.Platform, FMX.DialogService
//**************************************************************************************************
procedure TForm1.ListBoxItem1Click(Sender: TObject);
var
  ASyncService : IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface (ASyncService)) then
  begin
    ASyncService.ShowMessageAsync ( ( Sender as TListBoxItem ).Text );
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'ShowMessageAsync Displayed' );
end;


procedure TForm1.ListBoxItem2Click(Sender: TObject);
var
  SyncService : IFMXDialogServiceSync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface (SyncService)) then
  begin
    SyncService.ShowMessageSync ( ( Sender as TListBoxItem ).Text );
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'ShowMessageSync Displayed' );
end;

//**************************************************************************************************
procedure TForm1.ListBoxItem3Click(Sender: TObject);
var
  ASyncService : IFMXDialogServiceASync;
  caption, inData : array[0..1] of string;
begin
  caption[0]   := 'ID :';
  caption[1] := #1 + 'Pass :';  // #1 앞에 붙이면 마스킹 기능

  inData[0] := 'admin';  // 초기값
  inData[1] := '1234';

  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface (ASyncService)) then
  begin
    ASyncService.InputQueryAsync( 'Input String', caption, inData,
      procedure (const AResult : TModalResult; const AValues : array of string)
      begin
         case AResult of
           mrOk: ( Sender as TListBoxItem ).Text := AValues[0] + '/' + AValues[1];
         end;
      end );
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'InputQueryAsync Displayed' );
end;


procedure TForm1.ListBoxItem4Click(Sender: TObject);
var
  SyncService : IFMXDialogServiceSync;
  caption, inData : array[0..1] of string;
begin
  caption[0]   := 'ID :';
  caption[1] := #1 + 'Pass :';  // #1 앞에 붙이면 마스킹 기능

  inData[0] := 'admin';  // 초기값
  inData[1] := '1234';

  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceSync, IInterface(SyncService)) then
  begin
    if SyncService.InputQuerySync( 'Input String', caption, inData ) then
       ( Sender as TListBoxItem ).Text := inData[0] + '/' + inData[1];
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'InputQuerySync Displayed' );
end;


//**************************************************************************************************
procedure TForm1.ListBoxItem5Click(Sender: TObject);
var
  ASyncService : IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then
  begin
    ASyncService.MessageDialogAsync( 'Question ?', TMsgDlgType.mtConfirmation,
                                                   [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbNo, 0,
     procedure(const AResult: TModalResult)
     begin
       case AResult of
         mrYES : ( Sender as TListBoxItem ).Text := 'YES';
       end;
     end);
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'MessageDialogAsync Displayed' );
end;


procedure TForm1.ListBoxItem6Click(Sender: TObject);
var
  SyncService : IFMXDialogServiceSync;
  rValue, i : integer;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceSync, IInterface(SyncService)) then
  begin
    rValue :=  SyncService.MessageDialogSync( 'Question ?', TMsgDlgType.mtConfirmation,
                                             [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbNo, 0 );

    ( Sender as TListBoxItem ).Text := rValue.ToString;
  end;

  Memo1.Lines.Clear;
  Memo1.Lines.Add( 'MessageDialogSync Displayed' );
end;

end.


FireMonkey SQLite DBExpress vs FireDAC

FireMonkey 에서 SQLite 사용시 DBExpress의 TSQLConnection을 사용하면
다음과 같이 SQL 문 안에 as 항목을 사용하면 실행시 필드없음 에러가 발생한다.

    QueryI.Close;
    QueryI.SQL.Clear;
    QueryI.Sql.Add( 'SELECT count(*) as cnt from PackingMaster ' );
    QueryI.Open;
    result := QueryI.FieldByName('cnt').AsInteger;

이때는 DBExpress의 TSQLConnection 대신 FireDAC의 TFDConnection을 사용하면 에러 없이 잘 실행된다.

참고로 Data Module Unit안에 FireDAC 컴포넌트를 사용하면 컴파일시 FireDAC.VCLUI.Wait 가 자동추가 되어 타겟을 안드로이드나 iOS로 지정시 에러가 발생하는데 이때는 Data Module의 ClassGroup의 속성값을 FMX.Controls.TControl 로 바꿔주면 된다.