PLC user interface is made with Qt Designer
customized with Sirius-ES plug-ins.
Qt Designer
is a Qt’s tool for designing and building graphical user interfaces (GUIs) from Qt components. Users can compose and customize your widgets or dialogs in a what-you-see-is-what-you-get (WYSIWYG) way, and test them using different styles and resolutions.
For basics components and help, please refer to the official Qt Designer
site (QT Reference Documentation).
Qt Designer
offer many standard components to create layouts and forms. Sirius-ES has created a set of widget for an easy interface with PLC’s variables.
Inside the IEC project folder, there are two sub-folders: “ui” and “res”. They contains the user interface pages (ui) and all the resources they need (img, etc.).
In addition there is a Menu called “Projects” that contains parts of Sirius-ES customized tools.
Inside project menu are listed the items in the image below.
Click on it and select the language to show into forms. It shows translations into page with the selected language. Translation must be inserted into linguistic.txt file.
If translation is missing, default value is shown.
The translation languages are descripted into linguistic.txt file (CSV file). It use TAB character for separator.
The translation can be managed with a text editor orany electronic spredsheet program.
Opens a Sirius-ES Editor IEC project, or a simple HMI project. Selecting the project's folder, all the forms in the “ui” folder will loaded.
If files variables_list.txt or variables_list.alias are present,the designer load them.
It scans all the form in order to check missing destination pages or missing translations. The result is saved into the “ui/translation_destination.txt” file.
Analyzing file "default_800x480.ui"
Missing translation for label ---> "WWW.SIRIUS-ES.IT"
Missing translation for label_2 ---> "Version:"
Missing translation for qHmiLabel ---> "00.00"
Missing translation for QHmiLinkPage ---> "START"
Missing translation for label_3 ---> "USER INTERFACE EXAMPLE PROJECT"
In this case there aren’t translations, they are marked into fill
to be defined
Restore to initial conditions and positions, all the panels of Designer. The designer must be restarted.
Link to this site.
Version and release date of Designer customization.
If the project opened is a Sirius-Es IEC xml file, the designer search and load thevariables_list.txtfile.
It contains all the PLC variable to be used in HMI, to associate thme to buttons, fields and labels. Double click on variable name in order to copy to clipboard and paste in it into the widget.
Variables can dragged and dropped on widget to assign it.
If the project is only an HMI project, thevariables_list.txtfile is not present because it is created by the IEC editor. Is possible to create the variables_list.alias to redirectmemory address (IE: %MD1234) to a more useful naming convention. The system load the file and show the aliases instead of variables.
The file must be created with a text editor with the format:
IE:
If data type is not specified, the base type will be BYTE (%MB), WORD (%MW) or DWORD (%MD)
When variables are changed into IEC Editor, remember to save and build the project, in order to reload the variables list.
To simplify the creation of the user interface and communication with PLC, Sirius-ES added a set of custom components. They are listed into the Widget Tab inside the Sirius HMI Widget section.
They extend the QT widgets with some property like fieldName (link to PLC variable) to show variables value, or set of values into them; there is also the possibility of configuring multi language widget.
All the Sirius_ES components are created for an easy use with PLC’s variables.They contains a properties section to map variables and modify component’s style.
If the widget do not need variable (fieldName or stateField), is better use QT standard widgets because higher performance. (less CPU usage).
Each custom component can be have different properties to set.
Common custom properties are grouped into a section called like the widget type.
FieldName is a common properties used to associate PLC variable to the widget.
StateField is used to control the condition of the widget. It is a boolean variable that drive the stateAction field.
StateAction tells to the system what do when stateField is set to true. It can be set to Visible or Enable.
The custom fields into Dynamic Properties section are used to profiling the use of the interface.
“PasswordLevel” tell to the system if the widget can be used or not by the current user.
0 mean all users, a level from 1 to 99 means that the widget can be used only for user with the same level or higher.
“Hide” describe the functioning of widget: if checked, it can be visible or not for users, if not checked it can be enabled or disabled for the current user.
Dynamic Properties is a customization added to all Qt Designer
’s widget.
Used to show on screen a variable value.
Decimal count indicates the number of decimal places to display (if the variable is a number).
Used to edit variables values. Like QHmiLabel can show a fixed decimal number.
It has also two Dynamic properties to set min and max value (if number). When insert a new value, it must be in min/max range. Other values are not allowed.
Used to choose in a set of values.
To define combo box items, right click on the widget and select “Edit items...”.
Click on “+” button to add a new item. “-” to remove the selected item from list. Arrows are used to change items’ order.
If Translatable properties is checked, the item description is picked from the linguistic file. Item description is the translation key.
FieldName is an integer variable that match the item index.
It is used to call other pages on PLC (like an HTML link).
Destination page is the page to load when clicked.
Action can be set to jumpOnly and jumpAndDestroy. The first one load the selected page and keep in memory the graphic render of the current page, the second one, load the selected page and unload from memory the current page. In any case, the variable state is unloaded from memory (the system cache only the graphic render in memory when jump).
It has also a Dynamic properties to set the text alignment in the button (default in center).
Destination page can be setted with drag&drop of pagename on widget
System pages can be linked: destinations are listed in the GUI Forms panel
Internal Pages links |
---|
@ApplicationVariables |
@Account |
@DateAndTime |
@Informations |
@InternationalSettings |
@NetworkPreference |
@Monitor |
@ValuesHandler |
@Devices |
All this pages are password protected.
Login link:
@Login
Once logged, if user level >= 7, the system menu appears.
Used to change state of boolean variables.
Dynamic properties “Align Left” is used to set the text alignment in the button (default in center).
Works like a QHmiLabel but with a different layout. Used to show only numeric variables.
It shows number like 7 segments display.
Set values to PLC's variables. Used in combination with buttons and any widget can be edited.
IE: QPushButton wired to QHmiSetWidgetState is the equivalent of a Sirius QHMIButton.
QPushButton signal clicked(bool) set the state to the variable associated to QHmiSetWidgetState on slot set_value(bool).
QLineEdit signal textEdited(QString) wired to slot set_value(QString) is the equivalent of QHmiEdit widget.
Used to show an animated image (GIF format). With this widget gauges and progress bar can be created with a specific layout.
Movie name is the file to load.
Frame Number is the number of frame to show,
Fieldname is the plc variable with the current frame index.
In example this image can be used to monitor any value like Volt, Ampere ect. Click on the image below to show the animation.
Recipes are used to save and load PLC’s configurations.
In each recipe a set of variable can be saved with their values and loaded, in order to configure PLC.
Data will be saved with NAME “space” VALUE row format.
User can set recipes Directory, file extension and media device to store data.
Variables field contains the list variable to save/restore values.
The messages fields are the strings used for confirm popups.
See the "Recipes" example to see how configure and work with recipe
Used to show multiple page in one form.
Field name tell to the widget which page show (page index). Page changing can be done with two QPushButton (Previous and Next) wired from clicked() signal to slots slideInNext() and slideInPrev()
To insert pages right click on component and click "Insert page" from context menu. Inside page (QWidget) drop objects to create it.
Set a variable to ON or OFF. Vaue represent the default value.
Used to show the progress of work. The Plc variable set the value between minimum and maximum.
This widget is used to select some preloaded shapes or load dxf filefor cnc works.
The widget load DXF, CNC, TAP file extensionslocated into /SiriusApplicat/DATA/drawings folder on PLC (or PC for simulation).
Other file type can be specified by the extensions property, selecting the property and clicking Change String list. Then insert the new file extension without *. (only the type). The list is case sensitive.
Files into the folder are showed into the widget as preview list.
Once a shape is selected, the system send a signal to the connected slot.
CN or TAP files: the system send only the selected filename. thedefaultFeedRatevalues is inside the file.
DXF files: the property defaultFeedRatemust be set with PLC's REAL variable, in order to know the work speed.
If not set (or 0) the work does not start (speed is 0).
The selected file is converted into a cn file (with G-Code instructions). Then the filename is emitted to the connected slot.
The generated file is placed in the same folder of source file.
In the connected slot, if the file source file was on usb device, is recommended to make a local copy into /SiriusAppllicat/DATA for safe parsing and machining.
The received string (with filename) must be wrote into a PLC's string variable, then start the CNC_parser function block.
PLC CODE:
CNC_PARSER.FILENAME := "value from javascript";
CNC_EXECUTE := TRUE;
Now the cn file parsing starts; the result is a precompilated binary file
See the CNC_PARSER function block on IEC editor block.
if CNC_PARSER.DEFAULTVEL is not set from cn file or defaultFeedRate property, is possible to set a value from code.
CNC_PARSER.DEFAULTOFFSET is the Z axis offset the reference tool.
CNC_PARSER.DEFAULDIAMETER is used for tool compensation.
CNC_PARSER.IJKRELATIVE is used to calculate circles and archs, refert toCNC COOKBOOK
CNC_PARSER.DISTANCEMODERELATIVE tells to use G90 (false) or G91 (true)
CNC_PARSER.REF TO TABLE is used to specify the tools table. See IEC BUILT IN DATA TYPES.
For the G commands list please refer to:Wikipedia G-Code page.
CNC_PARSER has these outputs:
DONE: conversion finished
BUSY: operation in progress
ERROR: true if error is occurred
ERRORID: error code
ERRORSTR: error description
PERCENT: conversion progress indicator (IE: used to fill QHMIProgressBar)
The binary file produced is thecnc.bin.
Once the conversion is done without errors, the output file is used with CNC_READFILE function block.
CNC_READFILE.FILENAME = the original file name selected from QHMIPreviewTable ("value from javascript")
CNC_READFILE.EXECUTE run the function block
CNC_READFILE.DISPLACEORIGN true for traslate in 0,0 the entire project.
CNC_READFILE ref to LINEINFO use array of cnc_line_info to fill the work lines from binary.See IEC BUILT IN DATA TYPES.
Output parameters are DONE, BUSY, ERROR, ERRORID, ERRORSTR
CNC_READFILE.LOWBOUND is bottom coordinates of the draw
CNC_READFILE .HIGHBOUND is the top coordinates of draw.
If DISPLACEORIGIN is set to TRUE, LOWBOUND is always 0.
CNC_READFILE LINEINFO_LEN is the number of commands. is the length of the LINEINFO array.
CNC_READFILE READED is a counter of calling function block. Used for debug purpose.
CNC_READFILE PERCENT is the progress of reading,progress indicator (IE: used to fill QHMIProgressBar)
Once the process is completed, the LINEINFO Array is loaded with all the commands to execute.
To scale the lineinfo array, use the CNC_SCALE function block.
CNC_ROTATE rotate the lineinfo array fields with the given angle.
CNC_TRASLATE, move the lineinfo array with the given TRASLATE_TO input parameter
CNC_GETLINESTRING get the lineinfo array selected line as string.
CNC_GETLINEINFO fill a lineinfo structure ref to, with the lineinfo array specified line. like a get(i) element.
CNC_EXECUTE
INPUT
RESET (bool): reinizialize the process (set line 0 of lineinfo array)
EXECUTE (bool): start process from current line
AXIS(array of int): inizialize the used axes
STOP (bool): pause current process
LINEINFO (ref to): the process to execute
LINEINFO_LENGTH(DINT) total process line
RAPID_REV (bool): return back to tool path
RAPID_FOW (bool): forward to tool path
STEP_BY_STEP(bool): run a single command, the stop the process
STEP_DELAY(UINT): pause from STEP to another STEP in milliseconds
MAX_CENTRIPET_ACC (real) max centripetal acceleration
MAX_SPEED(real) max speed of process
ACCELERATION (real)
DECELERATION (real)
SPEED_OVERRIDE (real) force the actual speed
RAPID_SPEED (real) used by G00 code transfer
CURVEDIVISION (real) distance from points used to draw circles (mm)
BLEND_PATTERN (bool) : set to true by default, used to mix sharp direction changes.
SAVE_POINT (bool): if true save the current work path as gcode for debug. The result is into /tmp/savedpoints.cnc file.
OUTPUT
INTERPOLATING (bool): tells if is moving or not.
CNC_EXECUTE.ACTPOS position of the current axes. At start must be loaded with the real axes position.
When interpolating is true, the function block generate a virtual position to give as input to the corresponding axes.
AXIS_REF.ACTSPA := CNC_EXECUTE.ACTPOS[i], where i is the current axes
0 -> X axes
1 -> Y axes
2 -> Z axes
3 -> A axes
4 -> B axes
5 -> C axes
6 -> U axes
7 -> V axes
8 -> W axes
GROUP_STATE (GROUP_STT enum) is the state of working: refer to GROUP_STT built in data type
if the state is NEED_RESET, a reset command is needed.
CNC_STATE (CNC_STT emun )refer toCNC_STTbuilt in data type
CURRENT_SPEED (real) interpolator's actual spped
SPACE_DONE(real): distance done from start to actual position.
SPACE_LEFT(real). remaining distance to do.
ACTIVE (bool) working if true
STOPPING (bool): stopping current work
GCODELINE (DINT) current command line of the lineinfo array
ACTUAL_SPINDLE_SPEED (real): tool speed
EXTGCODENEED (bool): if true, an external operation in needed (ie: turn on/off tool)
refer to the example project.... <---- TODO ---->
INPUT/OUTPUT
EXTGCODEDONE(bool): response from external gcode request (EXTGCODENEED) if true
the function set to falseEXTGCODENEEDandEXTGCODEDONE, then proceed the work
Undescribed parameters are not used.
It is used to view the bounding box of cnc works. It loads thw preview generated with CNC_PARSER function block.
Emit the bounding box weight and heigth that can be connected to labels in order to show datas.
The input slots can be connected to buttons signal/slot.
The inputs are :
ZOOM IN (bool)
ZOOM OUT(bool)
ZOOM ALL(bool)
SET_SHOW_BOUND(bool)
SET_AUTO_PAN(bool)
The dynamic properties can be filled with PLC's variables to show data of the current work.
ON_WORK: CNC execute variable
GCODELINE: current line of execution
DRAWNAME: loaded file
TOOLDIAMETER: tool diameter
TOOLCUTTING: actual cutting or not
SHOWREALWORK: show real cutting path, used for debug working
ABSPOSX: actual X position
ABSPOSY: actual Y position
POSROT: show cutter orientation
POSRVX: min X bound
POSRVY min Y bound
POSFWX max X bound
POSFWY max Y bound
ROTATION: rotate specified grade the preview (only preview)
WORKING set to true to show the widget's round progress bar
WORKINGPERCENT:the widget's round progress bar value
RELOAD: counter of reloaded work, if change, refresh preview.
QHmiFileDialog
Open a popup form to choose a file. Once selected returns the full filename to the ui. (path+name).
Wire a QPushButton clicked() signal to open_dialog_slot() to open it.
Wire the open_dialog_file_signal(QString) signal to a QLabel (or else) setText(QString) slot to show the selected file name.
Files, folders and drives can be filtered by setting widget's properties.
Action can be set to Open, Rename or Remove.
Used to parse DXF, CNC or NC file. No properties are addded to this widget.
Wire from a widget with text (QString) signals to slot set_file_slot(QString) of this widget to load the file.
It can wired to a QHmiFileDialog to choose a file and show it.
To show the preview connect its signal set_pixmap_signal(QPixmap) to a QLabel slot setPixmap(QPixmap).
The label set its background image with the generated preview.
See the example page dxf.ui of the Sirius-ES example project.
It is used only for preview. I doesn't order segments and archs for CNC works.
To do that, use QHmiPreviewTable.
Used to show a variable value like an oscilloscope. Set fieldName with the variable to show.
For an easy configuration of alarms the QHmiAlarms widget can be used inside forms.
It accepts a list of boolean variables that tell to the system which alarm is enabled or not.
Each alarm can be configurated with messages, colors and other properties in order to customize the application.
Clicking on Change String List button, a popup si shown to add alarm variables.
Then click New and type the alarm variable's name.
If the string inserted does not match withvariables or alias, an error is occurred.
To edit alarm properties, right click on widget and choose Alarm properties.
A table shows the properties of each alarm.
Description: Message of alarm (translatable with the linguistic file)
Variable: the variable to check for the selected alarm.
BackGround: alarm popup background color
**ForeGround:**alarm popup foreground color
AutoRemove: reset the alarm to the initial condition (see truth level)
AutoHide: remove the alarm automatically on plc.
PopUp: show the alarm into popup window
Log: save the alarm into alarms.log file
Table: Show the alarms into QHmiAlarms widget table.
Truth level: set alarms on rising or falling variable's trigger.
Used to retrieve OS informations. No properties are addded to this widget.
To use it, wire the signal of the information needed.
Used to drow on screen object that can be moved, resized or rotated.
Variables attached to offsets, rowths or rotation set dimensions/position of the object on screen.
Frame shape is the type of object.
This widget is used to show an area with a different color based on "level" variable value.
It need a background image, a background color and a fill color. It replace the backgroud color with fill color from bottom (level 0) to level value.
Value is the filling percentage: from 0 to 100.
NOTE: ALL the pixels of image with background color will replaced. This means that the image can be drawed with wrong color areas.
*NOTE:*The image must have a border with a different color from background. In other case the fill color will not work.
Shows the progress like the linear QHmiProgressBar but with a different layout. it can be used to show data like gauges or measure instruments.
Different styles can choosen and color gradient can be set.
If a component needs some custom properties, they can be added clicking on "Add dynamic property" button.
Selecting "Other" from context menu, a popup is shown:
Then select name and type; after click OK, the new property is added to the widget and became accessible from javascript.
Some actions of widgets can be attached to other widget to do something.
The action of a widget is the signal emitted.
The receiver widget has many slot for the signal.
To activate the connection between widgets (wiring operation) there is a specific button into the toolbar that change the designer from edit mode into wire mode.
Clicking on it the designer switch in wire mode. The pages show the connection between widget with selected signals and slots.
Any widget have signals and slots.
To wire them click without release button on source and move the mouse on destination widget. On it, release the mouse button. A popup will appears in order to choose the source signal and the destination's slot.
In example a QPushButton can be wired to a QCheckBox:
At runtime when the button is pressed, in the checkbox will be checked or not.
Wiring components can be done also from a widget' signal from a javascript function.
Into the javascript file, in the register function, user can write the connection from signal to code.
If a connect is defined into function register, the corresponding disconnect must be written in the function unregister.
The system always append a connect slot, the previous connect is not replaced but continue to work.
With the disconnect call, the slot is deleted when the page is leaved.
function the_function_to_call(value) {
//value type is the same of calling signal...
//write code here...
}
function register() {
//APPEND a javascript function to the widget signal
the_widget["clicked(bool)"].connect(the_function_to_call);
}
function unregister() {
//remove the function from the signal when the page is leaved for another one
the_widget["clicked(bool)"].disconnect(the_function_to_call);
}
In any form, actions and settings can be done with JavaScript.
To add a javascript file to form the form, right click on the form filename into GUI Forms panel.
Javascript files have the same name of the form but with js as extension. Only one file per form is created.
If the javascript file is present, in the “GUI Forms” panel, on the right of form’s name, “ (js)” is shown.
At runtime, the system check if the file exists; then the file is loaded and code executed.
Clicking on Add/Edit javascriptthe editor is opened. It contains the file structure on left and the text editor on right.The buttons for find/replace and save are on the top left corner.
The drop-down “Additional js” is used to edit the system file system.js and the speficied with the variable include. If the form doesn’t have a JavaScript file associated, the system load a file template, with empty system functions.
In the left panel, “Functions” contains all the function written. Identifiers are the JavaScript variables. Objects represents the form’s widget list with their methods and properties.
The system.js file is a special javascript file thatruns independently from the pages. It is loaded when the application starts and executed in background. This file is the only one that can implement the function on_change_form(). It can be used for scheduled task.
Variables can be declared into code with the keyword “var”. The data type is defined by variable initialization. There are two reserved variable used into code: “timer_tick” and “include”.
Variable timer_tick represent the millisecond used to timing the call of function timer(); Variable “include” tell to the system which JavaScript file must be included into this page.
Only one file can be included per page. If a file is included, it can be edited in the “Additional js” drop-down.
var timer_tick = 500;
var include = "fileToInclude.js";
var example1 = "string variable"; // STRING
var example2 = new String(); // another STRING
var example3 = 10; // INT
var example4 = 0.5; // FLOAT
var example5 = []; // ARRAY
var example6 = new Object(); // GENERIC OBJECT`
Variables lifecycle is limited to the page. When the page is loaded variables are inizialized with declaration or within the register function. When the page is leaved, datas will be lost.
To save variables' status or use them across pages, values can be stores using the guiInterface function.
Values are all strings. The user must know the type to cast it.
Values can be saved, retreived or deleted.
// add a global variablle named "GLOBAL_NAME" with value "GLOBAL_VALUE"
guiInterface("setglobal", "GLOBAL_NAME", GLOBAL_VALUE);
// retrieve the "GLOBAL_NAME" value
var value = guiInterface("getglobal", "GLOBAL_NAME");
// remove from global the "GLOBAL_NAME" variable
guiInterface("removeglobal", "GLOBAL_NAME");
// remove all the variables with name that starts with "GLOBAL_"
guiInterface("removeglobal", "GLOBAL_*);`
JavaScript functions are declared with the keyword “function”. Input and output type are defined by the variable data type used for input parameters and output. There are four function defined by the system; the system.js file had 5 function;
Function register() is called when form is loaded, Function timer() is called every “timer_tick” interval, in milliseconds. If this var is not defined, the default value is 100 ms. The minimum value accepted is 20 ms.
Function unregister() is called before form is unloaded (IE: jump to another form).
When a form variable value is changed from PLC, the function values_changed(vars, changed) is invoked.
“on_change_form()” is only from system.js. Is invoked when user interface switch from a form to another.
function register(path) {
//executed after page load
}
function timer() {
//executed by timer_tick interval ( default = 100 ms, minimum value = 20 ms)
}
function unregister(path) {
//executed after page unload
}
function values_changed(vars, changed) {
//executed when a variable value is changed by PLC
//vars is the array of all varibales
//changed is the array of only changed vars
}
/* only for system. j s */
function on_change_form() {
//executed when the system switch from a form to another
}
When the function values_changed is invoked, is possibile to know which variable is changed testing it into changed array
function values_changed(vars, changed) {
//if the TEST_VAR is present in changed array
if (typeof changed["TEST_VAR"] != "undefined") {
//read the variable's value
var value = changed["TEST_VAR"];
}
}
guiInterface
guiInterface
is a special function developed for using QT components into javascript and system functions.
The first parameter of this function identifies the interface type and the command to call:
guiInterface("TYPE::COMMAND", ...........);
Used to read and write PLC variables from javascript code. Any variable must be registered in function register.
function register(path)`
{
guiInterface("vars::add_variable","VARIABLE_NAME");
}
Then when the code is executed, the variable is ready for any operation:
//reading variables value`
var stringValue = guiInterface("vars::string_value_read", "VARIABLE_NAME");
var boolValue = guiInterface("vars::bool_value_read", "VARIABLE_NAME");
var intValue = guiInterface("vars::number_value_read", "VARIABLE_NAME");
var floatValue = guiInterface("vars::double_value_read", "VARIABLE_NAME");
//writing variables value
guiInterface("vars::string_value_write", "VARIABLE_NAME", stringValue);
guiInterface("vars::bool_value_write", "VARIABLE_NAME", boolValue);
guiInterface("vars::number_value_write", "VARIABLE_NAME", intValue);
guiInterface("vars::double_value_write", "VARIABLE_NAME", floatValue);
Used to exectute operations on disk directories.
//Getting the last error string
var error = guiInterface("QDir::errno");
//Directory operations (all return boolean value)
var result =guiInterface("QDir::mkdir", dir_name);
var result =guiInterface("QDir::exist", dir_name);
var result =guiInterface("QDir::remove", dir_name);
var result =guiInterface("QDir::rename", dir_name);
var result =guiInterface("QDir::rmdir", dir_name);
//Path operations
//getCurrent path
var path = guiInterface("QDir::currentpath");
//get tmp path
var tmpPath = guiInterface("QDir::tempPath");
//set path with dir_name, retuns true/false as result
var result = guiInterface("QDir::setPath", dir_name);
//create directory dir_name and any previous missing folder, retuns true/false as result
var result = guiInterface("QDir::mkpath", dir_name)
//delete folder dir_name and all previous if empty, retuns true/false as result
var result = guiInterface("QDir::rmpath", dir_name);
//directory content
guiInterface("QDir::entryList", dir_name [,namefilters] [,filters]);
//example
var fileList = guiInterface("QDir::entryList", filepath, "*.csv|*.txt", Files|Dirs);
//dir_name is a string
//namefilters is a string. IE: "*.dxf|*.txt"
//filters, integer Dirs|Files|... see QT documentation
To work with files they must be registered first:
var file_descriptor = guiInterface("QFile::register", filename);
filename is the fullpath of file. Once registered file operations are allowed on the registered file.
When operations are finished, use unregister function to release it.
guiInterface("QFile::unregister", file_descriptor);
//file errors
var error = guiInterface("QFile::errorString");
var error = guiInterface("QFile::errno");
var result = guiInterface("QFile::exists", name);
var result = guiInterface("QFile::open", file_descriptor, flags);
//flags = ReadOnly,WriteOnly,ReadWrite,Append,Truncate,Text,Unbuffered can be in OR
var result = guiInterface("QFile::close", file_descriptor);
var result = guiInterface("QFile::copy", name);
var result = guiInterface("QFile::remove");
var result = guiInterface("QFile::rename", new_name);
var result = guiInterface("QFile::atend", file_descriptor); //true if EOF
var pos = guiInterface("QFile::pos", file_descriptor);
var result = guiInterface("QFile::seek", file_descriptor, pos);
var size = guiInterface("QFile::size", file_descriptor);
var bytes = guiInterface("QFile::read", file_descriptor, n_byte); //returns a byte array
var bytes = guiInterface("QFile::readall", file_descriptor);
var stringLine = guiInterface("QFile::readline", file_descriptor);
guiInterface("QFile::writeline", file_descriptor, string); //append \n and the end of string
Getting information about file, directories and paths. (see QT documentation QFileInfo).
var result = guiInterface("QFileInfo::absoluteFilePath", file_path);
var result = guiInterface("QFileInfo::absolutePath", file_path);
var result = guiInterface("QFileInfo::canonicalFilePath", file_path);
var result = guiInterface("QFileInfo::filePath", file_path);
var result = guiInterface("QFileInfo::path", file_path);
var result = guiInterface("QFileInfo::baseName", file_path);
var result = guiInterface("QFileInfo::completeBaseName", file_path);
var result = guiInterface("QFileInfo::suffix", file_path);
var result = guiInterface("QFileInfo::completeSuffix", file_path);
var result = guiInterface("QFileInfo::fileName", file_path);
var result = guiInterface("QFileInfo::size", file_path);
Getting the javascript time of execution.
var timeCount = guiInterface("QTime::elapsed", file_path);
Used to know system information or getting translation in javascript
guiInterface("system::sleep", seconds);
var kbFree = guiInterface("system::drivefree");
var usbPath = guiInterface("system::usbdrive");
var usbKbFree = guiInterface("system::usbdrivefree");
var sdPath = guiInterface("system::sddrive");
var sdKbFree = guiInterface("system::sddrivefree");
var flashKbFree = guiInterface("system::freespace");
var translation = guiInterface("system::linguistic", default_code, lang);
On screen popup messages, for the user.
Note: this code will not works into DIALOG pages.
//popup with error message style
guiInterface("gui::show_error", title, text);
//popup with javascript alert style
guiInterface("gui::messageBox", title, text);
//popup with javascript confirm style
guiInterface(
"gui::messageBox",
title,
text,
buttons[(messageBoxNo, messageBoxYes, messageBoxOk, messageBoxCancel)]
);
EXAMPLE:
var result = guiInterface("gui::messageBox", "WARNING", "Delete file?", messageBoxNo|messageBoxYes);`
if(result == messageBoxYes)
{
//do something for the confirm
}
else
{
// do something else...
}
The call interface is used to setup widget via code. It used for QComboBox, QListWidget and QTabWidget (to createspredsheet view table).
//Drop into form a QComboBox, with name "combobox"...
guiInterface("call::combobox::addItem", text);
guiInterface("`call::combobox::addIconText", path\_of\_image, text);
var itemNumber = guiInterface("call::combobox::count");
var index = guiInterface("call::combobox::currentIndex");
var text = guiInterface("call::combobox::itemText", index);
guiInterface("call::combobox::setCurrentIndex", index);
guiInterface("call::combobox::setFontSize", index, size);
//Drop into form a QListWidget, with name "listWidget"...
guiInterface("call::listWidget::addItem", text);
var itemNumber = guiInterface("call::listWidget::count");
var index = guiInterface("call::listWidget::currentRow");
var item = guiInterface("call::listWidget::item", index);
guiInterface("call::listWidget::setCurrentRow", index);
guiInterface("call::listWidget::setFontSize", index, size);
//Drop into form a QTabWidget, with name "tabWidget"...
//calling interface to setup a spreadsheet table.
guiInterface("call::tabWidget::setupTabs", [Tab names], [Header values])
guiInterface("call::tabWidget::clear");
guiInterface("call::tabWidget::addItem", [array of values]);
guiInterface("call::tabWidget::count");
var index = guiInterface("call::tabWidget::selected");
var row = guiInterface("call::tabWidget::getItem", index);
guiInterface("call::tabWidget::removeItem", index);
//Drop into form a QTableWidget, with name "tableWidget"...
//after beginUpdate execute all the operations at once.
guiInterface("call::tableWidget::beginUpdate");
//commit of operations
guiInterface("call::tableWidget::endUpdate");
//get the rectangle to paint cell editing, then call the inputcontext method to show the keyboard and set the callback function
var itemRect =guiInterface("call::tableWidget::visualItemRect", row, column);
guiInterface("inputcontext", "BOOL", value, "the_callback_function", rect);
guiInterface("call::tableWidget::setHorizontalHeaderLabels, {"label1", "label2"});
guiInterface("call::tableWidget::setVerticalHeaderLabels, {"label1", "label2"});
guiInterface("call::tableWidget::setVerticalHeaderWidth, value);
guiInterface("call::tableWidget::setColumnWidth, column, value);
guiInterface("call::tableWidget::setRowHeigth, row, value);
var rowIndex = guiInterface("call::tabWidget::currentIndex");
var rowIndex = guiInterface("call::tabWidget::currentRow");
guiInterface("call::tabWidget::setCurrentRow", rowIndex);
var columnIndex = guiInterface("call::tabWidget::currentColumn");
guiInterface("call::tabWidget::setCurrentColumn", columnIndex);
//add a item cell, cellItem contain row and column indexes.
guiInterface("call::tabWidget::addItem", cellItem);
var item =guiInterface("call::tabWidget::getItem", row, column);
guiInterface("call::tabWidget::removeRow", rowIndex);
//clear all table data
guiInterface("call::tableWidget::clear");
See the example of "**Table management**" for tabWidget use.
Used to manage a QTableView, in order to show data on screen.
//mandatory to used the tableView via javascript
//register a callback to get cell data from list
//Connect dataList to tableView
guiInterface("qtableview::tableView", "register_callback", "getTableItem");
guiInterface("qtableview::tableView", "setRowCount", number);
guiInterface("qtableview::tableView", "setcolcount", number);
guiInterface("qtableview::tableView", "setrowsheight", number);
guiInterface("qtableview::tableView", "setcolswidth", number);
guiInterface("qtableview::tableView", "setrowheight", number);
guiInterface("qtableview::tableView", "setcolwidth", number);
guiInterface("qtableview::tableView", "currentcellinfo");
guiInterface("qtableview::tableView", "updatecell", row, col);
guiInterface(
"qtableview::tableView",
"setverticalheaderlabels",
array_of_labels
);
guiInterface(
"qtableview::tableView",
"sethorizontalheaderlabels",
array_of_labels
);
guiInterface("qtableview::tableView", "repaint");
guiInterface("qtableview::tableView", "setcurrentcell", row, col);
guiInterface("qtableview::tableView", "editpending");
guiInterface("qtableview::tableView", "visualitemrect", row, col);
See QTableView example to use it
Sometimes is necessary to find a widget or a group of widgets.
In example it can be used to find buttons or labels of an included page:the main form's javascript editor didn't know the widget name, so it can be found.
For that the findChild method helps the programmer.
Check if the form is loaded and then search for the widget.
if (form != null) {
if (form.findChild("WIDGET_NAME") != null) {
var theWidget = form.findChild("WIDGET_NAME");
//do somethig with the retrieved widget
}
}
After opening a project, add user interface forms for controlling PLC status. To create Forms click the “NEW” button in the button bar. A dialog window appears. Then select “Widget” and click create. A new form is now created.
Now change page dimensions in order to fill the 800x480 Sirius-ES plc’s screens (small size).
Designing the form is simply, just drag and drop the components into it. Widget can be used like containers for groups of components (like inner panels).
In the Object inspector panel, the form’s widget hierarchy is shown.
Styles can be modified with style sheets, right click on the component to customize and click “Change styleSheet...”. A window with the current style appears (in the first time is empty). Just type the style, like CSS in HTML pages and apply.
A style can be defined for the main form and it extends to all the components, can be specific for a components family. Style can be also specified for a single component using its name with #
prefix, or for a component family using its type.
After typing or pasting a CSS style, click OK or APPLY to show the result (in this case, on the “Set Variable” button).
Translations are located in cvs format file “Linguistic.txt”. It contains translation labels, and user defined translations. It is a csv format file, with "TAB" character like separator.
A custom box is made for manage translations from Qt Designer
. It Allows users to create/delete/search and save translations into “Linguistic.txt” file
The system check if the widget text is set like language key inside table. If found, the translation for the system language is loaded. If not found, the widget label will not change.
Users can set their own translations adding row in table or editing the file directly. A simple way to add translation is right click on a widget, and select “Add widget to Translation database”.
The widget text is set like keyword, then type your own translation.
To simplify translation editing, or to set a massive translation group of labels, the file can be opened with any electronic spredsheet program like MS Excel or OpenOffice.
Once the file is edited, save it and reload the Qt Designer
with new translations.
Once the user interface is defined, it’s time to connect it to PLC.
With Sirius-ES custom components is very simple; double click on variable name in order to copy to clipboard and paste in it into the widget “fieldName” property.
In the example below the version value field is a QHmiLabel, a Qt Label redefined by Sirius-ES. Fill the field name with the variable, and at runtime the value is filled with the value read from PLC.
In case of input fields or buttons, value can be in read/write mode.
With all the Sirius-ES widgets that have the fieldName property, is possible to Drag&Drop variable on it.
Dragging the variable is an easy way to set the fieldName property.
The same operation can be used with the QHmiLinkButton. Drag the destination page on the widget to create the link.
The user interface can be configured to show alarms and warnings for monitoring PLC status.
The alarms can be shown into table or popup when the associated variable is set to on.
Drag the widget into the form; then drag into it the alarm variables (must be of BOOL type).
The widget reads a xml file alarm.xml; the alarms.xml file in this format:
<?xml version="1.0" encoding="utf-8"?>
<Alarms>
<Alarm name="CONF1_ALARMEXAMPLE" auto_remove="false" popup="true" log="false" table="false" text="ALARM TITLE\nTHIS IS THE ALARM MESSAGE" stylesheet="background-color: rgb(255, 0, 0);"/>
</Alarms> `
To add alarm variables click the property Variable in the property editor and then click "Change string list".
Double click inside widget to open configuration window.
Inside the page, this component can be wired to others widget, to manage alarms. It can be used to fill text fields or lists of alarms.
Priority is given by alarms order inside the xml file.
The QHmiAlarms widget is shared for all HMI. Once inserted, alarms can be showed in any form (popup mode).
To show alarm like a status bar follow this example:
Create a page and drop into it a QHmiLinkPage and QHmiAlarms.
Wire from QHmiAlarms signal firstactiveAlarm(QString) to QHmiLinkPage slot setButtonText(QString)
Into QHmiAlarms properties, set PasswordLevel to 999 and Hide to true, in order to make the widget invisible
Set destinationPage of QHmiLinkButton to the alarm page.
When a alarm is active, the link button show the active alarm. Clicking it the complete alarm table is shown.
Here an example to create a Recipes page.
Drop a QHmiWorkRecipe into page and insert "Directory", Extension and media from the Property Tab.
Drop 3 QHmiPushButton into page and replace text with LOAD, SAVE and DELETE
Drop a QLineEdit,QHmiEdit as your wish, to show/use the selected recipe filename.
If filename can not be edited, drop a Qlabel
User can also choose what message wants for the wired buttons (from QHmiWorkRecipe properties).
Wire the buttons with recipe widget selecting signal clicked() on each pushButton to slots loadRecipe(), saveRecipe() and deleteRecipe() for each wire.
Wire from QHmiWorkRecipe toQLineEdit(or else) to store the recipe file or change;from signal recipeSelected(QString) to slot setText(QString) on destination.
Wire fromQLineEditsignal textEdited(QString) to recipe slot setRecipeName(QString) to change the selected file.
The recipe page in the example project shows how recipes Widget works.
Is possible to load/save/delete a set of three variables, listed in the bottom of the page. editing values and pressing the save button, recipe will be saved with the specified file name.
Selecting a recipe from the box, the selected one can the loaded or deleted.
There are two waysto manage data in table format: the QTableWidget and QTableView.
The first one use a QT component with its connect slots and javasccript; the second in better for performance with huge data but does not have signals to connect and works only with javascript code.
Each cell is configured by javascript with the cellData object.
var cellData = {
row: 0,
col: 0,
icon: "",
iconPos: "",
text: "",
backGround: "",
foreGround: "",
};
Drop a QTableWidget into page, then edit javascript to populate it and manage cell clicks.
If you want to manage single and double click into cell, create variables cellRow and cellCol.
Then create the function cellClicked(row, col) to manage cells
var cellRow = -1;
var cellCol = -1;
function cellClicked(row, col) {
if (cellRow == row && cellCol == col) {
//second click
//close cell
guiInterface("call::tableWidget::setRowHeight", row, 40);
cellRow = -1;
cellCol = -1;
return;
} else {
//first click, do something
//expand cell height to highlight it
//and close the previous selected
guiInterface("call::tableWidget::setRowHeight", cellRow, 40);
guiInterface("call::tableWidget::setRowHeight", row, 150);
}
cellRow = row;
cellCol = col;
}
Then create a functions to load data and populate the table
var list = [];
function loadData() {
list = [];
list[0] = new Object();
list[0].code = "100";
list[0].description = "item 100";
list[0].quantity = "10";
list[1] = new Object();
list[1].code = "200";
list[1].description = "item 200";
list[1].quantity = "30";
list[2] = new Object();
list[2].code = "300";
list[2].description = "item 300";
list[2].quantity = "50";
}
function populateTable() {
guiInterface("call::tableWidget::beginUpdate");
guiInterface("call::tableWidget::clear");
guiInterface("call::tableWidget::setRowHeight", 0, 40);
for (i = 0; i < 3; i++)
guiInterface("call::tableWidget::setColumnWidth", i, 150);
for (i = 0; i < list.length; i++) {
//populate row cells
var codeCell = {
row: 0,
col: 0,
icon: "",
iconPos: "",
text: "",
width: 0,
height: 0,
backGround: "",
foreGround: "",
};
var descrCell = {
row: 0,
col: 0,
icon: "",
iconPos: "",
text: "",
width: 0,
height: 0,
backGround: "",
foreGround: "",
};
var qtyCell = {
row: 0,
col: 0,
icon: "",
iconPos: "",
text: "",
width: 0,
height: 0,
backGround: "",
foreGround: "",
};
codeCell.row = i;
codeCell.col = 0;
codeCell.text = list[i].code;
descrCell.row = i;
descrCell.col = 1;
descrCell.text = list[i].description;
qtyCell.row = i;
qtyCell.col = 0;
qtyCell.text = list[i].quantity;
guiInterface("call::tableWidget::addItem", codeCell);
guiInterface("call::tableWidget::addItem", descrCell);
guiInterface("call::tableWidget::addItem", qtyCell);
}
guiInterface("call::tableWidget::endUpdate");
}
Connect the function to the tableWidget, loadData and fill the table.
function register() {
loadData();
populateTable();
tableWidget["cellClicked(int,int)"].connect(cellClicked);
}
function unregister() {
tableWidget["cellClicked(int,int)"].disconnect(cellClicked);
}
At runtime when the page is loaded can be appears like this:
The QTableView only draws video lines, so better with large amounts of data. It has no signal to connect.
It only shows data; all the logic must be done on the source data structure (ie: sort, add, delete row), then refresh the table.
var list = [];
var cellRow = -1;
var cellCol = -1;
function loadData() {
//fill the data structure
}
//call this function when the data structure is modified
function refreshTable() {
guiInterface("qtableview::tableView", "repaint");
}
//draw the cell
function getTableItem(row, col) {
var cellData = {
row: 0,
col: 0,
icon: "",
iconPos: "top",
text: "",
backGround: "",
foreGround: "",
};
cellData.row = row;
cellData.col = col;
cellData.text = list[row][col];
return cellData;
}
var columnsWidth = [50, 100, 100, 250, 100];
var columnsHeader = ["a", "b", "c", "d", "e"];
function register() {
loadData();
guiInterface("qtableview::tableView", "register_callback", "getTableItem");
guiInterface("qtableview::tableView", "setRowCount", list.length);
guiInterface("qtableview::tableView", "setColCount", columnsHeader.length);
guiInterface("qtableview::tableView", "setRowsHeight", 25);
guiInterface(
"qtableview::tableView",
"sethorizontalheaderlabels",
columnsHeader
);
for (i = 0; i < columnsWidth.length; i++)
guiInterface("qtableview::tableView", "setColWidth", i, columnsWidth[i]);
}
To edit cell text, the timer function check which operation is made on cells, in order to expand cell or edit it.
function timer() {
var cellData = guiInterface("qtableview::tableView", "currentCellInfo");
//first click, expand selected cell height
if (cellData.row != cellRow) {
if (cellRow >= 0)
guiInterface("qtableview::tableView", "setRowHeight", cellRow, 25);
guiInterface("qtableview::tableView", "setRowHeight", cellData.row, 100);
cellRow = cellData.row;
}
if (cellData.col != cellCol) {
cellCol = cellData.col;
}
var editPending = guiInterface("qtableview::tableView", "editPending");
if (editPending.x != -1) {
var rect = guiInterface(
"qtableview::tableView",
"visualRect",
editPending.x,
editPending.y
);
//open keyboard or do something with the cell
guiInterface("inputcontext", "string", cellData.text, "cellEdited", rect);
}
}
Once the keyboard is closed, a callBack cellEdited will executed, to manage the new data.
function cellEdited(value) {
list[cellRow][cellCol] = value;
guiInterface("qtableview::tableView", "repaint");
}
Dialog popup
All user interface pages are displayed in full screen mode. To show a popup windows a dialog popup must be created.
Click the New button and select Dialog without Buttons, the press Create. The other Dialog type don't work as well, then do not use.
In the parent form add a link button to open the dialog or a tool button connected to javascript function that set parameters and open it.
function openClicked() {
guiInterface("setglobal", "parameterName", "parameterValue");
guiInterface("system::linkPage", "dialog.ui");
}
function register(path) {
btnOpen["clicked(bool)"].connect(openClicked);
}
function unregister(path) {
btnOpen["clicked(bool)"].disconnect(openClicked);
}
To send data to the parent form, values must set as global variables, beacuse there isn't a direct communication between pages.
When the dialog is closed, the parent for will be reloaded.
Save the parent data before invoke the dialog. Unsaved data (setglobal) will be lost!
Add two QToolButton to have the OK/Cancel functionality.
Connect them via javascript with two functions to manage the buttons click event.
function okClicked()
{
}
function cancelClicked()
{
}
function register(path)
{
btnOk["clicked(bool)"].connect(okClicked);
btnCancel["clicked(bool)"].connect(cancelClicked);
var parameter =`guiInterface("getglobal", "parameterName");
}
function unregister(path)
{
btnOk["clicked(bool)"].disconnect(okClicked);
btnCancel["clicked(bool)"].disconnect(cancelClicked);
}
Then drop all the widgets you need to desing the page, like comboBoxes or text fields.
The cancelClicked function is a simple reload of the parent from
The okClicked function can have the code to execute, and can set parameters to bring back to parent form.
function okClicked() {
guiInterface("setglobal", "dialogAction", "OK");
guiInterface("setglobal", "dialogValue", "the_result_value");
guiInterface("system::linkPage", "");
}
function cancelClicked() {
guiInterface("system::linkPage", "");
}
In the parent formregister function, the result data can be managed.
function register(path) {
btnOpen["clicked(bool)"].connect(openClicked);
var dialogAction = guiInterface("getglobal", "dialogAction");
if (dialogAction == "OK") {
// here the callback code
var dialogValue = guiInterface("getglobal", "dialgValue");
guiInterface("gui::messageBox", "Warning", dialogValue);
guiInterface("removeglobal", "dialogAction");
guiInterface("removeglobal", "dialogAction");
}
}
To monitoring PLC’s status, Sirius-ES has developed a component for monitoring variables status.
The variable list is written in the xml file called status.xml.
<?xml version="1.0" encoding="windows-1250"?>
<PlcStatus>
<Variable name="VARIABLE_NAME_1" text="MESSAGE TEXT 1"></Variable>
<Variable name="VARIABLE_NAME_2" text="MESSAGE TEXT 2"></Variable>
</PlcStatus>`
The QHmiPlcStatus custom component read this file and write on its “destination” if the text in the variable is set to TRUE. The order priority is the same of the file. It means that VARIABLE_NAME_1 is shown before of VARIABLE_NAME_2 if they are set at the same time.
Wire QHmiPlcStatus to another component (like QLabel) to show the status text.
During runtime, when a variable contained into xml file is set to true, “TextLabel” show the message.
Pages can be included into other page, using the “@frame::” keyword.
Insert a QLabel component into page and set text “@frame::pageToInclude.ui”.
**WARNING:**javascript of included file IS NOT EXECUTED. Only the main page javascript runs. See the next example to work with javascript into included frames.
Incuding file into others can be utils to create common parts like headers and tool bars.
But they can not have javascript. To use from javascript on their widgets, these are to be found in the page.
Use unique names for included widget, in order of to not have conflict with other widget into the main page.
If the included page is in ALL the pages (like headers or footers) the code can be written into the system.js file. This file works across all the pages of the application.
In the example into the included file (IE: a header) a led change its color reading a variable:
var isReady = false;
function register(path) {
guiInterface("vars::add_variable", "VAR_NAME");
}
function timer() {
//if is not ready, the variabale can not be read from javascript
if (!isReady) return;
if (form != null) {
if (form.findChild("labelLedStatus") != null) {
var value = guiInterface("vars::number_value_read", "VAR_NAME");
if (value == 2) {
form.findChild("`labelLedStatus`").styleSheet =
"background-image: url('../res/leds/led_green.png'); " +
"background-position: center;" +
"background-repeat: no-repeat;";
} else if (value == 1) {
form.findChild("`labelLedStatus`").styleSheet =
"background-image: url('../res/leds/led_yellow.png'); " +
"background-position: center;" +
"background-repeat: no-repeat;";
} else {
form.findChild("`labelLedStatus`").styleSheet =
"background-image: url('../res/leds/led_red.png'); " +
"background-position: center;" +
"background-repeat: no-repeat;";
}
}
}
}
function values_changed(vars, changed) {
isReady = true;
}
If needed to show a message with a index this is a possible way to show it. In this way is possible to show messages without the comboBox Arrow layout, and set a style for the label message with javascript.
Drag a QHmiComboBox and a QLabel into form. Then wire them from combo**currentIndexChanged(QString)**to label setText(QString). When the combo changed value, the label will change too.
Set the fieldName property for comboBox, then right Click on the widget and select Edit Items...
Insert the items to show (translatable) and press ok.
Now right click again and select Edit javascript... in the register function write:
function register(path) {
label.Text = qHmiComboBox.currentText;
qHmiComboBox.visible = false;
}
The first time the combo is not changing! The code force the label text and hide the combo.
If variable initialization is needed on page load, is not possible to set vars directly into register function.
- when the page is loading, the register function is called by the system.
- Then it tells to PLC which variables are requested from javascript code but the are with any value.
- PLC response contains the current values of requested vars.
- values_changed function is called when values are loaded
- now variable are accessible from code
To initialize vars at first time here an example:
var firstTime = true;
function register(path) {
guiInterface("vars::add_variable", "VARIABLE_NAME");
}
function values_changed(vars, changed) {
if (firstTime) {
//write init value
guiInterface("vars::string_value_write", "VARIABLE_NAME", stringValue);
firstTime = false;
//now this code is not execute on next value_changed call
}
}
When a variable change its value, a field can assume a different aspect to visualize a status.
Every type of data can be used.
Register the variable in the register function in order to use it in the page.
In the values_changed function test if the variable is changed and check its value.
Do something with the specified value.
NOTE: when the page is load, all the variables are changed because their initial value is "undefined".
NOTE: if the variable is tested from the changed array, all the values are STRING.
If the real type is needed, useguiInterface("vars::xxx_value_read", "VARIABLE") ;
function register(path) {
//register the variable to test`
guiInterface("vars::add_variable", "VARIABLE_TO_TEST");
}
function values_changed(vars, changed) {
//check if variable is changed
if (typeof changed["VARIABLE_TO_TEST"] != "undefined") {
//check the value and do something
if (changed["`VARIABLE_TO_TEST`"] == "TRUE")
widgetToChange.styleSheet = "background-color: rgb(0, 255, 0);";
else widgetToChange.styleSheet = "background-color: rgb(255, 0, 0);";
}
}
Files operations are moresimpler in javascript than in PLC code.
Data can be loaded and saved into a javascript file and passed to PLC to execute its code.
Every file to manage must be registered with theQFile::register function. When finish, the file must be unregistered withQFile::unregister
To load or delete file, before open it, check if exists withQFile::exists
if (guiInterface("QFile::exists", FILE_FULL_PATH)) {
var fd = guiInterface("QFile::register", FILE_FULL_PATH);
//execute the file operations
guiInterface("QFile::unregister", fd);
}
After this, the file can be opened to read or write
//read file
var fd = guiInterface("QFile::register", FILE_FULL_PATH);
var result = guiInterface("QFile::open", fd, ReadOnly);
if (result == 1) {
while (guiInterface("QFile::atEnd", fd) == false) {
var readLine = guiInterface("QFile::readLine", fd);
//do something...
}
guiInterface("QFile::close", fd);
}
guiInterface("QFile::unregister", fd);
//write file
var fd = guiInterface("QFile::register", FILE_FULL_PATH);
var result = guiInterface("QFile::open", fd, WriteOnly | Truncate);
if (result == 1) {
//write the file content
guiInterface("QFile::writeLine", fd, content);
guiInterface("QFile::close", fd);
}
guiInterface("QFile::unregister", fd);
//append to file
var fd = guiInterface("QFile::register", FILE_FULL_PATH);
var result = guiInterface("QFile::open", fd, WriteOnly | Append);
if (result == 1) {
//append a row
guiInterface("QFile::writeLine", fd, rowToAppend);
guiInterface("QFile::close", fd);
}
guiInterface("QFile::unregister", fd);
Files can be deleted in the same way
if (guiInterface("QFile::exists", `FILE\_FULL\_PATH`)) {
var fd = guiInterface("QFile::register", FILE_FULL_PATH);
guiInterface("QFile::remove", fd);
guiInterface("QFile::unregister", fd);
}
File operation can be done with the internal memory or USB devices.
Before access to the USB pendrive, check if it is mouted by the system.
This operaion can be done in the timer() function, in order to refreshperiodically the USB status.
In the example, A led turn green if a USB device is present, and turn red if not.
function timer() {
var usb = guiInterface("system::usbdrive") == "" ? false : true;
if (usb) label.styleSheet = "background-image: url('../res/LED_GREEN.png');";
else label.styleSheet = "background-image: url('../res/LED_RED.png');";
}
To show graphically a boolean value on screen, is possible to show it like a led.
It can be only ON-OFF or with two differen colors (GREEN=ON, RED=OFF) to show the variable state.
It can be done in two ways:
Drag a QHmiButton in page and associate it to the boolean variable you need to show.
Uncheck the enabled property in the Qwidget section.
In the QAbstractButton section choose and image in Nornal ON and Normal OFF properties
Do the same for Disable ON and Disable OFF properties with the same images
Set a size fo the image in iconSize->Width andiconSize->Heigth
Set this CSS stylesheet to the button (rigth click on button and select Change styleSheet...)
QHmiButton {
border: none;
background: transparent;
padding: 0;
}
QHmiButton:pressed {
border: none;
background: transparent;
padding: 0;
}
QHmiButton:checked {
border: none;
background: transparent;
padding: 0;
}
This code can be applied to the buttons parent. Remenber that ALL QHmiButton in the parent hinerit this style.
Drag a QHmiButton in page and associate it to the boolean variable you need to show.
Uncheck the enabled property in the Qwidget section.
Set this CSS stylesheet to the button (rigth click on button and selectChange styleSheet...)
QHmiButton {
border: none;
background-color: rgb(0, 255, 0);
padding: 0;`
}
QHmiButton:checked {
border: none;
background-color: rgb(255, 0, 0);
padding: 0;
}