Difference between revisions of "Standalone Executables on Windows"

From Lianjapedia
Jump to: navigation, search
(Lianja Standalone App Debugger)
(Login)
Line 444: Line 444:
  
 
==Login==
 
==Login==
 +
From v9.5 standalone Apps can require login.
 +
* [[Form]].requireslogin property
 +
* Fully customizable login form
 +
 
''Coming Soon''
 
''Coming Soon''
  

Revision as of 09:24, 26 March 2024

See Also

Deployment, Lianja Package Manager, Packaging Lianja Desktop Apps for Windows

Overview

From Lianja 7.0 developers can build standalone executables on Windows.

These executables can only be built for Apps that have been hand-coded in LianjaScript, Python, JavaScript, TypeScript or Babel using the Lianja UI Framework.

Alternatively, you can also visually build forms in the App Builder that use any of the following scripting languages as the Lianja UI framework is scripting language independent across Desktop, Web and Mobile Apps.

- LianjaScript/VFP

- Python

- JavaScript

- Typescript

Forms are based on a tabview section which contains canvas, grid or webview based sections in its tabs. See the blog article Working with forms in Lianja for further details.

This functionality is primarily for building small GUI Apps, not for business Apps running under the Lianja App Center with Users and Roles.

Sample App: example_timeclock

The example_timeclock sample App is included in the Lianja App Builder distribution from v7.0.0 in the 'General' category.

This app uses the new Camera UI Framework class and is a small App to handle clocking in and out of employees in a company.

Sample App: example_timeclock


Feel free to enhance this to suit your own needs by adding support for storing the clock in/out date and time along with the employees photo.

Building Standalone Executables

Scripting Language

Select the scripting language in the App Settings:

Scripting Language


Standalone

Check 'Standalone' in the UI Presentation Rules App Settings.

UI Presentation Rules and Standalone Options


Standalone Lib

From v9.5, checking the 'Standalone Lib' setting (in 'Standalone Options' shown in the screenshot above) will include library files. When the standalone app is run the library directory is set to the standalone App library directory.

The following text files are used to specify which library files should be included:

dependencies.txt

This is generated automatically when the App is built if the App contains any WebView Based Sections.

For example, the 'Tabbed Form' (form2) App, includes the following sections:

and its dependencies.txt is generated as:

@articleview
@catalogview
@commentsview
@carouselview
@calendar

The '@sectiontype' format references the corresponding 'sectiontype-dependencies.txt' file in the library containing the names of any required files and directories, e.g. catalogview-dependencies.txt:

bootstrap-3.3.4
jquery-1.10.2
catalogview.*

includes.txt

The 'includes.txt' file is used to specify any additional files that should be included.

As in 'dependencies.txt' these can be:

  • folder names
  • individual file names
  • wildcards (like catalogview.*)
  • @filename to include the contents of '@filename-dependencies.txt'

Standalone Zip

From v9.5, checking the 'Standalone Zip' setting (in 'Standalone Options' shown in the screenshot above) will create a zip file for the standalone App.

Standalone Exe

From v9.5, checking the 'Standalone Exe' setting (in 'Standalone Options' shown in the screenshot above) will create an installer as a self-extracting exe file for the standalone App. This can be unzipped / installed on a target machine.

Standalone Data

From v9.5, specifying a comma separated list of databases in the 'Standalone Data' setting (in 'Standalone Options' shown in the screenshot above) will copy the databases into the standalone App directory. When the standalone app is run the data directory is set to the standalone App data directory.

Setup

From v9.5, if the file setup.prg (or setup.py or setup.js) is contained within the App it is executed before the standalone App is run. You can check to see if the App is running standalone using Lianja.standalone and check the file path using Lianja.standalonefilepath.

You would typically do nothing in setup if the App is not running standalone.

if not Lianja.standalone
    return
endif
 
// e.g. to share data on a network 
set datadir to f:\lianja\data

Script

Then write the 'main' script with the same name as the App. In the case of the sample example_timeclock App, this is example_timeclock.prg or example_timeclock.js or example_timeclock.py (see code below). You can switch the 'Scripting language' in the App Settings to test each one.

LianjaScript/Visual FoxPro

Note the READ EVENTS at the end of the code which is needed for a standalone App.

example_timeclock.prg

local oform = createObject("Form")
oform.resize(600, 600)
oform.closable = 1
oform.caption = "Time clock - LianjaScript"
oform.minwidth = 600
oform.minheight = 600
 
oform.addObject("ocont", "container")
ocont.margin = 10
ocont.autosize = 1
ocont.layout = "V"
ocont.backcolor = "black"
 
ocont.addObject("obadgenolab", "label")
obadgenolab.fixedheight = 24
obadgenolab.autowidth = 1
obadgenolab.alignment = "center"
obadgenolab.caption = "Enter Badge Number"
obadgenolab.backcolor = "darkgray"
obadgenolab.forecolor = "white"
obadgenolab.fontbold = 1
obadgenolab.fontsize = 10
 
ocont.addspacing(10)
ocont.addObject("obadgeno", "textbox")
obadgeno.fixedheight = 24
obadgeno.autowidth = 1
obadgeno.placeholder = "Enter your badge number, Take a Photo then CLOCK IN or CLOCK OUT"
 
ocont.addspacing(10)
ocont.addObject("obadgenolab2", "label")
obadgenolab2.fixedheight = 24
obadgenolab2.autowidth = 1
obadgenolab2.alignment = "center"
obadgenolab2.caption = "Take a Photo"
obadgenolab2.backcolor = "darkgray"
obadgenolab2.forecolor = "white"
obadgenolab2.fontbold = 1
obadgenolab2.fontsize = 10
 
ocont.addspacing(10)
ocont.addObject("ocamera", "camera")
ocamera.autowidth = 1
ocamera.autoheight = 1
ocamera.backcolor = "black"
 
ocont.addspacing(10)
ocont.addObject("obadgenolab3", "label")
obadgenolab3.fixedheight = 24
obadgenolab3.autowidth = 1
obadgenolab3.alignment = "center"
obadgenolab3.caption = "Clock in / Clock out"
obadgenolab3.backcolor = "darkgray"
obadgenolab3.forecolor = "white"
obadgenolab3.fontbold = 1
obadgenolab3.fontsize = 10
 
ocont.addObject("ocont2", "container")
ocont2.layout = "H"
ocont2.fixedheight = 60
ocont2.backcolor = "black"
ocont2.spacing = 10
 
ocont2.addObject("obtn1", "commandbutton")  
obtn1.resize(60, 50)
obtn1.flat = 1
obtn1.fixedheight = 50
obtn1.caption = "CLOCK IN"
obtn1.stylesheet = "btn"
ocont2.addObject("obtn2", "commandbutton")  
obtn2.flat = 1
obtn2.resize(60, 50)
obtn2.fixedheight = 50
obtn2.stylesheet = "btn"
obtn2.caption = "CLOCK OUT" 
 
// event handlers
proc clockin()
	ocamera.takePhoto()
	thisform.showSuccessMessage("You clocked in on " + dtoc(date()) + " at " + ampm())
endproc
 
proc clockout()
	ocamera.takePhoto()
	thisform.showSuccessMessage("You clocked out on " + dtoc(date()) + " at " + ampm())
endproc
 
obtn1.click = clockin
obtn2.click = clockout
 
// modal form
oform.show()
 
// must put read events at the bottom so the window stays open until closed by user
read events

Python

  • Check that pip is the latest version. From a Windows Command Prompt (replace drive: with the drive for your Lianja App Builder installation):
cd drive:\lianja\bin
python -m pip install --upgrade pip --no-warn-script-location
  • In the Lianja App Builder Console Workspace, create the python3 directory to contain the modules local to the App (this has already been created for the example_timeclock App):
mkdir python3
  • Install local Python modules (pip can be run from the Lianja/VFP tab in the Console Workspace Command Window), e.g. numpy:
pip install --target=drive:\lianja\apps\example_timeclock\python3\ numpy

Note: the local installation of the Python modules causes these modules to be included when the standalone executable is generated. During App development, any required modules also need to be installed in the default location for Lianja, i.e. without a --target specified.

To check modules installed in the default location:

pip list

To check the Python path (from the Python tab):

import sys
print(sys.path)

example_timeclock.py

import sys
import types
from datetime import datetime
import Lianja
 
class myCommandbutton(Lianja.Commandbutton):
	pass
 
oform = createObject("form")
oform.resize(600, 600)
oform.closable = 1
oform.caption = "Time clock - Python"
oform.minwidth = 600
oform.minheight = 600
 
oform.addObject("ocont", "container")
ocont.margin = 10
ocont.autosize = 1
ocont.layout = "V"
ocont.backcolor = "black"
 
ocont.addObject("obadgenolab", "label")
obadgenolab.fixedheight = 24
obadgenolab.autowidth = 1
obadgenolab.alignment = "center"
obadgenolab.caption = "Enter Badge Number"
obadgenolab.backcolor = "darkgray"
obadgenolab.forecolor = "white"
obadgenolab.fontbold = 1
obadgenolab.fontsize = 10
 
ocont.addspacing(10)
ocont.addObject("obadgeno", "textbox")
obadgeno.fixedheight = 24
obadgeno.autowidth = 1
obadgeno.placeholder = "Enter your badge number, Take a Photo then CLOCK IN or CLOCK OUT"
 
ocont.addspacing(10)
ocont.addObject("obadgenolab2", "label")
obadgenolab2.fixedheight = 24
obadgenolab2.autowidth = 1
obadgenolab2.alignment = "center"
obadgenolab2.caption = "Take a Photo"
obadgenolab2.backcolor = "darkgray"
obadgenolab2.forecolor = "white"
obadgenolab2.fontbold = 1
obadgenolab2.fontsize = 10
 
ocont.addspacing(10)
ocont.addObject("ocamera", "camera") 
ocamera.autowidth = 1
ocamera.autoheight = 1
ocamera.backcolor = "black"
 
ocont.addspacing(10)
ocont.addObject("obadgenolab3", "label")
obadgenolab3.fixedheight = 24
obadgenolab3.autowidth = 1
obadgenolab3.alignment = "center"
obadgenolab3.caption = "Clock in / Clock out"
obadgenolab3.backcolor = "darkgray"
obadgenolab3.forecolor = "white"
obadgenolab3.fontbold = 1
obadgenolab3.fontsize = 10
 
ocont.addObject("ocont2", "container")
ocont2.layout = "H"
ocont2.fixedheight = 60
ocont2.backcolor = "black"
ocont2.spacing = 10
 
ocont2.addObject("obtn1", "myCommandbutton")  
obtn1.resize(60, 50)
obtn1.flat = 1
obtn1.fixedheight = 50
obtn1.caption = "CLOCK IN"
obtn1.stylesheet = "btn"
ocont2.addObject("obtn2", "myCommandbutton")  
obtn2.flat = 1
obtn2.resize(60, 50)
obtn2.fixedheight = 50
obtn2.stylesheet = "btn"
obtn2.caption = "CLOCK OUT" 
 
# event handlers
def clockin(self):
	ocamera.takePhoto()
	now = datetime.now()
	oform.showSuccessMessage("You clocked in at " + now.strftime("%m/%d/%Y %I:%M:%S %p"))
 
def clockout(self):
	ocamera.takePhoto()
	now = datetime.now()
	oform.showSuccessMessage("You clocked out at " + now.strftime("%m/%d/%Y %I:%M:%S %p"))
 
# We can dynamically bind events to custom classes in Lianja/Python
bindEvent(obtn1, clockin, "click")
bindEvent(obtn2, clockout, "click")
 
# modal form
oform.show()
Lianja.readEvents()

JavaScript

example_timeclock.js

var oform = createObject("Form");
oform.resize(600, 600);
oform.closable = 1;
oform.caption = "Time clock - JavaScript";
oform.minwidth = 600;
oform.minheight = 600;
oform.deleteonclose = 1;
 
oform.addobject("ocont", "container"); 
ocont.margin = 10;
ocont.autosize = 1;
ocont.layout = "V";
ocont.backcolor = "black";
 
ocont.addObject("obadgenolab", "label"); 
obadgenolab.fixedheight = 24;
obadgenolab.autowidth = 1;
obadgenolab.alignment = "center";
obadgenolab.caption = "Enter Badge Number";
obadgenolab.backcolor = "darkgray";
obadgenolab.forecolor = "white";
obadgenolab.fontbold = 1;
obadgenolab.fontsize = 10;
 
ocont.addspacing(10);
ocont.addObject("obadgeno", "textbox");
obadgeno.fixedheight = 24;
obadgeno.autowidth = 1;
obadgeno.placeholder = "Enter your badge number, Take a Photo then CLOCK IN or CLOCK OUT";
 
ocont.addspacing(10);
ocont.addObject("obadgenolab2", "label");
obadgenolab2.fixedheight = 24;
obadgenolab2.autowidth = 1;
obadgenolab2.alignment = "center";
obadgenolab2.caption = "Take a Photo";
obadgenolab2.backcolor = "darkgray";
obadgenolab2.forecolor = "white";
obadgenolab2.fontbold = 1;
obadgenolab2.fontsize = 10;
 
ocont.addspacing(10);
ocont.addObject("ocamera", "camera");
ocamera.autowidth = 1;
ocamera.autoheight = 1;
ocamera.backcolor = "black";
 
ocont.addspacing(10);
ocont.addObject("obadgenolab3", "label");
obadgenolab3.fixedheight = 24;
obadgenolab3.autowidth = 1;
obadgenolab3.alignment = "center";
obadgenolab3.caption = "Clock in / Clock out";
obadgenolab3.backcolor = "darkgray";
obadgenolab3.forecolor = "white";
obadgenolab3.fontbold = 1;
obadgenolab3.fontsize = 10;
 
ocont.addObject("ocont2", "container");
ocont2.layout = "H";
ocont2.fixedheight = 60;
ocont2.backcolor = "black";
ocont2.spacing = 10;
 
ocont2.addObject("obtn1", "commandbutton");  
obtn1.resize(60, 50);
obtn1.flat = 1;
obtn1.fixedheight = 50;
obtn1.caption = "CLOCK IN";
obtn1.stylesheet = "btn";
ocont2.addObject("obtn2", "commandbutton"); 
obtn2.flat = 1;
obtn2.resize(60, 50);
obtn2.fixedheight = 50;
obtn2.stylesheet = "btn";
obtn2.caption = "CLOCK OUT";  
 
// event handlers
function clockin() 
{
	ocamera.takePhoto()
	oform.showSuccessMessage("You clocked in on " + new Date().toLocaleString())
};
 
function clockout()
{
	ocamera.takePhoto();
	oform.showSuccessMessage("You clocked out on " + new Date().toLocaleString())
};
 
obtn1.click = clockin;
obtn2.click = clockout;
 
// modal form
oform.show();
 
// must put read events at the bottom so the window stays open until closed by user
read_events();

Login

From v9.5 standalone Apps can require login.

  • Form.requireslogin property
  • Fully customizable login form

Coming Soon

Building and Testing Standalone Executables

To build and test, switch to the Console workspace and click the 'Desktop App View' button in the Headerbar:

Sample App: example_timeclock


The Output window will show the compilation and build operations.

To build and test the other scripts in the example_timeclock App - LianjaScript (.prg), Python (.py) and JavaScript (.js) are available - just select the required scripting language in the App Settings and click the 'Desktop App View' button again.

Lianja Standalone App Debugger

From v9.5 standalone Apps can be debugged at runtime.

Coming Soon

Deploying Standalone Executables

When building a standalone executable with LianjaScript, all the .prg files in the App directory are linked together into a .src file and this is compiled.

The resulting object file is bound onto the end the Lianja runtime executable and the authenticode code signing is adjusted so as to not be affected by the application payload.

All of the supporting libraries are included in the standalone application directory.

If your application is built in Python, there is a python3 directory included which contains the python modules you have installed with pip.

Pre-9.5 Deployment

To deploy, install the whole standalone application directory, e.g for the example_timeclock App the directory is:
drive:\lianja\installers\standalone\example_timeclock

and it has a single bin sub-directory containing the executable and required libraries:

drive:\lianja\installers\standalone\example_timeclock\bin

To run, the executable is:

drive:\lianja\installers\standalone\example_timeclock\bin\example_timeclock.exe

Note: In v7.0, the exe and required libraries are located in the application directory itself, e.g. example_timeclock (this has been corrected in v7.1). These need to be placed in a bin directory, either by renaming the directory to bin or by creating a bin sub-directory and copying all the other files into the new sub-directory.

Post-9.5 Deployment

From v9.5 you can also choose to deploy your App in the following ways:

Additionally, using the Standalone Lib and Standalone Data App settings allows any required library files and databases to be included in the deployment zip or exe.