Как мне сказать GTK, чтобы обновить приложение извне? - PullRequest
2 голосов
/ 17 апреля 2020

Контекст и вопрос

По причинам, мне нужно раскошелиться на мой код и обновить переменную на обеих вилках. Переменная хранится в памяти через mmap, поэтому она доступна для всех процессов. В одном дочернем процессе я увеличиваю переменную. Как сообщить приложению GTK обновить / обновить / перерисовать из дочернего процесса?

MWE

/*
 * Update GTK label from variable stored in mmap
 */

#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>

static void activate (GtkApplication *app, gpointer localval) {
    GtkWidget *window;
    // Button Containers
    GtkWidget *button_box_quit;
    // Buttons
    GtkWidget *exit_button;
    // Text
    GtkWidget *text_status;

    // Define Window, dynamic size for screen.
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "test");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

    // Define Button Boxes.
    button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);

    // Define Exit Button, put it in a box, put box in window
    exit_button = gtk_button_new_with_label ("Exit");
    gtk_container_add(GTK_CONTAINER (button_box_quit), exit_button);
    gtk_container_add(GTK_CONTAINER (window), button_box_quit);

    // Connect signals to buttons
    g_signal_connect_swapped (exit_button, "clicked", G_CALLBACK (gtk_widget_destroy), window);

    // Define text status
    char msg[32]={0};
    // The "print" line
    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
    text_status = gtk_label_new(msg);
    gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);

    //Activate!
    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
    gtk_label_set_text(GTK_LABEL(text_status), msg);
    gtk_widget_show_all (window);
}

int main (int argc, char **argv) {
    GtkApplication *app;
    int status;

    int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int pid = fork();

    if (pid == 0) {
        while(!*ABORT) {
            printf("%d\n", *VAL);
            // Increments here should be reflected outside this PID.
            *VAL = *VAL + 1;
            usleep(1000000);
        }
        exit(0);
    } else {
        app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
        // The passing line
        g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
        status = g_application_run (G_APPLICATION (app), argc, argv);
        g_object_unref (app);
        *ABORT = 1;
    }
    *ABORT = 1;
    return status;
}

Что происходит во время выполнения

Когда MWE запущен, терминал покорно печатает значение при каждом обновлении. Тем не менее, окно GTK навсегда говорит "val: 1". Мы можем сказать, что значение, хранящееся в mmap, доступно для процесса GTK, добавив usleep(3000000) в процесс activate непосредственно перед gtk_widget_show_all. В этом варианте в окне всегда будет отображаться «val: 4».

Повторный вопрос

Как сделать так, чтобы вывод в окне GTK соответствовал терминалу?

Ответы [ 2 ]

2 голосов
/ 18 апреля 2020

Это потому, что activate вызывается только один раз (когда окно загружается / активируется), но после загрузки метки ничего не обновляется, я сделал некоторые изменения в коде (используя глобальную, очень уродливую, но простую для иллюстрации проблему) ), кнопка «Выход» теперь является «кнопкой Refre sh». Нажмите ее, и вы увидите изменения в VAL.

/*
 * Update GTK label from variable stored in mmap
 */

#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>

static GtkWidget *text_status;

static void refresh(GtkWidget *widget, gpointer data)
{
    (void)widget;

    char msg[32]={0};

    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)data);
    gtk_label_set_text(GTK_LABEL(text_status), msg);
}

static void activate (GtkApplication *app, gpointer localval) {
    GtkWidget *window;
    // Button Containers
    GtkWidget *button_box_quit;
    // Buttons
    GtkWidget *refresh_button;
    // Text


    // Define Window, dynamic size for screen.
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "test");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

    // Define Button Boxes.
    button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);



    // Define Exit Button, put it in a box, put box in window
    refresh_button = gtk_button_new_with_label ("Refresh");
    gtk_container_add(GTK_CONTAINER (button_box_quit), refresh_button);
    gtk_container_add(GTK_CONTAINER (window), button_box_quit);

    // Connect signals to buttons
    g_signal_connect(refresh_button, "clicked", G_CALLBACK (refresh), localval);

    // Define text status
    char msg[32]={0};
    // The "print" line
    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
    text_status = gtk_label_new(msg);
    gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);

    //Activate!
    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
    gtk_label_set_text(GTK_LABEL(text_status), msg);
    gtk_widget_show_all (window);
}

int main (int argc, char **argv) {
    GtkApplication *app;
    int status;

    int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int pid = fork();

    if (pid == 0) {
        while(!*ABORT) {
            printf("%d\n", *VAL);
            // Increments here should be reflected outside this PID.
            *VAL = *VAL + 1;
            usleep(1000000);
        }
        exit(0);
    } else {
        app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
        // The passing line
        g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
        status = g_application_run (G_APPLICATION (app), argc, argv);
        g_object_unref (app);
        *ABORT = 1;
    }
    *ABORT = 1;
    return status;
}

Если вы хотите обновить sh метку без нажатия кнопки, вы можете использовать g_timeout_add и установить функция, которая вызывается через регулярные интервалы обновления VAL.

1 голос
/ 20 апреля 2020

g_timeout_add решение

Для автоматического c обновления основного l oop из приложения мы можем использовать g_timeout_add, как указал @David Ranieri. Однако API GTK3 требует, чтобы функция refresh немного отличалась от g_timeout_add.

Изменение OP MWE и ответ @David Ranieri:

/*
 * Update GTK label from variable stored in mmap
 * Timeout Method
 */

#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>

static GtkWidget *text_status;

static gboolean refresh(gpointer data) {
    char msg[32]={0};

    g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)data);
    gtk_label_set_text(GTK_LABEL(text_status), msg);

    return TRUE;
}

static void activate (GtkApplication *app, gpointer localval) {
    GtkWidget *window;
    // Button Containers
    GtkWidget *button_box_quit;
    // Buttons
    GtkWidget *exit_button;

    // Define Window, dynamic size for screen.
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "test");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

    // Define Button Boxes.
    button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);

    // Define Exit Button, put it in a box, put box in window
    exit_button = gtk_button_new_with_label ("Exit");
    gtk_container_add(GTK_CONTAINER (button_box_quit), exit_button);
    gtk_container_add(GTK_CONTAINER (window), button_box_quit);

    // Connect signals to buttons
    g_signal_connect_swapped (exit_button, "clicked", G_CALLBACK (gtk_widget_destroy), window);

    // Define text status
    text_status = gtk_label_new(NULL);
    gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);

    // Define timeout
    g_timeout_add(500, G_SOURCE_FUNC(refresh), localval);

    // Activate!
    refresh(localval);
    gtk_widget_show_all (window);
}

int main (int argc, char **argv) {
    GtkApplication *app;
    int status;

    int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    int pid = fork();

    if (pid == 0) {
        while(!*ABORT) {
            printf("%d\n", *VAL);
            // Increments here should be reflected outside this PID.
            *VAL = *VAL + 1;
            usleep(1000000);
        }
        exit(0);
    } else {
        app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
        // The passing line
        g_signal_connect (app, "activate", G_CALLBACK (activate), VAL);
        status = g_application_run (G_APPLICATION (app), argc, argv);
        g_object_unref (app);
        *ABORT = 1;
    }
    *ABORT = 1;
    return status;
}

Важные различия:

  • Мы больше не передаем пустой виджет для refre sh, как это делается при использовании обратного вызова.
  • GTK3 должен быть явно указан, что refresh является G_SOURCE_FUNC.
...