用C语言实现GTK3 GUI编程中的控制台

时间:2018-03-10 00:58:58

标签: c console gtk3

我实现了一个带有GTK3的GUI,它基本上为exe程序生成一个输入文本文件,这些输入可以进行详细说明。 通过系统调用(系统(" exe input.dat&"))将此exe放入GUI中执行。

此exe可以在屏幕上打印信息或错误消息。

我想要做的是在GtkTextView上重定向这些消息。

我的想法是将输出和错误重定向到文件(系统(" exe input.dat> output_file.txt 2>& 1&"))和GUI中逐行阅读 文件并在textView中发送此字符串。

我不确定2个进程是否可以写入和读取同一个文件并测试这个概念我使用了这2个简单的程序: 作者(像./writer> out_file.txt一样使用):

      #include <stdio.h>
      #include <unistd.h>

      main()
      {
         int a;

         while(1)
         {
            fprintf(stdout,"a=%d\n",a);
            fflush(stdout);
            sleep(1);
            a++;
         }
      }

和读者:

      #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);
               }
            }
         }
      }

这两个程序正确运行,第二个程序打印第一个程序的输出。

在GUI中添加类似的代码,GUI只读取文件的第一行。

我如何解决这个问题? 谢谢

2 个答案:

答案 0 :(得分:1)

您应该使用popen而不是执行system("exe input.dat &"),然后从程序的stdout输出中轻松阅读。

像这样:

#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;
}

输出:

$ ./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

如果您需要更多控制权并希望阅读stderr,那么您必须为stdoutstderr创建管道, 将fork和孩子dup2的管道设为stderr&amp; stdout和 然后执行exec(或该系列的任何其他功能)来执行 程序

像这样:

#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 wait\n");
        return 1;
    }

    if(WIFEXITED(status) == 0)
    {
        fprintf(stderr, "ls exited abnormally\n");
        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;
}

输出:

$ ./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

答案 1 :(得分:1)

Pablo's answer是正确的,您需要使用pipe(7) - s。

你可以使用GTK&amp; Glib的g_spawn_async_with_pipes(基于Linux上的pipefork以及execve)(而不是forkpopen)。在GTK交互式程序中,它比通常的popen更好,因为分叉程序将与事件循环同时运行。

您甚至可以考虑在使用g_source_add_unix_fd调用的pipe(2)g_spawn_async_with_pipes给出的管道fd-s(或部分)上使用pipe(2)。但您可能更喜欢g_io_channel_unix_newg_io_add_watch

请注意,GTK主循环(和Gtk Input and Event Handling Model),即GtkApplication和相关的g_application_run或较早的gtk_main围绕某些event loop多路复用系统调用如poll(2)(或较旧的select(2)),您可能需要该循环来了解您的管道。当一些数据到达管道时,你可能想要read(2)它(然后调用一些GtkTextBuffer插入函数)。

您应该做出设计选择:您是否希望GUI界面和其他进程同时运行?或者另一个exe进程是否总是如此快速且输出较小(并且没有输入),您可能只使用popen

在当前的GUI应用程序中,如果您需要响应式GUI应用程序,事件循环应该快速运行(每秒至少30或50次)。

还要查看某些现有free software GTK应用程序的源代码中的灵感(例如,在github或您的Linux发行版上)。