Banana.Ui

This class Banana.Ui contains methods to interact with user interface.

Add Message 

You can display information to the user by using the following functions:

Simple Display and Input 

For simple display and input of data from the user you can use the Banana.Ui Methods likes:

  • showText()
    Show the given text in a dialog with the given title.
  • showQuestion()
    how the user a question dialog with Yes and No buttons.
  • getText()
    Show the user a dialog asking to insert a text.
  • getDouble()
    Show the user a dialog asking to insert a double.
  • getItem()
    Show the user a combo box dialog asking to select an item from a list. 

Structured data input

The openPropertyEditor() function create a structured dialog for entering data. 

It is particularity useful for letting the user customize the extension. 

Creating Widgets

With QT Creator you can design any kind of widget and use it in the extension to visualize o get data from the user.

 

 

 

Banana.Ui Methods

This class Banana.Ui contains methods to interact with user interface.

createPropertyEditor(title, params, [dialogId])

Create a dialog to set given params. To display the dialog use the method exec(). To get the modified parameters use the method getParams(). 
It used in combination of the openPropertyEditor.

For more information on how tu use see:

Public slots:

  • void addCustomCommand(functionName, commandLabel)
    //allows to call a function of the script from a command in the dialog
  • void addImportCommand()
    //allows to import the extension's settings from other files
  • QJSValue getParams()
    //returns the params of the dialog
  • void setParams(QSJValue params)
    //allows to set the params of the dialog

 

createUi(uiFilePath)

Read the file uiFilepath and return an object representing the dialog. The uiFileName has to be in the same directory as the running script. You can also load a ui file from the table documents with the prefix 'documents:', ex.: 'documents:calculator.ui', For details and examples see Script dialogs or the tutorial JavaScript Tutorial 1.

Every widget defined in the ui file is exposed to the script environment and you can interact with them like in c++ code. You find further details under Banana.Ui.Widget.

If an error occurred undefined is returned.

Example:


@includejs = ch.banana.calculator.dialog.js;  // Define the class Calculator
                                              // that control the .ui file
...
var calculatorUi = Banana.Ui.createUi("ch.banana.calculator.dialog.ui");
var calcJs = new Calculator(calculatorUi); 
Banana.application.progressBar.pause();    // Pause wait cursor
calclatorUi.exec();                        // Show the dialog
Banana.application.progressBar.resume();   // Restart wait cursor

getDouble(title, label [, value , min, max, decimals])

Show the user a dialog asking to insert a double. Return the inserted double or undefined if the user clicked cancel.


var a = Banana.Ui.getDouble("Title text", "Label text");
var b = Banana.Ui.getDouble("Title text", "Label text", "10.0");

getInt(title, label [, value, min, max, steps])

Show the user a dialog asking to insert an integer. Return the inserted integer or undefined if the user clicked cancel.


var a = Banana.Ui.getInt("Title text", "Label text");
var b = Banana.Ui.getInt("Title text", "Label text", "5", "1", "10","1");

getItem(title, label, items [, current, editable])

Show the user a combo box dialog asking to select an item from a list. Return the selected item or undefined if the user clicked cancel.


var value = Banana.Ui.getItem("Input", "Choose a value", ["a","b","c","d","e"], 2, false);

getItems(title, label, items [, selectedItems])

Show the user a list dialog asking to select one or more items from a list. Return the selected items or undefined if the user clicked cancel.


var value = Banana.Ui.getItems("Input", "Choose one or more values", ["a","b","c","d","e"], ["b","c"]);

Since: Banana Experimental 9.1.0

getPeriod(title, startDate, endDate [, selectionStartDate, selectionEndDate, selectionChecked])

Show the user a dialog asking to select a period like the tab Period. Return an object with the atributes 'startDate', 'endDate' and 'hasSelection' or undefined if the user clicked cancel. Date values are in the format "YYYY-MM-DD".


var period = Banana.Ui.getPeriod("Title text", "2016-01-01", "2016-12-31");
if (period) {
    var selectedStartDate = period.startDate; // return the start date of the selected period
    var selectedEndDate = period.endDate; // return the end date of the selected period
}

getText(title, label [, text])

Show the user a dialog asking to insert a text. Return the inserted text or undefined if the user clicked cancel.


var text = Banana.Ui.getText("Title text","Label text");

openPropertyEditor(title, params, [dialogId])

For more information:

Show the user a dialog asking to set given params.

Return the modified params or undefined if the user clicked cancel.

 

banana setting dialog

showHelp(uiFileName)

Show the help of a dialog. The help is loaded from the Banana.ch web site.

showInformation(title, msg)

Show the user an information dialog.


Banana.Ui.showInformation("Information", 'Insert here the text of the information.');

showQuestion(title, question)

Show the user a question dialog with Yes and No buttons. Return true if the user clicked Yes, otherwise false.


var answer = Banana.Ui.showQuestion("Question title", "Insert here the text of the question");

showText(text)

Show the given text in a dialog. The text can be plain text of html and span over multiple lines. If the text is in html the title is taken form the html. The dialog enables the user to save the content in the formats html, pdf, odf and txt.
The use of pixels to set the font sizes is not supported, the text is not rendered properly.


// Normal text
Banana.Ui.showText("Insert here the text.");
// Html text
Banana.Ui.showText('<html><header><title>This is title</title></header><body>Hello world</body></html>');

showText(title, text)

This is an overloaded function.

Show the given text in a dialog with the given title. The text can be plain text of html and span over multiple lines. The dialog enables the user to save the content in the formats html, pdf, odf and txt.

showText(title, text, options)

This is an overloaded function.

Show the given text in a dialog with the given title. The text can be plain text of html and span over multiple lines. The dialog enables the user to save the content in the formats html, pdf, odf and txt.

Through the object options it is possible to set the following additional parameters:

  • codecName: the name of the codec to be used in case the content will be saved as txt file. Default is 'UTF-8'
  • outputFileName: the file name without path to be used in case the content will be saved. The path is current open document path or the user's document path.

var options = {
   'codecName' : "latin1", // Default is UTF-8
   'outputFileName' : "prova.txt"
}
Banana.Ui.showText("Title", "some text...", options);

Extension Widget Ui Example

This is an example dialog to search in the whole accounting a text.

This example is also found in the tutorial file JavaScript Tutorial 1 under the table documents at code 742.

The dialog:

All the properties and public slots of the widgets in the dialogs will be accessible from the script.

Example UI code

The script file ch.banana.scripts.find.js 

  • Display the the Ui Widget using the Banana.Ui.createUi().
  • Get the text from the user-
  • Use the function searchInTables() 
  • Notify the user when the text has been found with the table.addMessage() function

 

/**
 * This example search a text in all the tables of the document,
 * and show the matches in the messages pane.
 */
// @id = ch.banana.scripts.find
// @version = 1.3
// @date = 2019-12-06
// @publisher = Banana.ch SA
// @description = Find in whole accounting
// @description.it = Cerca in tutta la contabilità
// @description.de = Suchen in der gesamten Buchhaltung
// @description.fr = Chercher dans toute la comptabilité
// @task = app.command
// @doctype = nodocument
// @inputdatasource = none
// @timeout = -1

/**
 * param values are loaded from Banana.document, edited through dialog and saved to Banana.document
 * This array object is like a map (associative array) i.e. "key":"value", see initParam()
 * Examples of keys: searchText, wholeText, ...
 */
var param = {};

/** Dialog's functions declaration */
var dialog = Banana.Ui.createUi("ch.banana.scripts.find.ui"); // use "documents:742.ui" in template file

/** Dialog Objects declaration */
var findNextButton = dialog.findChild('findNextButton');
var helpButton = dialog.findChild('helpButton');
var closeButton = dialog.findChild('closeButton');
var searchTextLineEdit = dialog.findChild('searchTextLineEdit');
var matchCaseCheckBox = dialog.findChild('matchCaseCheckBox');
var wholeTextCheckBox = dialog.findChild('wholeTextCheckBox');


dialog.checkdata = function () {
    var valid = true;

    if (searchTextLineEdit.text.length <= 0) {
        Banana.Ui.showInformation("Error", "Search text can't be empty");
        valid = false;
    }

    if (valid) {
        dialog.accept();
    }
};

dialog.showHelp = function () {
    Banana.Ui.showHelp("ch.banana.script.find");
};

dialog.closeDialog = function () {
    dialog.close();
};

/** Dialog's events declaration */
findNextButton.clicked.connect(dialog, dialog.checkdata);
helpButton.clicked.connect(dialog, dialog.showHelp);
closeButton.clicked.connect(dialog, dialog.closeDialog);

/** Main function */
function exec(inData) {

    //calls dialog
    // var rtnDialog = true;
    var rtnDialog = dialogExec();

    //search text in the whole accounting
    if (rtnDialog && Banana.document) {
        Banana.document.clearMessages();
        searchInTables();
    }
}

/** Show the dialog and set the parameters */
function dialogExec() {
    // Read saved script settings
    initParam();
    if (Banana.document) {
        var data = Banana.document.getScriptSettings();
        if (data.length > 0) {
            param = JSON.parse(data);
        }
    }

    // Text at cursor position
    var cursor = Banana.document.cursor;
    var columnName = cursor.tableName === 'Documents' && cursor.columnName == 'Attachments' ? 'Description' : cursor.columnName;
    param["searchText"] = Banana.document.value(cursor.tableName,cursor.rowNr,columnName);
    searchTextLineEdit.text = param["searchText"];
    // Set dialog parameters
    if (param["matchCase"] == "true")
        matchCaseCheckBox.checked = true;
    else
        matchCaseCheckBox.checked = false;
    if (param["wholeText"] == "true")
        wholeTextCheckBox.checked = true;
    else
        wholeTextCheckBox.checked = false;

    Banana.application.progressBar.pause();
    var dlgResult = dialog.exec();
    Banana.application.progressBar.resume();

    if (dlgResult !== 1)
        return false;

    // Read dialog parameters
    param["searchText"] = searchTextLineEdit.text;
    if (matchCaseCheckBox.checked)
        param["matchCase"] = "true";
    else
        param["matchCase"] = "false";
    if (wholeTextCheckBox.checked)
        param["wholeText"] = "true";
    else
        param["wholeText"] = "false";

    // Save script settings
    var paramString = JSON.stringify(param);
    var value = Banana.document.setScriptSettings(paramString);

    return true;
}

/** Initialize dialog values with default values */
function initParam() {
    param = {
        "searchText": "",
        "matchCase": "false",
        "wholeText": "false"
    };
}

/** Search a text in the accounting's tables */
function searchInTables() {
    var searchText = param["searchText"];
    if (param["matchCase"] === "false")
        searchText = searchText.toLowerCase();
    var tables = Banana.document.tableNames;
    // Tables
    for (var t=0; t < tables.length; t++) {
        var table = Banana.document.table(tables[t]);
        var columns = table.columnNames;
        // Rows
        for (var r=0; r < table.rowCount; r++) {
            // Columns
            for (var c=0; c < columns.length; c++) {
                var textFound = false;
                var text = table.value(r, columns[c]);
                if (param["matchCase"] === "false")
                    text = text.toLowerCase();
                // Find text
                if (param["wholeText"] === "true") {
                    if (text === searchText)
                        textFound = true;
                } else {
                    if (text.indexOf(searchText) >= 0)
                        textFound = true;
                }
                // Show message
                if (textFound) {
                    table.addMessage("Text \"" + param["searchText"] +
                        "\" found in \"" + table.value(r, columns[c]) + "\"", r, columns[c]);
                }
            }
        }
    }
}

 

 

UI file 

The UI file has been created using QT Creator 

  • Install Qt Creator
  • Draw the dialog with Qt Creator
  • Save the dialog in a .ui file,
  • Load the .ui file in the script through the function Banana.Ui.createUi()

The .ui file: ch.banana.scripts.find.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>DlgFind</class>
 <widget class="QDialog" name="DlgFind">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>438</width>
    <height>193</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Find</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <item>
    <layout class="QGridLayout" name="gridLayout">
     <property name="horizontalSpacing">
      <number>40</number>
     </property>
     <item row="0" column="0">
      <widget class="QLabel" name="searchTextLabel">
       <property name="text">
        <string>Search &amp;text</string>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QLineEdit" name="searchTextLineEdit"/>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QGroupBox" name="groupBox">
     <property name="title">
      <string>Options</string>
     </property>
     <property name="flat">
      <bool>false</bool>
     </property>
     <property name="checkable">
      <bool>false</bool>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <widget class="QCheckBox" name="matchCaseCheckBox">
        <property name="text">
         <string>&amp;Match case</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QCheckBox" name="wholeTextCheckBox">
        <property name="text">
         <string>&amp;Whole text</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>15</height>
      </size>
     </property>
    </spacer>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <spacer name="horizontalSpacer">
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>80</width>
         <height>20</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="findNextButton">
       <property name="text">
        <string>&amp;Find</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="helpButton">
       <property name="text">
        <string>Help</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="closeButton">
       <property name="text">
        <string>Close</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <tabstops>
  <tabstop>matchCaseCheckBox</tabstop>
  <tabstop>findNextButton</tabstop>
 </tabstops>
 <resources/>
 <connections/>
</ui>

Banana.Ui.Widget

Every widget defined in a ui file is exposed to the script engine. 

You can get a particular widget through it's name and the method findChild:

For every widget you have access to:

  • properties
  • public slots
  • signals

If a method is not defined as public slot in c++ qt api,  it will not be available to the script engine. Some widgets are wrapped so that you have access to additional methods, see next chapter Wrapped widgets.

For complete examples see the page Script dialogs or the tutorial JavaScript Tutorial 1 (documents 740 'QTableWidget interaction' and 742 'Find dialog').

Example:



// Load ui file 
var dialog = Banana.Ui.createUi("ch.banana.scripts.find.ui"); 

// Get the label/widget through it's name
var statusLabel = dialog.findChild('statusLabel');

// Set the text of label/widget
statusLabel.setText('Hello!');

// Connect button box accepted signal to the dialog close method
var buttonBox = dialog.findChild("buttonBox");
buttonBox.accepted.connect(function() {dialog.close();});

Banana.application.progressBar.pause();  // Pause wait cursor 
dialog.exec();                           // Show the dialog 
Banana.application.progressBar.resume(); // Restart wait cursor

Signals

You have three ways to connect signals to methods.



// Method1: Connect a signal to a function
function findNext() {
   Banana.console.log('find next');
} 
findNextButton.clicked.connect(findNext);

// Method2: Connect a signal to an object method
var object = {}
object.onClicked = function() { 
   Banana.console.log('clicked');
};
findNextButton.clicked.connect(object, object. onClicked);

// Method3: Connect a signal to an inline function 
findNextButton.clicked.connect(
   function(){Banana.console.log('clicked');}
);

 

 Wrapped widgets

The widgets listed below have been wrapped to let you use additional methods that are not exposed to the script engine because they are not declared as public slots.

Hereby only the additional available methods are listed. Their usage and parameters correspond to the c++ counterpart.

QObject

Methods:



findChild(name)

QComboxBox

Methods:



addItem(text)
addItems(texts)
count()
insertItem(index, text)
insertItems(index, texts)
removeItem(index)

QTableWidget

Since: Banana Experimental 9.1.0

Properties:



currentColumn
currentRow

Methods:



currentItem()
item(row, column)
setColumnWidth(column, width)
setEditTriggers(triggerFlag)
setHorizontalHeaderLabels(labels)
setColumnCount(columns)
setCurrentCell(row, column)
setRowCount(rows)

QTableWidgetItem

Properties:



background
checkState
flags
foreground
text

Extensions Dialog PropertyEditor

The Dialog Property Editor allows to create a flexible dialog that allows the user to enter data.
The structure and content of the Dialog is defined with a JSon.

It is useful for extensions settings and in particulary in the  settingsDialog() function.

 

 

createPropertyEditor(title, params, [dialogId])

Create a dialog to set given params. To display the dialog use the method exec(). To get the modified parameters use the method getParams(). The structure of params is described in the openPropertyEditor paragraph.

Public slots:

  • void addCustomCommand(functionName, commandLabel)
    //allows to call a function of the script from a command in the dialog
  • void addImportCommand()
    //allows to import the extension's settings from other files
  • QJSValue getParams()
    //returns the params of the dialog
  • void setParams(QSJValue params)
    //allows to set the params of the dialog

function exec(inData, options) {
    let params = {};
    params.version = '1.0';
    params.data = [];
    let editor = Banana.Ui.createPropertyEditor('Dialog Title', params, 'pageAnchor');
    let param = {};
    param.name = 'header';
    param.title = 'header';
    param.type = 'string';
    param.value = 'this is an example of header';
    param.defaultvalue = 'my header';
    param.readValue = function () {
        param.header = this.value;
    }
    params.data.push(param);
    editor.setParams(params);
    editor.addImportCommand();
    editor.addCustomCommand("functionName1", "label pushbutton 1");
    editor.addCustomCommand("functionName2", "label pushbutton 2");
    let rtnValue = editor.exec();
    if (parseInt(rtnValue) === 1) {
        // Read data from dialog
        params = editor.getParams();
        Banana.console.debug(JSON.stringify(params));
    }
}
function functionName1(params) {
    Banana.console.debug("called functionName1");
    Banana.console.debug(JSON.stringify(params));
    for (var i = 0; i < params.data.length; i++) {
        if (params.data[i].name === 'header') {
            params.data[i].value = 'hello world';
        }
    }
    return params;
}
function functionName2(params) {
    Banana.console.debug("called functionName2");
    Banana.console.debug(JSON.stringify(params));
    return params;
}

 

 

openPropertyEditor(title, params, [dialogId])

 

Show the user a dialog asking to set given params.

Return the modified params or undefined if the user clicked cancel.
See Invoice Extension containing examples with this method.

Params is an object containing:

  • version
    A string with a version number. Required '1.0';
  • data
    An array containing the elements of the property editors.

Element properties of the array

  • name: param's key (unique id)
  • title: param's title, which appears in the left column "property"
  • parentObject (optional):
    • Should be the name (id) of the parent's element.
    • It let you define a tree structure, as the following one:

banana setting dialog

see the structure example on GitHub

  • type (optional):
    • string (default)
      A single line of text
    • multilinestring
      A text area with multiple lines
    • bool 
      checkbox for setting true/false value
    • date
      calendar popup for date selection
    • combobox
      Use the property items for setting a list of values
    • color
      Color dialog box for color selection
    • font
      Combobox that lets the user select a font family
       
  • value: the value, which appears in the right column "value". For type bool: true/false, for type string and multilinestring a text. For type combox the selected text.
  • defaultvalue (optional): value used by Restore Defaults button. If the param has this property the button Restore Defaults will be available.
  • items:  array of strings. Used by type combobox.
  • collapse (optional): true/false. By default the dialog expand all items. If collapse is true the item will not be expanded.
  • enabled (optional): true/false. If false the value will appears grey and the user cannot change the value (default: true)
  • editable (optional): true/false. If false the user cannot change the value, available only for string and multilinestring types (default: true)
  • tooltip (optional): text which appears over the item without clicking on the item
  • errorId (optional): error id that identify an error. The error id is used to link the error to the corresponding help page
  • errorMsg (optional): error string that describe an error. If a parameter has this property set, the label will be show in red and the error showed on the right of the name.

Validating data

Before accepting data you can validate them using the method validateParams(). If this method returns false the dialog will prevent to close.


function validateParams(params) {
   for (var i = 0; i <  params.data.length; i++) {
       if (params.data[i].value != "myValue") {
          params.data[i].errorMsg = "this object is not valid";
          return false;
       }
   }
   return true;
}

Example code openPropertyEditor


var paramList = {};
paramList.version = '1.0';
paramList.data = [];
// bool
var param = {};
param.name = 'print_header';
param.title = 'print header';
param.type = 'bool';
param.value = true;
param.readValue = function() {
  param.print_header = this.value;
}
paramList.data.push(param);
// combobox
var param = {};
param.name = 'methodId';
param.title = 'Method ID';
param.type = 'combobox';
param.value = 'item01';
param.items = ['item01', 'item02', 'item03'];
param.readValue = function () {
  param.methodId= this.value;
}
paramList.data.push(param);
var dialogTitle = 'Settings';
var pageAnchor = 'dlgSettings';
if (Banana.Ui.openPropertyEditor(dialogTitle, paramList, pageAnchor))) {
   for (var i = 0; i < paramList.data.length; i++) {
      // Read values to param (through the readValue function)
      paramList.data[i].readValue();
   }