YOUSEFUL / DELPHI / COMPONENTS
TAtlasDashboard
A scroll box of KPI, chart and grid cards that all show one filtered snapshot of a TAtlasTable and refresh together.
Description
A visual control on the Atlas Analytics palette page. Set Source to a TAtlasTable and add widgets with AddWidget; each widget is a white card with a bold heading and one of three contents, chosen by TAtlasWidgetDefinition:
awKPI: one large number, the measure over all rows, formatted withFormatFloat('#,##0.##')in the user's locale, orNo data.awChart: aTAtlasChartbar chart with one series named after the widget title and one bar per dimension value.awGrid: aTAtlasGridover a read-only copy of the filtered rows, every column.
Linked updates. Every refresh takes one snapshot of the source (its current view, then the dashboard filters from SetFilters) and builds every widget from it before any widget changes, so the cards never disagree. The dashboard listens to the source's change notifications: adding, editing or deleting rows, or changing the table's own filter or sort, refreshes all widgets. If such an automatic refresh fails, the previous data stays on screen and LastError holds the message. Explicit calls (Source, SetFilters, Refresh, AddWidget, LoadJSON) raise instead, and restore the previous state. Freeing the source detaches it and clears widget data. The dashboard never changes the source table.
Layout. Cards are 300 pixels high and equally wide, in ClientWidth div 400 columns (at least one), with 16-pixel gaps, in the order they were added; the box scrolls vertically. At most 64 widgets. There is no drag-and-drop layout designer, no per-card size and no chart kind other than bars.
Persistence. Only Source is published; widgets are not stored with the form. Add them in code or with LoadJSON. ToJSON saves widget definitions and dashboard filters, never the data.
Use the dashboard from the main thread, like any VCL control.
Members
| Member | Kind | What it does |
|---|---|---|
Create | constructor | Creates an empty dashboard, 1000 by 650 pixels, without a border, with a light background ($00F7F4F0), no horizontal scroll bar and a tracking vertical scroll bar. |
Destroy | destructor | Stops listening to the source and frees every widget with its controls and data. |
AddWidget | method | Validates the definition, builds its data from the current filtered snapshot, creates its card after the existing ones and returns the widget, which the dashboard owns. |
ClearWidgets | method | Frees every widget and its card. |
SetFilters | method | Replaces the dashboard filters and refreshes every widget. |
Refresh | method | Rebuilds every widget from a new snapshot of the source. |
ToJSON | method | Returns the layout as a version 1 JSON object: widgets (each kind, title, field, dimension and measure, with enumerations as ordinals) and filterDefinition, a TAtlasReportDefinition JSON object that carries the dashboard filters. |
LoadJSON | method | Replaces the widgets and filters with a layout written by ToJSON. |
WidgetCount | property | The number of widgets, from 0 to 64. |
Widgets | property | The widget at Index, from 0 to WidgetCount - 1, in the order added; an index outside that range raises an exception. |
LastError | property | The message of the last automatic refresh (after a source change) that failed, while the widgets kept their previous data. |
Source | property | The table every widget reads. |
Constructors
Create constructor
constructor Create(AOwner: TComponent); override;Creates an empty dashboard, 1000 by 650 pixels, without a border, with a light background ($00F7F4F0), no horizontal scroll bar and a tracking vertical scroll bar. Set Parent (usually with Align := alClient), then Source and the widgets.
Dashboard := TAtlasDashboard.Create(Self);
Dashboard.Parent := Self;
Dashboard.Align := alClient;Destructors
Destroy destructor
destructor Destroy; override;Stops listening to the source and frees every widget with its controls and data. The source table is not freed.
Properties
WidgetCount property
property WidgetCount: Integer read GetWidgetCount;The number of widgets, from 0 to 64.
Widgets property
property Widgets[Index: Integer]: TAtlasDashboardWidget read GetWidget;The widget at Index, from 0 to WidgetCount - 1, in the order added; an index outside that range raises an exception. The dashboard owns the widgets: read their Definition and Data, but do not free or change them.
Writeln(Dashboard.Widgets[0].Definition.Title, ' = ', Dashboard.Widgets[0].Data.Cell(0, 0));LastError property
property LastError: string read FLastError;The message of the last automatic refresh (after a source change) that failed, while the widgets kept their previous data. Empty after any successful refresh. Explicit calls raise instead of setting it.
if Dashboard.LastError <> '' then
StatusBar.SimpleText := Dashboard.LastError;Source property
property Source: TAtlasTable read FSource write SetSource;The table every widget reads. Setting it refreshes all widgets from the new table and subscribes to its change and free notifications; if the refresh fails the previous source is kept and the exception is raised. Set nil to clear widget data. The dashboard does not own the table. Published, so it can be linked to a table on the same form in the designer.
Dashboard.Source := Sales;Methods
AddWidget method
function AddWidget(const Definition: TAtlasWidgetDefinition): TAtlasDashboardWidget;Validates the definition, builds its data from the current filtered snapshot, creates its card after the existing ones and returns the widget, which the dashboard owns. Raises EAtlasAnalyticsError for more than 64 widgets (Dashboard is limited to 64 widgets), a blank title or an out-of-range kind or measure (Invalid dashboard widget), a chart without a dimension (A chart needs a dimension), an unknown field or a numeric measure on a non-numeric field, and a chart value above 1E100 in magnitude. A chart whose title equals its dimension name raises EAtlasDataError (duplicate column name), because the title is the measure column caption. On failure nothing is added. Without a Source the widget is added with no data.
Dashboard.AddWidget(TAtlasWidgetDefinition.Create(awChart, 'Revenue by region', 'Revenue', amSum, 'Region'));ClearWidgets method
procedure ClearWidgets;Frees every widget and its card. Source and the dashboard filters are kept.
SetFilters method
procedure SetFilters(const Filters: array of TAtlasFilter);Replaces the dashboard filters and refreshes every widget. The filters apply to the snapshot only; the source table's own filter and sort are not touched. An empty array removes all filters. If the refresh fails (unknown field, a value that does not fit the column, afContains on a non-text field) the previous filters and widget data are restored and the exception is raised. There is no property that reads the filters back; ToJSON contains them.
Dashboard.SetFilters([TAtlasFilter.Create('Region', 'West')]);Refresh method
procedure Refresh;Rebuilds every widget from a new snapshot of the source. Called automatically after the source changes; call it yourself only to retry after LastError was set. On success LastError is cleared; on failure the exception is raised and the widgets keep their previous data. This method hides TControl.Refresh: on a TAtlasDashboard reference it rebuilds data, not just the painting.
ToJSON method
function ToJSON: string;Returns the layout as a version 1 JSON object: widgets (each kind, title, field, dimension and measure, with enumerations as ordinals) and filterDefinition, a TAtlasReportDefinition JSON object that carries the dashboard filters. The source and its data are not included.
{"version":1,"widgets":[{"kind":0,"title":"Revenue","field":"Revenue","dimension":"","measure":1}],"filterDefinition":{"version":1,"title":"Atlas report","subtitle":"","fields":[],"dimensions":[],"measures":[],"filters":[]}}LoadJSON method
procedure LoadJSON(const Text: string);Replaces the widgets and filters with a layout written by ToJSON. Everything is validated first: the structure (version 1, at most 64 widgets, valid kinds and measures, a valid filterDefinition) and, when a Source is assigned, every widget is built against the source with the new filters. Only then are the old widgets replaced. On failure it raises EAtlasAnalyticsError (or EAtlasDataError for a bad filter value) and the current widgets and filters are unchanged.
Dashboard.LoadJSON(TFile.ReadAllText('layout.json', TEncoding.UTF8));Examples
Dashboard on a form
Two KPI cards, a bar chart and a detail grid bound to one table; every card refreshes when the table changes.
uses Vcl.Forms, Vcl.Controls, Atlas.Data, Atlas.Analytics, Atlas.Dashboard;
function CreateSalesDashboard(Form: TForm; Sales: TAtlasTable): TAtlasDashboard;
begin
Result := TAtlasDashboard.Create(Form);
Result.Parent := Form;
Result.Align := alClient;
Result.Source := Sales;
Result.AddWidget(TAtlasWidgetDefinition.Create(awKPI, 'Revenue', 'Revenue'));
Result.AddWidget(TAtlasWidgetDefinition.Create(awKPI, 'Orders', '', amCount));
Result.AddWidget(TAtlasWidgetDefinition.Create(awChart, 'Revenue by region', 'Revenue', amSum, 'Region'));
Result.AddWidget(TAtlasWidgetDefinition.Create(awGrid, 'Sales detail', ''));
end;
Filter every widget at once
An empty region shows all rows; the source table keeps its own filter.
uses Atlas.Analytics, Atlas.Dashboard;
procedure ShowRegion(Dashboard: TAtlasDashboard; const Region: string);
begin
if Region = '' then
Dashboard.SetFilters([])
else
Dashboard.SetFilters([TAtlasFilter.Create('Region', Region)]);
end;
Save and restore the layout
The JSON holds widget definitions and filters; assign Source before loading so the widgets are checked against the data.
uses System.SysUtils, System.IOUtils, Atlas.Dashboard;
procedure SaveLayout(Dashboard: TAtlasDashboard; const FileName: string);
begin
TFile.WriteAllText(FileName, Dashboard.ToJSON, TEncoding.UTF8);
end;
procedure RestoreLayout(Dashboard: TAtlasDashboard; const FileName: string);
begin
if FileExists(FileName) then
Dashboard.LoadJSON(TFile.ReadAllText(FileName, TEncoding.UTF8));
end;
Report a failed automatic refresh
After a source change that could not be shown, the widgets keep the previous data and LastError says why.
uses Vcl.StdCtrls, Atlas.Dashboard;
procedure ShowDashboardState(Dashboard: TAtlasDashboard; Status: TLabel);
begin
if Dashboard.LastError <> '' then
Status.Caption := 'Dashboard not updated: ' + Dashboard.LastError
else
Status.Caption := 'Dashboard up to date';
end;
Related
TAtlasWidgetDefinition · TAtlasDashboardWidget · TAtlasFilter · TAtlasReportDefinition · TAtlasTable · TAtlasChart · TAtlasGrid