YOUSEFUL / DELPHI / SAMPLE PROJECTS
Linked dashboard
A VCL program, with the form created in code, that binds a TAtlasDashboard
to one TAtlasTable and shows six widgets from the same snapshot: three KPI
cards (revenue, average sale, order count), two bar charts (revenue by region,
units by product) and a detail grid. A region combo box filters every widget at
once, a button appends a row to the table so the widgets refresh through the
table's change notification, and two buttons save and reload the layout as
JSON.
What it demonstrates
TAtlasDashboard.SourceandAddWidgetwithTAtlasWidgetDefinition.CreateforawKPI,awChartandawGridwidgets, including a count KPI with an empty field.SetFiltersapplying one filter to all widgets without touching the source table's own filter.- Automatic refresh when the source table changes (
AddRow), andLastErrorwhen a refresh fails. ToJSON/LoadJSONof widget definitions and dashboard filters.- Reading
Widgets[I].DefinitionandWidgets[I].Datafor a status line. - The shared
Atlas.TestVcl.SaveFormImagehelper:LinkedDashboard --image out.pngrenders the form off screen into a PNG and exits.
Build and run
Open LinkedDashboard.dpr in Delphi 13; the IDE creates the project file. Add the Atlas Analytics src folder, the Atlas VCL src folder and the shared folder to the project's search path, then build and run for Win32 or Win64. No packages need to be installed. The shared folder is part of the sample download. It holds Atlas.TestVcl.pas, which the optional snapshot mode uses to save a picture of the window.
Expected output
A 1200 by 860 window with a dark header and a two-column card layout. The
status line reads 6 source rows, 6 widgets, Revenue 73300, Average sale 12216.6666666667, Orders 6. Choosing West changes the revenue KPI to
32,100, leaves one bar in the region chart and two rows in the grid. Add a sale appends a West/Service row and every widget updates. Save layout
writes dashboard-layout.json next to the executable; Load layout rebuilds
the widgets from it.
Adapting it
Replace SalesTable with your own table and change the widget definitions:
the Field of a KPI or chart must be an ackNumber column unless the measure
is amCount, and a chart needs a Dimension. The dashboard has no property
that reads back its filters, so keep the current selection in your own code
(this sample keeps it in the combo box) if you need it after LoadJSON.
Project files
- LinkedDashboard.dpr
- LinkedDashboardForm.pas
- README.md
- shared/Atlas.TestVcl.pas snapshot helper, in the download
The ZIP contains every sample project with its README. See getting started for the search path to add.
Source
LinkedDashboard.dpr
program LinkedDashboard;
// Atlas Analytics: KPI, chart and grid widgets linked to one table.
// Run without arguments for the window. "LinkedDashboard --image out.png"
// renders the form off screen into a PNG and exits, for automated review.
uses
Vcl.Forms,
Atlas.TestVcl,
LinkedDashboardForm in 'LinkedDashboardForm.pas';
var
Form: TLinkedDashboardForm;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TLinkedDashboardForm, Form);
if (ParamCount = 2) and (ParamStr(1) = '--image') then
SaveFormImage(Form, ParamStr(2))
else
Application.Run;
end.
LinkedDashboardForm.pas
unit LinkedDashboardForm;
// A form created in code that links KPI, chart and grid widgets of a
// TAtlasDashboard to one TAtlasTable. A region filter, a new row and a saved
// layout each refresh every widget from the same snapshot.
interface
uses
System.Classes, Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls,
Atlas.Data, Atlas.Dashboard;
type
TLinkedDashboardForm = class(TForm)
private
FSource: TAtlasTable;
FDashboard: TAtlasDashboard;
FRegion: TComboBox;
FStatus: TLabel;
FLayoutFile: string;
procedure RegionChanged(Sender: TObject);
procedure AddSaleClick(Sender: TObject);
procedure SaveLayoutClick(Sender: TObject);
procedure LoadLayoutClick(Sender: TObject);
procedure ShowStatus(const Note: string = '');
public
constructor Create(AOwner: TComponent); override;
property Dashboard: TAtlasDashboard read FDashboard;
end;
function SalesTable(Owner: TComponent): TAtlasTable;
implementation
uses
System.SysUtils, System.IOUtils, Vcl.Graphics, Atlas.Analytics;
function SalesTable(Owner: TComponent): TAtlasTable;
begin
Result := TAtlasTable.Create(Owner);
Result.AddColumn('Region');
Result.AddColumn('Product');
Result.AddColumn('Revenue', ackNumber);
Result.AddColumn('Units', ackNumber);
Result.AddColumn('Booked', ackDate);
Result.ImportCSV(
'Region,Product,Revenue,Units,Booked' + sLineBreak +
'East,Desktop,18000,12,2026-09-01' + sLineBreak +
'West,Desktop,22500,15,2026-09-02' + sLineBreak +
'East,Service,6400,8,2026-09-03' + sLineBreak +
'North,Desktop,12000,8,2026-09-04' + sLineBreak +
'West,Service,9600,12,2026-09-05' + sLineBreak +
'North,Service,4800,6,2026-09-06');
end;
constructor TLinkedDashboardForm.Create(AOwner: TComponent);
var
Header: TPanel;
function AddButton(Left: Integer; const Caption: string; OnClick: TNotifyEvent): TButton;
begin
Result := TButton.Create(Self);
Result.Parent := Header;
Result.SetBounds(Left, 13, 110, 30);
Result.Caption := Caption;
Result.OnClick := OnClick;
end;
begin
inherited CreateNew(AOwner);
Caption := 'Atlas Analytics: linked dashboard';
Width := 1200;
Height := 860;
Position := poScreenCenter;
Font.Name := 'Segoe UI';
Font.Size := 10;
FLayoutFile := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), 'dashboard-layout.json');
Header := TPanel.Create(Self);
Header.Parent := Self;
Header.Align := alTop;
Header.Height := 56;
Header.BevelOuter := bvNone;
Header.Color := $003D2C1E;
Header.ParentBackground := False;
FRegion := TComboBox.Create(Self);
FRegion.Parent := Header;
FRegion.SetBounds(16, 14, 150, 28);
FRegion.Style := csDropDownList;
FRegion.Items.Add('All regions');
FRegion.Items.Add('East');
FRegion.Items.Add('West');
FRegion.Items.Add('North');
FRegion.ItemIndex := 0;
FRegion.OnChange := RegionChanged;
AddButton(182, 'Add a sale', AddSaleClick);
AddButton(300, 'Save layout', SaveLayoutClick);
AddButton(418, 'Load layout', LoadLayoutClick);
FStatus := TLabel.Create(Self);
FStatus.Parent := Header;
FStatus.SetBounds(548, 18, 630, 22);
FStatus.AutoSize := False;
FStatus.Font.Color := clWhite;
// The table is owned by the form; the dashboard only listens to it.
FSource := SalesTable(Self);
FDashboard := TAtlasDashboard.Create(Self);
FDashboard.Parent := Self;
FDashboard.Align := alClient;
FDashboard.Source := FSource;
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awKPI, 'Revenue', 'Revenue'));
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awKPI, 'Average sale', 'Revenue', amAverage));
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awKPI, 'Orders', '', amCount));
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awChart, 'Revenue by region', 'Revenue', amSum, 'Region'));
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awChart, 'Units by product', 'Units', amSum, 'Product'));
FDashboard.AddWidget(TAtlasWidgetDefinition.Create(awGrid, 'Sales detail', ''));
ShowStatus;
end;
procedure TLinkedDashboardForm.RegionChanged(Sender: TObject);
begin
// SetFilters rebuilds every widget from one filtered snapshot. The source
// table keeps its own filter and sort; a bad filter raises and leaves the
// previous filters in place.
if FRegion.ItemIndex > 0 then
FDashboard.SetFilters([TAtlasFilter.Create('Region', FRegion.Text)])
else
FDashboard.SetFilters([]);
ShowStatus;
end;
procedure TLinkedDashboardForm.AddSaleClick(Sender: TObject);
begin
// The dashboard listens to the table, so every widget updates by itself.
FSource.AddRow(['West', 'Service', '1500', '2', '2026-09-07']);
ShowStatus('Added a West/Service sale of 1500');
end;
procedure TLinkedDashboardForm.SaveLayoutClick(Sender: TObject);
begin
// ToJSON persists widget definitions and dashboard filters, not the data.
TFile.WriteAllText(FLayoutFile, FDashboard.ToJSON, TEncoding.UTF8);
ShowStatus('Layout saved to ' + FLayoutFile);
end;
procedure TLinkedDashboardForm.LoadLayoutClick(Sender: TObject);
begin
if not FileExists(FLayoutFile) then
begin
ShowStatus('Save a layout first');
Exit;
end;
// LoadJSON validates every widget against the current source before it
// replaces anything; an invalid file leaves the current widgets in place.
FDashboard.LoadJSON(TFile.ReadAllText(FLayoutFile, TEncoding.UTF8));
ShowStatus('Layout loaded from ' + FLayoutFile);
end;
procedure TLinkedDashboardForm.ShowStatus(const Note: string);
var
Text: string;
Widget: TAtlasDashboardWidget;
I: Integer;
begin
Text := Format('%d source rows, %d widgets', [FSource.RowCount, FDashboard.WidgetCount]);
for I := 0 to FDashboard.WidgetCount - 1 do
begin
Widget := FDashboard.Widgets[I];
if (Widget.Definition.Kind = awKPI) and Assigned(Widget.Data) then
Text := Text + Format(', %s %s', [Widget.Definition.Title, Widget.Data.Cell(0, 0)]);
end;
if FDashboard.LastError <> '' then
Text := 'Last error: ' + FDashboard.LastError
else if Note <> '' then
Text := Note + ' (' + Text + ')';
FStatus.Caption := Text;
end;
end.