Blog Options


<< June 2019 >>



Most Recent Post  |  Next Post  |  Previous Post  |  Index List

TMS WEB Core v1.2 tips & tricks part 8: 3D Charts made easy


Friday, June 28, 2019

One of the big new features in TMS WEB Core v1.2 Padua is the built-in support for 3D graphics. This is accomplished by providing Object Pascal components and classes that wrap the powerful ThreeJS / WebGL libraries for rendering 3D graphics in the browser. This technology is meanwhile supported on every mainstream browser, including on mobile devices.

As always, the goal of TMS WEB Core is to offer OO RAD Component based we development, so, integrating 3D graphics in your TMS WEB Core web client applications becomes a piece of cake. So, let's immediately jump to writing the code as an inspiration to get you started exploring the wonderful 3D web world.

When interfacing with an existing JavaScript libraries is done from a TMS WEB Core application, we have made it equally easy to setup the link references to these libraries. In case of 3D we will need the ThreeJS library. So, from the project context menu in the Delphi IDE select "Manage JavaScript Libraries" and from there check to use ThreeJS.

Next, put a TWebThreeJSChart on the form and you can start defining series for your chart. Since climate change is a bit a "hot" topic (pun intended) in the last years everywhere, for our sample we got the idea to render the average month temperatures in Belgium (where our main TMS office is located) for the past 3 years. We got the data from a website that lists the temperature measurements since 1833. You can find the data here.

The series we want to display are a series with all months of the year and a series for years. A series is of the type TThreeJsChartSeries and is created with the X and Y axis labels via:

  aSeries: TThreeJsChartSeries;

  aSeries := TThreeJsChartSeries.Create(
   TJSArray.New('2017', '2018', '2019'));
As you can see, we can create in Object Pascal a dynamic JavaScript array that ThreeJS needs via TJSArray.New(values).

Next, let's add values for each month for the 3 years to the series. This is done for a month via:

  aSeries.addLegendRow('Jan', TJSArray.New(1.1, 6.0, 3.0));

Other than this, all we need is to setup things like title, X-axis label, Y-axis label, Z-axis label, Z-axis maximum and step value and the formatting of the values along the Z-Axis. When this is ready, to display the series, we simply clear any possible previous series that were setup and then assign the series created and call WebThreeJsChart.createChart.

One more property available in a series is the property TThreeJsChartSeries.ChartType. The 3D chart supports following types: ctBarChart, ctCylinderChart, ctConeChart, ctLineChart, ctAreaChart.

The total code becomes:

procedure TForm1.InitChart(AType: TThreeJSChartType);
  aSeries: TThreeJsChartSeries;
  aSeries := TThreeJsChartSeries.Create(
    TJSArray.New('2017', '2018', '2019')

  aSeries.addLegendRow('Jan', TJSArray.New(1.1, 6.0, 3.0));
  aSeries.addLegendRow('Feb', TJSArray.New(6.1, 0.8, 7.0));
  aSeries.addLegendRow('Mar', TJSArray.New(9.6, 5.4, 8.5));
  aSeries.addLegendRow('Apr', TJSArray.New(8.8, 13.0, 11.0));
  aSeries.addLegendRow('May', TJSArray.New(15.5, 16.3, 12.0));
  aSeries.addLegendRow('Jun', TJSArray.New(19.2, 18.1, 0));
  aSeries.addLegendRow('Jul', TJSArray.New(18.3, 22.0, 0));
  aSeries.addLegendRow('Aug', TJSArray.New(18.1, 19.4, 0));
  aSeries.addLegendRow('Sep', TJSArray.New(14.1, 15.4, 0));
  aSeries.addLegendRow('Oct', TJSArray.New(13.3, 12.6, 0));
  aSeries.addLegendRow('Nov', TJSArray.New(6.6, 7.4, 0));
  aSeries.addLegendRow('Dec', TJSArray.New(4.4, 5.8, 0));

  aSeries.valueAxisMarkMaximum := 30;
  aSeries.valueAxisMarkStep := 5;
  aSeries.ChartType := AType;
  aSeries.valueFormatFloat := '#,##0°C';
  aSeries.Title := 'Average month'#13#10'temperatures Belgium';
  aSeries.ValueAxisTitle := 'Temp';
  aSeries.LegendAxisTitle := 'Months';
  aSeries.CategoryAxisTitle := 'Year';

  WebThreeJsChart1.Series := aSeries;

Finally, we want to add one more additional tip and that is a frequently asked question how we can easily center a control in the browser window using pure Object Pascal code instead of what is in web development more commonly done using HTML/CSS. Well, the good news is that in Object Pascal this is also very easy. Add an event hanlder for Form.OnResize and write the code:

procedure TForm1.WebFormResize(Sender: TObject);
  if WebThreeJsChart1.Width < Width then
    WebThreeJsChart1.Left := (Width - WebThreeJsChart1.Width) div 2;

This results in the chart with the numbers we have till now, May 2019:

or better discover it live here

There is much more than doing just 3D charts with ThreeJS and TMS WEB Core, there are also samples for creating surface charts, show 3D model files or setup your own 3D scenes. Discover these and more exciting features in TMS WEB Core that let you create web client applications with unprecedented capabilities.

You can download the trial version, go ahead with the standalone version you purchased or with TMS WEB Core and additional tools that are all included in TMS ALL-ACCESS. Our team looks forward to all your comments, feedback, feature-requests to help steering the development of TMS WEB Core to the next stages!

Bruno Fierens


This blog post has received 2 comments. Add a comment.

TMS WEB Core v1.2 tips & tricks part 7: Get Base64 encoding for images


Thursday, June 27, 2019

Not so long ago, I posted about how to convert an image into a Base64 string. Today, I will show you that using TMS Web Core, this is even easier.

If we look into the source code of TMS Web Core, we find that the custom image control adds a public property called Base64Image of type string. That means, we can read the Base64 string of an image using that property. However, this is a read-only property. You cannot assign a Base64-string to it in order to assign image data. I will show a different approach how to work with Base64-strings for images in one of my next posts.

  TCustomImageControl = class(TCustomControl)
    // ...
    // ...
    property Base64Image: string read GetBase64Img;

Let’s try this functionality. Drop (1) a TWebImage (WebImage) and a (2) TWebMemo (WebMemo) on the form.

Implement the OnCreate event of the form as follows to load an image and assign its Base64 representation to the memo:

procedure TForm1.WebFormCreate(Sender: TObject);
  WebImage.URL :=
    '' +
  WebMemo.Lines.Text := WebImage.Base64Image;

Running the application results in the following app in the web browser:

Note that we can also assign a URL to the web image component in order to load an image.

Holger Flick


This blog post has received 4 comments. Add a comment.

TMS WEB Core v1.2 tips & tricks part 6: Exploring new TWebDBGrid features


Wednesday, June 26, 2019

In TMS WEB Core v1.2 Padua, several new features have been added to the TWebDBGrid. This is a DB-aware grid for TMS WEB Core web client applications. Note that TMS WEB Core features a databinding model that is similar to the VCL databinding. There is the concept of a TDataSet, TDataSource and for a DB-aware TWebDBGrid, databinding is done by setting TWebDBGrid.DataSource := yourDataSource.

So, what is new in TWebDBGrid in TMS WEB Core v1.2:

  • A new DB row indicator is added. This row indicator not only shows the active record but also the dataset state.
  • New column types: cdCheck, cdLink, cdExternalLink, cdRadio, cdImage other than the default cdText type
  • Column sorting and sort direction indication in the column header

Together with many improvements in dataset handling itself, this opens up for more powerful DB applications with a minimum amount of code.

Row indicator

When grid.FixedCols = 1 and grid.ShowIndicator = true, the first fixed column is used to show a row and dataset state indicator. This is the standard triangle when the dataset is in browse state and a stylus when the dataset is in editing state

Column types

Now, a column can automatically display checkboxes, radiobuttons, hyperlinks or images without adding any code. When the column type is set to cdCheck, the column will render as a checkbox. The checkbox state will be checked when the DB field value is Y,TRUE or 1 and as expected it will be unchecked for N,FALSE or 0. Note that when the grid is not editable, the checkbox will be displayed disabled. When the grid is editable, a click on the checkbox will trigger the event OnCheckClick from where further actions could be done.
When the column type is set to cdImage, the DB field value is consider the hyperlink to the image and the grid will automatically render this as an image. Note that when the DB field does not contain the full correct hyperlink, the DBField.OnGetText event handler can be used to return a full image link. Finally, there is also the cdLink, cdExternalLink column type. When this type is chosen, the DB field text is rendered as a hyperlink with or without the target attribute respectively. Also here, when the DB field doesn't store a fully qualified hyperlink, the DBField.OnGetText event handler can be used to transform the DB field text value to a fully qualified hyperlink.

Column sorting

Column sorting is triggered from a column header click. The column header click will trigger the event OnFixedCellClick. From this event, code can be added to sort the dataset and to show a sort indicator in the column header.

To set a sort indicator in a column header, the property grid.Columns[Column].SortIndicator can be used. The values can be siNone, siAscending, siDescending.

Putting it all together

In the new TWebDBGrid, these new capabilities are demonstrated. For a quick setup and convenience, the grid is bound to a dataset at URL: This dataset contains a column with images, one with an URL and one with a checkbox. At designtime, set the URL to WebClientConnection.URL and connect a WebClientDataSet.Connection to WebClientConnection. After this, right-click the dataset and select "Fetch fields". This will at design time fetch the JSON and extract the columns information. When this is done, the columns you want ot show in the grid can be added to the TWebClientDataSet fields collection by opening the editor for the TWebClientDataSet context menu and adding the desired fields from there. Then connect to TWebClientDataSet to the TWebDataSource and the TWebDataSource to TWebDBGrid.DataSource and the columns will be displayed.

To show pictures for the Picture column, set for this column Column.DataType = cdImage, for the checkbox column set it to cdCheck and for the URL column set it column.DataType to cdExternalLink.

Note that in the sample JSON, the image is a simple short image name, not a fully qualified URL. We use the DBField.OnGetText to make it a proper real & fully qualified URL:

procedure TForm1.WebClientDataSet1PictureGetText(Sender: TField;
  var Text: string; DisplayText: Boolean);
  Text := '' + StringReplace(Sender.AsString, '.jpg', '_96.jpg', [rfReplaceAll]);

Similar for the URL column that doesn't contain by default a HTTP prefix:

procedure TForm1.WebClientDataSet1URLGetText(Sender: TField; var Text: string;
  DisplayText: Boolean);
  Text := 'https://' + Sender.AsString;

Now, let's finish by adding column sorting. The column sorting is triggered from the OnFixedCellClick event handler.

In this event handler, we basically figure out the column clicked, determine the sort order (i.e. toggle on each click) and added the index to the TWebClientDataSet and make the index active:

This code is:

procedure TForm1.WebDBGrid1FixedCellClick(Sender: TObject; ACol, ARow: Integer);
  LIndex: string;
  desc: boolean;
  opt: TIndexOptions;
  if ACol = 0 then

  Lindex := 'By'+ WebDBGrid1.Columns[ACol - 1].DataField;

  // toggle sort order when the same column is clicked
  desc := SameText(webclientdataset1.ActiveIndex, LIndex);

  if desc then
    LIndex := LIndex + 'Desc';

  opt := [];

  if desc then
    opt := opt + [siDescending];

  // clear any previous indexes and add the new index to sort
  webclientdataset1.Indexes.Add(LIndex, WebDBGrid1.Columns[ACol - 1].DataField, opt);
  webclientdataset1.ActiveIndex := LIndex;

  // set the sort indicator in the grid column header
  if ixDescending in webclientdataset1.Indexes.Find(Lindex).Options then
    WebDBGrid1.Columns[ACol - 1].SortIndicator := siDescending
    WebDBGrid1.Columns[ACol - 1].SortIndicator := siAscending;

  // force the grid to update
  WebDataSource1.DataSet :=  WebClientDataSet1;
Live, this shows in Chrome like:

or better, explore and play with the live demo.

Discover these and more exciting features in TMS WEB Core that let you create web client applications with unprecedented capabilities.

You can download the trial version, go ahead with the standalone version you purchased or with TMS WEB Core and additional tools that are all included in TMS ALL-ACCESS. Our team looks forward to all your comments, feedback, feature-requests to help steering the development of TMS WEB Core to the next stages!

Bruno Fierens


This blog post has not received any comments yet. Add a comment.

TMS WEB Core v1.2 tips & tricks part 5: Accessing microphone and camera


Tuesday, June 25, 2019

Over the years, increasingly new web APIs have given web developers access to areas of the operating system that were originally only accessible from native applications. This is also the case with the microphone and camera that is attached to a desktop computer or integrated in laptops, tablets and smartphones.

As the main vision and mission of TMS WEB Core is to bring OO RAD component based development for web client applications, it is expected that TMS WEB Core also brings easy to use components for using your device microphone or cameras (yes that is plural as it is possible to select between front and back cameras on a mobile device for example) from a TMS WEB Core application.

Security / privacy

An important first remark to make is that before a web client application can access a microphone or camera, user authorization is required. So, clearly, no web client application can secretly in the back listen to your conversations or watch what you are doing! Together with this authorization is also the requirement that the web application comes from a HTTPS URL so there is a reliable identification of the author or company hosting the application possible. So, when your TMS WEB Core application runs from a HTTPS domain and you want to access a microphone or camera, the user will see a prompt to allow this access:


To use the microphone, we have introduced the non-visual component TWebMediaCapture. This component can be used to capture audio via the microphone when TWebMediaCapture.Capture is set to mctAudio. The standard recording mode is mrmManual. This means that recording is started by calling WebMediaCapture.Start and the recording is stopped by calling WebMediaCapture.Stop. After the recording is stopped, the data that was recorded is asynchronously returned via the event OnStopCapture that has the event signature:

TMediaCaptureCloseEvent = procedure(Sender: TObject; ABinary: TJSUint8Array; ABase: string) of object;
This event returns the audio via a JavaScript array or as a base64 encoded string. An important remark is that different browsers might have a different default audio file format. In the Chrome browser this is the webm format for example, in Firefox it is ogg. With this component, we have created a TMS WEB Core dictaphone app. You can discover this application here:

The code to start and stop the recording is simply done from a click on an image:

procedure TForm1.WebImageControl1Click(Sender: TObject);
  if not FRecording then
    FRecording := True;
    WebImageControl1.URL := GetStopImage;
    FRecording := False;
    WebImageControl1.URL := GetRecordImage;

The code to capture the recorded data is here:

procedure TForm1.WebMediaCapture1StopCapture(Sender: TObject;
  ABinary: TJSUint8Array; ABase: string);
  if WebIndexedDbClientDataset1.RecordCount > 0 then
    WebIndexedDbClientDataset1.RecNo := 1;

  WebIndexedDbClientDataset1.FieldByName('descr').AsString := WebEdit1.Text;
  WebIndexedDbClientDataset1.FieldByName('time').AsDateTime := Now;
  WebIndexedDbClientDataset1.FieldByName('base').AsString := ABase;
  WebEdit1.Text := 'SoundClip ' + IntToStr(WebIndexedDbClientDataset1.RecordCount + 1);

This code shows another interesting concept, that is to store the recorded audio in an IndexedDB dataset. That is a dataset hosted in your browser which is private and accessible at a later time, so you can listen again to captured audio snippets when you open the web application again at a later time.

One more interesting option is to let the microphone record automatically when a certain audio level is reached. To use the TWebMediaCapture this way, set WebMediaCapture.RecordingMode to mrmAudio. With the properties WebMediaCaptature.Sensitivity, WebMediaRecorder.FFTSize, WebMediaRecorder.SmoothingTimeConstant it can be controlled at what noise level and what duration of the noise level the recording will start and stop.


Thanks to the new TWebCamera component, taking pictures from a TMS WEB Core web client application is equally easy. Drop the component on the form and start the camera by calling WebCamera.Start. To take a picture, call one of the three properties depending on the format you wish for the captured image data:

property WebCamera.SnapShotAsBase64: string;
property WebCamera.SnapShotAsImageData: TJSImageData;
property WebCamera.SnapShotAsUint8Array: TJSUint8Array;
A very simple use is to call WebCamera.SnapShotAsBase64 and assign the result to a HTML image element, i.e. what is wrapped in TWebImageControl via:

WebImageControl1.URL := WebCamera1.SnapShotAsBase64;
The image format returned van be selected between JPEG or PNG with WebCamera.BaseFormat.

As mentioned, TWebCamera can handle multiple cameras. To select a camera, the property WebCamera.CameraType can be used that can select between rear, front or default camera. To detect the cameras available, the TWebCamera.CameraDevices collection can be used. You can use:

for i := 0 to WebCamera.CameraDevices.Count - 1 do

For camera & microphone use, we have published 3 demos you can discover at: There is even a camera demo that integrates with a QR and barcode scanning library so you can detect and decode pictures taken from a QR code or barcode. Of course, the TMS WEB Core distribution contains the full source code of these demos

Discover these and more exciting features in TMS WEB Core that let you create web client applications with unprecedented capabilities.

You can download the trial version, go ahead with the standalone version you purchased or with TMS WEB Core and additional tools that are all included in TMS ALL-ACCESS. Our team looks forward to all your comments, feedback, feature-requests to help steering the development of TMS WEB Core to the next stages!

Bruno Fierens


This blog post has not received any comments yet. Add a comment.

Something I forgot to say about TMS XData 4.5 regarding URL endpoints


Monday, June 24, 2019

Well, actually I didn't forget it. I was just not blog about it.

But since we got a very positive feedback in the last post about new features in TMS XData 4.5, I decided to show you an extra thing, somewhat related to the parameter binding feature: it's the new EnableKeyAsSegment property.

Basically it controls how the endpoint URL for entities will look like. Let's take the SQLiteConsoleServer demo as an example. It automatically publishes all tables in the database without a single line of code, as Aurelius entities. If you run the demo and navigate to the swaggerui address you will see the available endpoints. In the screeshot below we can see the ones for Album entity:

Now let's see what happens when we set EnableKeyAsSegment property to True:

  XDataServer1.EnableKeyAsSegment := True;
If you recompile, run the demo again and refresh the browser page to update SwaggerUI interface, here is what you get:

Did you notice the difference?

Yep, the URL format was /Album(2) - the id enclosed in parenthesis - and now it is /Album/2. Everything works just as before, and both URL are valid REST addresses, but having the ID in the path is a more common approach out there and might be easier for you to integrate with existing non-Delphi frameworks.

For entities with composite keys, you put one id in each path segment. For example, suppose you have an entity Order which is identified by CustomerId (string) and OrderNo (integer). Instead of using the URL /Order(AFKLI,2) you can use /Order/AFKLI/2.

As one final note, when you enble the new URLs, the old ones are still valid. It's not a replacement, but an addition. The exception is in the SwwaggerUI itself, which hides the URLs using parenthesis to not clutter the UI too much.

Hope you liked this one, two!

Wagner R. Landgraf


This blog post has received 2 comments. Add a comment.

TMS WEB Core v1.2 tips & tricks part 4: Display another form as a pop-up

Bookmarks:  Creating a second form is easy and has been shown in the last post. However, sometimes we might not want to take the other form to take up the whole screen but make it a pop-up window instead.

As it’s an amazing framework from TMS Software (disclaimer: this statement might be biased), making a new form to display as a pop-up is really easy:

  LFrm := TFrmSettings.CreateNew( @AfterCreate );
  // LFrm als Pop-Up festlegen
  LFrm.Popup := true;
  // Transparenz definieren
  LFrm.PopupOpacity := 0.2;
  LFrm.ShowModal( @AfterShowModal )

The call to CreateNew is no different than for any other form. As an alternative, we just use the TWebForm constructor this time instead of TApplication. However, we can make the necessary property changes after calling the constructor. Be aware, that this is indeed possible in the same method. We do not have to do this in AfterCreate as it affects the object on the application side and has no impact on the creation for the window.

In order to make the new form a pop-up, we set the property Popup to true. We also specify an opacity for the form. 0 means it is completely transparent. 1 means no transparency at all.

We then show the form calling ShowModal. Yet again, this is possible as it tells the framework to show the form ‘when ready’. This means, AfterCreate will be called after creation and as soon as the form is ready to be shown, it will be shown. The method AfterShowModal will server as a means to get values from the pop-up window after its been closed.

That’s how easy it is to display a pop-up window using TMS Web Core.

Holger Flick


This blog post has not received any comments yet. Add a comment.

Most Recent Post  |  Next Post  |  Previous Post  |  Index List