Blogs Introducing PVL - the Portable Visual Library If you follow our changelogs, you may have wondered about an acronym that recently keeps appearing there: PVL. This post explains what PVL is, why we built it, and how you can use it in your own DK developed applications. The problem PVL solves The TatukGIS Developer Kernel is available for Delphi (VCL, FMX), .NET (WinForms), Java (Swing), and Python, among other platforms. For years, every dialog window had to be written separately for each platform. That was costly, it slowed development, and because every change had to be rolled out across all platforms simultaneously, it increased the risk of inconsistencies along the way. PVL, the Portable Visual Library, began as a small project to unify dialog windows. It is a GUI abstraction layer: a form is written only once against PVL classes and is rendered through the native library behind the scenes for: VCL, FMX, WinForms, or Swing. The same code, the same functionality, on every platform. From internal tool to public API What began as an internal effort to unify a few of the more important DK dialogs grew until PVL became the main UI platform for DK.Python: the entire Python interface and all Python samples are written in PVL. The single biggest undertaking was the Layer Properties form. Within a few weeks, a new PVL-based form replaced the old one on every platform, which immediately allowed us to extend it with new functionality such as layer restructuring and field rules. Thanks to PVL, the recently released Topology Layer shipped on day one with built-in wizards on all supported platforms. Though PVL was initially meant to remain internal, a growing number of questions from DK users convinced us to make the API public to better support our customers in their cross-platform development efforts. What is available today Nearly all controls related to layer properties have already been ported to PVL: Font, Color, Symbology, Symbology Library, Bitmap, FieldFactor, LegendForm, Statistics, as well as the Grid Wizard, the Vector Wizard, and all the new Topology Layer tools. Porting of the remaining DK and Editor dialogs is ongoing. A few components (ViewerWnd, ViewerBmp, NorthArrow, Scale, 3D, Attributes, Legend, and PrintPreview) are not yet implemented natively in PVL. For these we provide simple proxy classes so that PVL can already serve as a complete, reliable GUI library for your DK application. PVL renders through VCL, FMX, WinForms, and Swing, and is the native UI layer of the Python edition. WPF and MAUI are not supported, and PVL does not apply to the ASP.NET edition (its interface runs in the browser) or to the ActiveX edition. The PVL project is not yet finished. Future plans (not yet scheduled) include a simple form designer. How to use it All PVL components ship with the Developer Kernel and are free for use by every developer with a DK license. You can build forms directly, or inherit from TGIS_PvlForm, TGIS_PvlModalForm, or TGIS_PvlModalWizard to create your own reusable components. Below is the same simple modal form (a label, an edit box, and an OK button) implemented on each platform. Delphi Delphi: declaration unit PVL.MyPvlForm; interface uses PVL.GisPvl, PVL.GisPvlForms; type MyPvlForm = class ( TGIS_PvlForm ) public Text : String ; private oLabel : TGIS_PvlLabel ; oEdit : TGIS_PvlEdit ; oButton : TGIS_PvlButton ; protected procedure DoInitControls ; override; private procedure onClick ( _sender : TObject ); end; implementation // onClick event which is fired when oButton is pressed procedure MyPvlForm.onClick( _sender : TObject ) ; begin // Assign text to a variable which can be accessed outside of the form Text := oEdit.Text ; // Ensure that form closes with Ok result Self.ModalResult := TGIS_PvlModalResult.Ok ; end; procedure MyPvlForm.DoInitControls ; begin // Initialize Form and give it proper size and caption Self.ClientWidth := 350 ; Self.ClientHeight := 100 ; Self.Caption := 'PVL showcase form' ; // Initialize oLabel, give it proper caption as well as place it on the parent element oLabel := TGIS_PvlLabel.Create( Context ) ; oLabel.Caption := 'PVL showcase label' ; oLabel.Place( ClientWidth - 2 * Context.HMargin, 0, nil, Context.HMargin, nil, Context.VMargin ) ; // Initialize oEdit, give it proper caption as well as place it on the parent element oEdit := TGIS_PvlEdit.Create( Context ) ; oEdit.Text := 'PVL showcase edit' ; oEdit.Place( ClientWidth - 2 * Context.HMargin, 0, nil, Context.HMargin, oLabel, Context.VSpace ) ; // Initialize oButton, give it proper caption as well as place it on the parent element oButton := TGIS_PvlButton.Create( Context ) ; oButton.Caption := 'Ok' ; oButton.Place( 100, 0, nil, - Context.VMargin, nil, ClientHeight - oButton.Height - Context.HMargin ) ; // Assign onClick event to onClick property oButton.OnClick := onClick ; end; end. Delphi: usage uses PVL.MyPvlForm in 'PVL.MyPvlForm.pas'; begin // Create form // Parent of the form can be null but its strongly recommended for it to be a native // platform control. var form : MyPvlForm := MyPvlForm.Create( nil ) ; var text : String ; var proc : TGIS_Proc ; // You have to pick one overload of the TGIS_PvlForm.ShowModal // Show form with a proc and free it after usage ( asynchronic version ) // Proc is beeing used to perform action upon closing form. proc := procedure( _modal_result : TGIS_PvlModalResult ) begin if _modal_result <> TGIS_PvlModalResult.Ok then exit ; text := form.Text ; end; form.ShowModal( proc, Assigned( proc ) ) ; // Show form without a proc ( synchronic version ) try if form.ShowModal <> TGIS_PvlModalResult.Ok then exit; text := form.Text ; finally // If we use TGIS_PvlForm.Close we don't need to call free otherwise we have to // form.free ; end; end. .NET (WinForms, C#) C#: form class using TatukGIS.NDK.PVL; namespace TestRendering { class MyPvlForm : TGIS_PvlForm { public String Text; private TGIS_PvlLabel oLabel; private TGIS_PvlEdit oEdit; private TGIS_PvlButton oButton; public MyPvlForm(object _parent) : base(_parent) { } // onClick event which is fired when oButton is pressed private void onClick(object _sender) { // Assign text to a variable which can be accessed outside of the form Text = oEdit.Text; // Ensure that form closes with Ok result this.ModalResult = TGIS_PvlModalResult.OK; } protected override void DoInitControls() { this.ClientWidth = 350; this.ClientHeight = 100; this.Caption = "PVL showcase form"; // Initialize oLabel, give it proper caption as well as place it on the parent element oLabel = new TGIS_PvlLabel(Context); oLabel.Caption = "PVL showcase label"; oLabel.Place(ClientWidth - 2 * Context.HMargin, 0, null, Context.HMargin, null, Context.VMargin); // Initialize oEdit, give it proper caption as well as place it on the parent element oEdit = new TGIS_PvlEdit(Context); oEdit.Text = "PVL showcase edit"; oEdit.Place(ClientWidth - 2 * Context.HMargin, 0, null, Context.HMargin, oLabel, Context.VSpace); // Initialize oButton, give it proper caption as well as place it on the parent element oButton = new TGIS_PvlButton(Context); oButton.Caption = "Ok"; oButton.Place(100, 0, null, -Context.VMargin, null, ClientHeight - oButton.Height - Context.HMargin); // Assign onClick event to onClick property oButton.OnClick = onClick; } } } C#: usage static void Main() { String text; MyPvlForm form = new MyPvlForm(null); // You have to pick one overload of the TGIS_PvlForm.ShowModal // Show form with a proc and free it after usage ( asynchronic version ) // Proc is beeing used to perform action upon closing form. Action<TGIS_PvlModalResult> proc = (res) => { if (res != TGIS_PvlModalResult.OK) return; text = form.Text; }; form.ShowModal(proc, proc != null); // Show form without a proc ( synchronic version ) if (form.ShowModal() != TGIS_PvlModalResult.OK) return; else text = form.Text; Application.Run(); } Python Since DK.Python runs purely on PVL, you inherit and implement your own component directly: Python import tatukgis_pdk as pdk class MyPvlForm(pdk.TGIS_PvlForm): def __init__(self, owner): super().__init__(owner) # Initialize Form and give it proper size and caption self.ClientWidth = 350 self.ClientHeight = 100 self.Caption = "PVL showcase form" # Initialize oLabel, give it proper caption as well as place it on the parent # element self.oLabel = pdk.TGIS_PvlLabel(self.Context) self.oLabel.Caption = "PVL showcase label" self.oLabel.Place( self.ClientWidth - 2 * self.Context.HMargin, 0, None, self.Context.HMargin, None, self.Context.VMargin ) # Initialize oEdit, give it proper caption as well as place it on the parent # element self.oEdit = pdk.TGIS_PvlEdit(self.Context) self.oEdit.Text = "PVL showcase edit" self.oEdit.Place( self.ClientWidth - 2 * self.Context.HMargin, 0, None, self.Context.HMargin, self.oLabel, self.Context.VSpace ) # Initialize oButton, give it proper caption as well as place it on the parent # element self.oButton = pdk.TGIS_PvlButton(self.Context) self.oButton.Caption = "OK" self.oButton.Place( 100, 0, None, -self.Context.VMargin, None, self.ClientHeight - self.oButton.Height - self.Context.HMargin ) # Assign onClick event to onClick property self.oButton.OnClick = self.on_ok def on_ok(self, sender): # Assign text to a variable which can be accessed outside of the form self.oText = self.oEdit.Text # Ensure that form closes with Ok result # As python has no enums we have to emulate it # so to use it we need to create an object first. self.ModalResult = pdk.TGIS_PvlModalResult().OK # Create form # Parent of the form has to be None when using Python form = MyPvlForm(None) # You have to pick one overload of the TGIS_PvlForm.ShowModal def modal_proc(modal_result): if modal_result != pdk.TGIS_PvlModalResult().OK: return Text: str = form.oText # Show form with a proc and free it after usage ( asynchronic version ) # Proc is beeing used to perform action upon closing form. form.ShowModal( modal_proc, modal_proc != None ) try: # Show form without a proc ( synchronic version ) if form.ShowModal() != pdk.TGIS_PvlModalResult().OK: raise SystemExit Text: str = form.oText finally: form.Free() Java Java uses getter and setter methods, since the language has no properties: Java: form class import tatukgis.jdk.pvl.*; public class Form extends TGIS_PvlForm { public String Text; private TGIS_PvlLabel oLabel; private TGIS_PvlEdit oEdit; private TGIS_PvlButton oButton; private TGIS_PvlEvent onClick; public Form(Object _owner) { super(_owner); } @Override public void DoInitControls() { // Initialize Form and give it proper size and caption this.setClientWidth(350); this.setClientHeight(100); this.setCaption("PVL showcase form"); // Initialize oLabel, give it proper caption as well as place it on the parent element oLabel = new TGIS_PvlLabel(getContext()); oLabel.setCaption("PVL showcase label"); oLabel.Place(getClientWidth() - 2 * getContext().getHMargin(), 0, null, getContext().getHMargin(), null, getContext().getVMargin()); // Initialize oEdit, give it proper caption as well as place it on the parent element oEdit = new TGIS_PvlEdit(getContext()); oEdit.setText("PVL showcase edit"); oEdit.Place(getClientWidth() - 2 * getContext().getHMargin(), 0, null, getContext().getHMargin(), oLabel, getContext().getVSpace()); // Initialize oButton, give it proper caption as well as place it on the parent element oButton = new TGIS_PvlButton(getContext()); oButton.setCaption("Ok"); oButton.Place(100, 0, null, -getContext().getVMargin(), null, getClientHeight() - oButton.getHeight() - getContext().getHMargin()); // OnClick event. We cant assign it when we declare it because this function is // called from within the constructor onClick = (Object _sender) -> { // Assign text to a variable which can be accessed outside of the form Text = oEdit.getText(); // Ensure that form closes with Ok result this.setModalResult(TGIS_PvlModalResult.OK); }; // Assign onClick event to onClick property oButton.setOnClick(onClick); } } Java: usage public static void main(String[] args) { // Create a form. // Parent of the form can be null but its strongly recommended for it to be a native // platform control. Form form = new Form(null); // To be able to get values from lambda expression AtomicReference<String> text = new AtomicReference<>(); // You have to pick one overload of the TGIS_PvlForm.ShowModal // Show form with a proc and free it after usage ( asynchronic version ) // Proc is beeing used to perform action upon closing form. Consumer<Integer> proc = (res) -> { if (res != TGIS_PvlModalResult.OK) return; text.set(form.Text); }; form.ShowModal(proc, proc != null); // Show form without a proc ( synchronic version ) if (form.ShowModal() != TGIS_PvlModalResult.OK) return; else text.set(form.Text); } Try it PVL is already in your DK installation: no extra downloads, and no extra cost. Build a form once, compile it on another platform, and the same UI appears. As always, we welcome your questions and feedback. See also our earlier post: Editor PVL Example Script in Python. Posted: July 17, 2026