Realize a console in a GTK3 GUI programming in C
Realize a console in a GTK3 GUI programming in C
I realized a GUI with GTK3 that, essentially, generates an input text file for an exe program that with these inputs can do elaborations.
This exe is put in executions in the GUI by mean of a System call ( system("exe input.dat &") ).
This exe can print on screen message of information or error.
What I want to do is redirect these message on a GtkTextView.
The idea that I had is to redirect output and error on a file ( system("exe input.dat > output_file.txt 2>&1 &") ) and in the GUI read line by line this
file and send this strings in the textView.
I was not sure that 2 process can write and read the same file and to test this concept I used these 2 simple programs:
the writer (used like ./writer > out_file.txt):
#include <stdio.h>
#include <unistd.h>
main()
{
int a;
while(1)
{
fprintf(stdout,"a=%dn",a);
fflush(stdout);
sleep(1);
a++;
}
}
and the reader:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("out_file.txt","r");
char string_new[1024];
char string_old[1024];
strcpy(string_old," ");
while(1)
{
fgets(string_new,1024,fp);
if ( strlen(string_new) != 0 )
{
if ( strcmp(string_new, string_old) != 0 )
{
fprintf(stdout,"%s",string_new);
fflush(stdout);
strcpy(string_old,string_new);
}
}
}
}
This two programs run correctly and the second one print the output of the first one.
Putting in the GUI a similar code, the GUI read only the first line of the file.
How I can solve this issue?
Thank you
exe
Without more information about that
exe
program your question is unclear. Is that exe
program always running very quickly (in a fraction of a second) like e.g. some ps
command?– Basile Starynkevitch
Mar 10 at 2:45
exe
exe
ps
The exe program is designed to performs the measurement of a physical variable. After the configuration of several instruments via LAN, it start to make measurements. The time needed to ends all measurements is, more or less, about 1 hour. What I want displays on the console is something like: "Total N. points of measurements 1000" "Measured 3 points of 1000" "Measured 4 points of 1000" etc.
– arkkimede
Mar 10 at 9:32
That should go in the question
– Basile Starynkevitch
Mar 10 at 11:30
I have rollbacked your heavy changes to the question after it got answered twice. Please do not include a solution in your question itself: instead, post your own answer (or edit the deleted one that you already made, then flag it for undeletion).
– Cœur
Jul 1 at 10:11
2 Answers
2
You should use popen
instead of executing system("exe input.dat &")
, then it's easy to read from the stdout
output of the program.
popen
system("exe input.dat &")
stdout
Like this:
#include <stdio.h>
int main(void)
{
FILE *fp = popen("ls -lah /tmp", "r");
if(fp == NULL)
return 1;
char buffer[1024];
int linecnt = 0;
while(fgets(buffer, sizeof buffer, fp))
printf("Line: %d: %s", ++linecnt, buffer);
putchar('n');
fclose(fp);
return 0;
}
which outputs:
$ ./b
Line: 1: total 108K
Line: 2: drwxrwxrwt 8 root root 12K Mar 10 02:30 .
Line: 3: drwxr-xr-x 26 root root 4.0K Feb 15 01:05 ..
Line: 4: -rwxr-xr-x 1 shaoran shaoran 16K Mar 9 22:29 a
Line: 5: -rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
Line: 6: -rw------- 1 shaoran shaoran 16K Mar 9 22:29 .a.c.swp
Line: 7: -rwxr-xr-x 1 shaoran shaoran 11K Mar 10 02:30 b
Line: 8: -rw-r--r-- 1 shaoran shaoran 274 Mar 10 02:30 b.c
Line: 9: -rw------- 1 shaoran shaoran 12K Mar 10 02:30 .b.c.swp
Line: 10: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 firefox_shaoran
Line: 11: drwxrwxrwt 2 root root 4.0K Mar 9 20:06 .ICE-unix
Line: 12: srwx------ 1 mongodb mongodb 0 Mar 9 20:07 mongodb-27017.sock
Line: 13: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-c2s-1874
Line: 14: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-s2c-1874
Line: 15: drwxrwxr-x 2 root utmp 4.0K Mar 9 20:06 screen
Line: 16: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:07 ssh-XueH0w8zWCSE
Line: 17: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 thunderbird_shaoran
Line: 18: -r--r--r-- 1 root root 11 Mar 9 20:07 .X0-lock
Line: 19: drwxrwxrwt 2 root root 4.0K Mar 9 20:07 .X11-unix
If you need more control and want also to read stderr
, then you would have to create pipes for stdout
and stderr
,
make a fork
and the child dup2
the pipes to stderr
& stdout
and
then execute exec
(or any other function of that family) to execute the
program.
stderr
stdout
stderr
fork
dup2
stderr
stdout
exec
Like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
int stdout_pipe[2];
int stderr_pipe[2];
pipe(stdout_pipe);
pipe(stderr_pipe);
pid_t pid = fork();
if(pid < 0)
return 1;
if(pid == 0)
{
// closing reading ends and duplicating writing ends
close(stdout_pipe[0]);
dup2(stdout_pipe[1], STDOUT_FILENO);
close(stderr_pipe[0]);
dup2(stderr_pipe[1], STDERR_FILENO);
execlp("ls", "ls", "-alh", "a.c", "kslkdl", NULL);
exit(1);
}
// closing writing end
close(stdout_pipe[1]);
close(stderr_pipe[1]);
int status;
if(waitpid(pid, &status, 0) < 0)
{
fprintf(stderr, "could not waitn");
return 1;
}
if(WIFEXITED(status) == 0)
{
fprintf(stderr, "ls exited abnormallyn");
close(stdout_pipe[0]);
close(stderr_pipe[0]);
return 1;
}
puts("STDOUT:");
char buffer[1024];
ssize_t len;
while((len = read(stdout_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('n');
close(stdout_pipe[0]);
puts("STDERR:");
while((len = read(stderr_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('n');
close(stderr_pipe[0]);
return 0;
}
which outputs:
$ ./b
STDOUT:
-rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
STDERR:
ls: cannot access 'kslkdl': No such file or directory
Pablo, thank you for your codes. I will try to use them.
– arkkimede
Mar 10 at 9:59
Pablo's answer is correct, you need to use pipe(7)-s.
And you could probably use GTK & Glib's g_spawn_async_with_pipes
(which is based on pipe
and fork
and execve
on Linux) for that (instead of fork
or popen
). In a GTK interactive program, it is better than the usual popen
because the forked program would run concurrently with your event loop.
g_spawn_async_with_pipes
pipe
fork
execve
fork
popen
popen
You could even consider using g_source_add_unix_fd
on the (or on some) of the pipe fd-s given by pipe(2) or by g_spawn_async_with_pipes
which use that pipe(2) call. But you might prefer g_io_channel_unix_new
and g_io_add_watch
g_source_add_unix_fd
g_spawn_async_with_pipes
g_io_channel_unix_new
g_io_add_watch
Be aware that the GTK main loop (and Gtk Input and Event Handling Model), i.e. GtkApplication and the related g_application_run
or the older gtk_main
are an event loop around some multiplexing system call like poll(2) (or the older select(2)) and you probably need that loop to be aware of your pipes. When some data arrives on the pipe, you probably want to read(2) it (and then call some GtkTextBuffer insert function).
g_application_run
gtk_main
You should make design choices: do you want the GUI interface and the other process to run concurrently? Or is the other exe
process always so quick and with a small output (and no input) that you might just use popen
?
exe
popen
On current GUI applications, the event loop should run quickly (at least 30 or 50 times per second) if you want a responsive GUI app.
Look also for inspiration inside the source code of some existing free software GTK application (e.g. on github or from your Linux distro).
Nice answer. I haven't used GTK other than for trivial "hello world" programs, so I didn't know about any of these functions.
– Pablo
Mar 10 at 2:15
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
You need to improve your answer to tell a lot more about your
exe
program.– Basile Starynkevitch
Mar 10 at 2:28