-
YAD - Yet Another (GUI) Dialog
05/09/2024 at 07:59 • 0 commentsYAD
I was looking for an upgraded Zenity when I found YAD.
I was looking for a version that allows boxes to be pre-populated with defaults.
YAD is a graphical dialog program for shell scripts.
Th first websiteI found was discussing Puppy Linux menus (https://www.linux.org/threads/create-a-simple-gui-to-run-scripts-from.44473/).
From there I when to http://smokey01.com/yad/, this was a basic beginners guide.
I tried a simple script:
#!/bin/bash RESULT=$(yad --borders=10 --title="simple form" --text="Please enter:"\ --form --field="Last Name": "AlanX" --field="First Repo" --field="Date"\ ) echo $RESULT
Worked great. YAD was even installed by default.
This website has all the documentation (https://yad-guide.ingk.se/YAD_Guide.html).
I found that the "HTML" option did not work, but I believe the option needs to be turned on during compilation.
The format of the results string will make interfacing in C simpler.
AlanX
-
Porting a Visual Basic Excel Program to C and Zenity
02/18/2022 at 04:56 • 0 commentsBoat Offset Table
I have an DeltaCAD macro for importing Boat Offset Tables from Excel as a CSV file. The macro creates 3D DXF models, unfolded panels and various sections.
The problem is that DeltaCad is Windows only and is not 100% WINE compatible (no macro support).
I ported the macro to Excel but I update my computer every few years so keeping a copy of Excel for Linux is a problem.
Thus I am slowly migrating my code to "C". I have already written a DXF Out header library to export a "primitive" version of DXF. Therefore I use QCad or DeltaCad to import my DXF files.
What I need is a simple GUI to input files names and parameters. (In the past I used a batch file, or xForms as a full featured GUI). Zenity is an acceptable GUI interface (not perfect but good enough).
Here is the Zenity function:
char** zenity(char* cmd,int n) { // create string array char** str=calloc(n+1,sizeof(char*)); for (int i=0;i<=n;i++) *(str+i)=(char*)calloc(256,sizeof(char)); char line[256]={0}; // Run Zenity FILE* fp=popen(cmd,"r"); if (fp==NULL) { // Error perror("Pipe returned a error"); exit(0); } else { fgets(line,sizeof(line),fp); sprintf(*str,"%d",WEXITSTATUS(pclose(fp))); } // Remove non-characters for (int i=0;(line[i]>0);i++) if (line[i]<32) line[i]=0; int ptr=1; char* token=line; for (int i=0;(line[i]>=32);i++) if (line[i]==',') { line[i]=0; strcpy(*(str+ptr),token); ptr++; token=&line[i+1]; } strcpy(*(str+ptr),token); return str; }
It is short and to the point. The main limitation is that the maximum line length is 256 characters (including the end of line marker).
Here is an example of use:
// Test if Zenity is installed (get version) if ((fp=popen("zenity --version","r"))!=NULL) { // Get CSV Input File parameters=zenity("zenity --file-selection \ --file-filter='Boat Offsets *.csv' \ --title='Select a CSV File' \ ",1); if (atoi(*parameters)==1) { // Cancel exit(0); } else { strcpy(offsetNamePath,*(parameters+1)); } // printf("str ptr %ld\n",(long)parameters); // for (int i=0;i<=1;i++) { // printf("%s %ld\n",*(parameters+i),(long)*(parameters+i)); // } for (int i=0;i<=1;i++) free(*(parameters+i)); free(parameters); ...
The first zenity call "zenity --version" checks if zenity is installed.
if ((fp=popen("zenity --version","r"))!=NULL) {
The second zenity call "zenity --file-section ..." gets a CSV file for processing:parameters=zenity("zenity --file-selection \ --file-filter='Boat Offsets *.csv' --title='Select a CSV File' \ ",1);
The last parameter of the zenity call is 1, meaning only one parameter is returned, accessed as string "*(parameter+1)". Actually, two parameters are returned, the exit code is accessed as the string "*parameter". This code prints out the parameters:
for (int i=0;i<=1;i++) { printf("%s\n",*(parameters+i)); }
Finally, when your finished with the parameter, you should free the memory space with:
for (int i=0;i<=1;i++) free(*(parameters+i)); free(parameters);
What does the GUI look like?
Here is the file selection GUI:
And here is the Options form:
The only problem with the form is that I cannot populate the entry boxes with default values.
Reading Comma Separated Values (CSV) in C
Reading CSV files is quite difficult as CSV is not really a standard. How should we read:
1,"hello, ",, A B ,
1 Is the second comma part of the string of a separator?
2 How should we deal with leading spaces?
3 How should we deal with embedded spaces?
4 How should we deal with trailing spaces?
5 How should we deal with multiple delimiters?
6 How should we dealt with end of line characters?
My answer is that the embedded , is illegal and the ,, should read a NULL string.
What is the problem with C input?
Consider this fscanf(F1,"%d,%s,%s,%s",&i,str1,str2,str3); to import a line from a file. Note I don't care for the last value after str3. The %d will work fine, the next %s with read the remainder of the line!
The problem is the %s. C does provide a way out:
fscanf(F1,"%d,[^,],[^,],[^,]",&i,str1,str2,str3);
The code [^,] is a %s replacement but uses , as a deliminator. This format code is very powerful but I am not going to explain its full use here. Note, we still need to read the , with the format string with the trailing ,.
But there are still problems. The two consecutive ",," are read as one ",". We can fix this with %*c. This format reads any character (i.e. the ,) but does not (i.e. the *) use the character:
fscanf(F1,"%d,[^,]%*c[^,]%*c[^,]",&i,str1,str2,str3);
This works as expect but on the next file read, we find we are still processing the last line! So in order to fscanf() we have to fully specify the line we want to read! We could just use a %s after the last format code to clean up anything remaining but is not very elegant. Few people would know why the extra %s is sometimes necessary.
I think a better solution is to read the file line separately with
fgets(line,sizeof(line),F1);
and then process the line with:
sscanf(line,"%d,[^,]%*c[^,]%*c[^,]",&i,str1,str2,str3);
AlanX
-
Tokenising the StdOut String
02/15/2022 at 10:33 • 0 commentsTokenising the StdOut String
I first tried strtok() but it skips consecutive delimiters:
char* token; // Get the first token n=0; token=strtok(line,","); while (token!=NULL) { printf("%d %s\n",++n,token); token=strtok(NULL,","); }
So I wrote my own:
// Tokenise Line // Note: Returns NULL or Space for absent data) // Note: A new array is created each time this function is called char** tokenise(char* line,int n) { char** str=calloc(n,sizeof(char**)); int ptr=0; // Remove non-characters for (int i=0;(line[i]>0);i++) if (line[i]<32) line[i]=0; char* token=line; for (int i=0;(line[i]>=32);i++) if (line[i]==',') { line[i]=0; *(str+ptr++)=token; token=&line[i+1]; } *(str+ptr++)=token; return str; }
Now you just need to create you own parameter types from the strings.
Here is the complete listing:
#include <stdio.h> #include <stdlib.h> #include <string.h> char** zenity(char* cmd,int n) { // create string array char** str=calloc(n+1,sizeof(char**)); for (int i=0;i<=n;i++) *(str+i)=(char*)calloc(256,sizeof(char)); char line[256]={0}; // Run Zenity FILE* fp=popen(cmd,"r"); if (fp==NULL) { // Error perror("Pipe returned a error"); for (int i=0;i<=n;i++) free(*(str+i)); free(str); return NULL; } else { fgets(line,sizeof(line),fp); sprintf(*str,"%d",WEXITSTATUS(pclose(fp))); } // Remove non-characters for (int i=0;(line[i]>0);i++) if (line[i]<32) line[i]=0; int ptr=1; char* token=line; for (int i=0;(line[i]>=32);i++) if (line[i]==',') { line[i]=0; strcpy(*(str+ptr),token); ptr++; token=&line[i+1]; } strcpy(*(str+ptr),token); return str; } int main(int argc, char *argv[]) { char** list=NULL; list=zenity("zenity --forms \ --title='Add Member' \ --text='Member information' \ --separator=',' \ --add-entry='First Name' \ --add-entry='Family Name' \ --add-entry='Email' \ --add-combo='Sex' --combo-values='Male|Female' \ --add-calendar='Birthday' \ --add-list='Program' \ --list-values='Expert|Intermediate|Beginner' \ ",6); if (list!=NULL) { for (int i=0;i<=6;i++) printf("%d %s\n",i,*(list+i)); for (int i=0;i<=6;i++) free(*(list+i)); free(list); } return 0; }
So that is it, AlanX