我如何告诉 GTK 从外部更新应用程序?
How do I tell GTK to update application externally?
背景和问题
由于某些原因,我需要分叉我的代码并更新两个分叉上的变量。该变量通过 mmap
存储在内存中,因此所有进程都可以访问它。在一个子进程中,我增加了变量。我如何告诉 GTK 应用程序从子进程 refresh/update/redraw?
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 window 永远显示“val: 1”。我们可以通过在 activate
进程中 gtk_widget_show_all
之前添加 usleep(3000000)
来告诉 GTK 进程可以访问存储在 mmap 中的值。在此变体中,window 将永远显示“val: 4”。
重申问题
如何使 GTK window 上的输出与终端匹配?
那是因为 activate
只被调用一次(当 window 是 loaded/activated 时)但是加载后没有刷新标签,我对代码做了一些更改(使用全局,非常丑陋但很容易说明问题),"Exit Button" 现在是 "Refresh Button"。按下它,你会看到 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;
}
如果你想在不按按钮的情况下刷新标签,你可以使用g_timeout_add并设置一个定期调用刷新的函数VAL
。
g_timeout_add
解决方案
为了允许从应用程序自动更新主循环,我们可以使用 g_timeout_add
正如@David Ranieri 指出的那样。但是,GTK3 的 API 要求我们将 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;
}
重要区别:
- 我们不再像使用回调时那样传递空的小部件进行刷新。
- 必须明确告知 GTK3
refresh
是一个 G_SOURCE_FUNC
.
背景和问题
由于某些原因,我需要分叉我的代码并更新两个分叉上的变量。该变量通过 mmap
存储在内存中,因此所有进程都可以访问它。在一个子进程中,我增加了变量。我如何告诉 GTK 应用程序从子进程 refresh/update/redraw?
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 window 永远显示“val: 1”。我们可以通过在 activate
进程中 gtk_widget_show_all
之前添加 usleep(3000000)
来告诉 GTK 进程可以访问存储在 mmap 中的值。在此变体中,window 将永远显示“val: 4”。
重申问题
如何使 GTK window 上的输出与终端匹配?
那是因为 activate
只被调用一次(当 window 是 loaded/activated 时)但是加载后没有刷新标签,我对代码做了一些更改(使用全局,非常丑陋但很容易说明问题),"Exit Button" 现在是 "Refresh Button"。按下它,你会看到 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;
}
如果你想在不按按钮的情况下刷新标签,你可以使用g_timeout_add并设置一个定期调用刷新的函数VAL
。
g_timeout_add
解决方案
为了允许从应用程序自动更新主循环,我们可以使用 g_timeout_add
正如@David Ranieri 指出的那样。但是,GTK3 的 API 要求我们将 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;
}
重要区别:
- 我们不再像使用回调时那样传递空的小部件进行刷新。
- 必须明确告知 GTK3
refresh
是一个G_SOURCE_FUNC
.