Click here to Skip to main content
15,394,479 members
Articles / Programming Languages / R
Article
Posted 30 Sep 2019

Stats

5.4K views
134 downloads
4 bookmarked

A TCL-TK Form Generator

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
30 Sep 2019CPOL3 min read
A Tcl -Tk Form Generator that can be used alone or used to provide a fast Form generation for language in which this activity can be complex

Introduction

This article is about a form generator[1] (TK FormGen) developed in Tcl - Tk for creating and managing Forms from simple message box to relatively complex forms with text fields, combos, radio buttons and so on. TK FormGen can be used by TCL applications or invoked by other languages, in particular, those that don't have a native GUI; here, rather than an exhaustive presentation of the product[2] it is illustrated how it can be used by other languages.

Using the Program

The form generator is composed by the scripts fGen.tcl that contain the procedure for generating a Form and fGenProc.tcl which contains some procedures; this last is automatically included by fGen.tcl.
The form generator can be called:

  • by a Tcl script:
    proc cBack {data} {
    	foreach id [dict keys $data] {puts "$id\t : [dict get $data $id]"}
    	exit
    }
    append src [file dirname [file normalize [info script ]]] "/fGen.tcl"
    source $src
    set ask "Title,Form generalities;Ground,silver;"
    append ask "CMB,Theme,,,|alt|clam|classic|default|vista|winnative|xpnative;"
    append ask "CMB,Ground,,,|cyan|gray|magenta|olive|silver|teal;"
    append ask "CMB,Form,Form type,,|T:Texts|B:Buttons|C:Combo boxes|F:Files 
           and folders|S:Scales and combos;"
    append ask "Default,Theme:clam,Ground:teal;"
    formGen[list $ask cBack]
  • directly from command line, e.g. tclsh fgen.tcl "T,name,User name;P,psw,Enter password" data.json
  • by another language

Using Form Generator With Some Languages

Image 1

The examples deal with a form (see image above) that can be used for test widgets and also for exploring different form backgrounds and themes.

When the form is closed, the data are sent in Json format to standard output to be transformed in internal variables of the host language; below the list, contained in the file params.txt, for creating the form:

CMB,Theme;# themes are set at the beginning with the available themes;
Labels,Right, :;
Comment,Hexadecimal color value must have the form #RRGGBB;
CMT,Ground,,,|cyan|gray|magenta|olive|silver|teal|#C0D0E0;
CMB,Form,
Form examples,,|Texts|Buttons|Combo boxes|Files and folders|Scales and Check box;
T,try,Try this,400;
Default,Theme:clam,Ground:teal;
Validate,Ground is ^#[0-9a-fA-F]{6}$,Not valid color;
Default,try:C#2CThis is a form for test some widgets\nCondor Informatique - Turin#3b
CMB#2CGround#2C#2C#2C|cyan|gray|magenta|olive|silver|teal|#C0D0E0;

In all the languages with which TK FormGen was tested, the form was generated by executing the TCL interpreter via external command and data was read from the standard output and transformed in internal variables.
The command sent is: tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle where tclsh is the Tcl interpreter, fGen.tcl is the Tcl script that it generates a Form if it is invoked with appropriate parameters. handler.tcl contains a procedure handle for manage events, in particular, the event Start and the event Select of combo Form. The procedure tryForm is invoked when the form is closed and it generates a new form taking the data from the field try.

namespace eval handler {
	variable examplesMap [dict create \
			"Texts" "Text,Text;P,psw,Password,,Almost 6 characters;T,
             Mail,E mail,18;N,Number;IN,Integer,Integer Number;\
		     DN,DecNumber,Decimal Number;A,textArea,Text area,5;;
             Default,Mail:ross@lib.it,Integer:7,Number:-11,DecNumber:3.1415;" \
			"Scales and Check box" "CMB,greeck,Greek letters,,
             A:Alfa|Beta|G:Gamma|Delta;CMT,spinBox,Combo and text,,
             A:Alfa|Delta|G:Gamma|Omega|T:Tau;\
				Scale,Scale2,Scale 2,222,1 -1;QS,Urgency,,,
                White|Green|Yellow|Red;Default,Scale2:-0.35,Urgency:Yellow,ckBox:1; \
				Check,ckBox,Check box,,Accept news;Check,ckBox2,Check box2,,Agree;" \
			"Buttons" "P,psw,Password,18;RDB,Gender,,,M:Male|F:Female|
             U:Unspecified;B,infoButton,\u24d8,,infoPsw;After,infoButton,psw;\
				B,Start,\u270E;B,fg_Cancel,\u2718;B,fg_Reset,\u21B6;
                Default,Gender:M;Required,psw;Validate,psw is .{6#2c},Password too short;\
				RDB,Langue,,,us:img\\uss.png|fr:img\\frs.png;"\
			"Combo boxes" "CMB,greeck,Greek letters,,A:Alfa|Beta|G:Gamma|Delta;CMT,
             spinBox,Combo and text,,A:Alfa|Delta|G:Gamma|Omega|T:Tau;"\
			"Files and folders" "File,File,,40,Images:*.gif *.jpg *.png,All:*;
             Default,File:C:\\www\\condorinformatique\\nodejs\\tcl;D,
             Folder,,30;WF,wFile,Write File;"]
}
proc handle {e} {
	lassign $e event path name
	if {$event == "Start"} {
		# populate the combo Theme with themes supported 
		fg_setCombo Theme "[join [ttk::style theme names] "|"]"
	}
	if {$name == "Form" && $event == "Select"} {
		# Insert in text area a sample of form
		if {[valueOf $name] != ""} {setValue try 
           "[dict get $::handler::examplesMap [valueOf $name]]"}
	}
}
proc tryForm {data} {
	append prefix "Title,Form example of " [dict get $data "Form"] ";Ground," 
           [dict get $data "Ground"] "," [dict get $data "Theme"] ";Labels,Right, :;"
	set systemTime [clock seconds]
	append prefix "H,hField,[clock format $systemTime];" [dict get $data "try"]
	formGen [list "$prefix"]
}

Below is an example of output (Ruby).

>ruby tcltk.rb
{
  "fg_Button" => "Ok",
  "Text" => "Text",
  "psw" => "Lafayette77",
  "Mail" => "ross@lib.it",
  "Number" => -11,
  "Integer" => 17,
  "DecNumber" => 3.1415,
  "textArea" => "big\ttext\n\n"
}
Sum: 9.141500
>Exit code: 0

Using With Languages

C++

The use of FGen in C ++ 11 was the most laborious; C++ was chosen instead of C for its support of regular expressions and map structure.

I didn't use the Json libraries to avoid including third-party libraries, on the other hand, I only needed to read a simple key-value structure so I preferred to use a regular expression to extract the key-value pairs and insert them in a map (function json2Map).

C++
// C++ 11
#include <bits stdc="">
using namespace std;
// function to find all the matches
map<string, string=""> <code>json2Map</code>(string jsonData) {
    map<string, string=""> dataMap;
    string regExspr("\\\"(\\w+)\\\":\\s*(\\\"([^\"]*)\\\"|(([+-]?\\d*)(\\.\\d+)?))");
    regex re(regExspr);
    for (sregex_iterator it = sregex_iterator
        (jsonData.begin(), jsonData.end(), re);it != sregex_iterator(); it++) {
            smatch match;
            match = *it;
            int indexValue = 3;
            if (match.str(3) == "") indexValue = 2;
            cout << match.str(1) << "\t" << match.str(indexValue) << endl;
            dataMap[match.str(1)] =  match.str(indexValue);
    }
    return dataMap;
}
int main( int numberArgs, char* argsList[] ) {
    vector<string> params{ "fGen.tcl","T,t1;N,n1", "", "" };
    int commandLength = 40; // roughly default + fixed (i.e. tclsh ... >j.json)
    for (int i=1; i<numberargs; i=""> j.json", params[0].c_str(), 
         params[1].c_str(),params[2].c_str(),params[3].c_str());
    int status = system(tcl);
    ifstream jsonFile("j.json");
    string jsonData {istreambuf_iterator<char>(jsonFile), istreambuf_iterator<char>()};
    cout << jsonData << endl;
    map<string, string=""> dataMap = json2Map(jsonData);
    if (dataMap.count("Integer") > 0) {
        cout << "Sum: " << (atof(dataMap["Integer"].c_str()) + 
        atof(dataMap["Number"].c_str()) + atof(dataMap["DecNumber"].c_str())) << endl;
    }
	return 0;
}
Node
JavaScript
const args = process.argv.slice(2)
const {exec} = require('child_process');
var command = 'tclsh ' + args[0] + ' "' + args[1] + '" ' + args[2] + ' "' + args[3]
const tcl = exec(command, function (error, stdout, stderr) {
	if (error) {    
		console.error(`exec error: ${error}`);
		return;
	}
	console.log('Child Process STDOUT: '+stdout); 
});
let jsonData = '';
tcl.stdout.on('data', (chunk) => {
  jsonData += chunk.toString();
});
tcl.on('exit', () => {
	console.log(jsonData);
	var data = JSON.parse(jsonData)	// the data becomes a JavaScript object 
	if ("Integer" in data) {
			console.log("Sum " + (data.Integer + data.Number + data.DecNumber))
	}
});
Python
import os
import subprocess
import json
def main():
    pass
os.chdir("C:\\Sviluppo\\fGenTk\\")
p = subprocess.Popen("tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle", 
                      shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
data = json.loads(stdout.decode('utf-8'))
print(data)
if 'Integer' in data:
   print ("Sum: {}".format(data['Integer'] + data['Number'] + data['DecNumber']))
if __name__ == '__main__':
    main()
R
library("rjson")
setwd("C:\\Sviluppo\\fGenTk")
parameters <- c("fGen.tcl", "params.txt", "handler.tcl::tryForm", "handler.tcl::handle")
json <- system2("tclsh",parameters,stdout=TRUE)
tmp <- tempfile()
cat(json,file = tmp)
data <- fromJSON(readLines(tmp))
data$fg_Button
if ("Integer" %in% names(data)) 
	cat("Sum:",data$Integer + data$Number + data$DecNumber,"\n")
Ruby

Ruby has perhaps the easiest use of TK Fgen, with only two instructions, we get form and data:

require 'json'
require 'FileUtils'
FileUtils.cd('c:/Sviluppo/fGenTk')
output = `tclsh fGen.tcl params.txt handler.tcl::tryForm handler.tcl::handle`
data = JSON.parse(output) if output != ""
puts JSON.pretty_generate(data).gsub(":", " =>")
if data.has_key?("Integer") then
	puts sprintf("Sum: %f",data['Integer'] + data['Number'] + data['DecNumber'])
end

Note

  1. ^ This is one of some form generators (for Autoit, Powershell, B4A, B4J and JavaScript) which can be found on my site.
    Also, there are some articles on Codeproject:
    1. A JavaScript Form Generator
    2. A Form Generator for Android
    3. A PowerShell Form Generator [Tip/Trick]
  2. ^ The package and documentation can be download here.

History

  • 30th September, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Member 4206974
Software Developer Condor Informatique
Italy Italy
Computer literacy (software) : Languages: PHP, Javascript, SQL Autoit,Basic4Android; Frameworks: JOOMLA!
Teaching/Training skills on Office, WEB site development and programming languages.
Others : WEB site development.
UNDP Missions
feb – may 2003 Congo DR Bukavu: ground IT computer course
nov 2003 Burundi Bujumbura: Oracle Data Base course
feb 2005 Burundi Bujumbura: JAVA course
mar 2005 Mali Kati: MS Office course
oct 2006 Mali Kati: MS Office course
jun 2006 Burkina Faso Bobo Dioulasso: MS Office course
jun 2007 Burkina Faso Bobo Dioulasso: MS Office course
may 2007 Argentina Olavarria hospital: Internet application for access to medical records
apr 2008 Burkina Faso Ouagadougou: MS ACCESS and dynamic Internet applications
jun 2008 Niger Niamey: analysis of the computing needs of the Niamey hospital
may 2009 Burkina Faso Ouagadougou: MS ACCESS and dynamic Internet applications
oct 2010 Niger Niamey: analysis of the computing needs of the Niamey hospital (following)
Region Piedmont project Evaluation
mar 2006 Burkina Faso, Niger
mar 2007 Benin, Burkina Faso, Niger
sep 2008 Benin, Burkina Faso, Niger
Others
feb 2010 Burundi Kiremba hospital: MS Office course
feb 2011 Congo DR Kampene hospital: MS Office course

Comments and Discussions

 
-- There are no messages in this forum --