I would like to update a GtkLabel with data imported from a text file, but it doesn't evaluate \n as a newline character, as it should normally do.
The file contains (for example):
This is a\nnewline.
How do I get this \n understood by GtkLabelĀ ?
This is my complete code:
#include <gtk/gtk.h>
#include <string.h>
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *label, *window;
gchar* test_char;
ssize_t len;
FILE* fh;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Test GTK");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);
label = gtk_label_new ("");
fh = fopen ("file.txt", "r");
getline (&test_char, &len, fh);
gtk_label_set_text (GTK_LABEL (label), test_char);
gtk_container_add (GTK_CONTAINER (window), label);
gtk_widget_show_all (window);
fclose (fh);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
The key is converting the literal \n to \n.
There are two ways to do this I can come up with (I leave out the file reading part):
Text in test file:
That's\na\ntest,\nfolks!
1.
gchar *new_txt = g_strcompress (test_char);
gtk_label_set_text (GTK_LABEL (label), new_txt);
g_free (new_txt);
2.
GRegex *regex = g_regex_new ("\\\\n", 0, 0, NULL);
gchar *new_txt = g_regex_replace (regex, test_char, -1, 0, "\\n", 0, NULL);
gtk_label_set_text (GTK_LABEL (label), new_txt);
g_free (new_txt);
g_regex_unref (regex);
Note that you have to escape twice: first for C, then for the regex engine, so that the latter sees: replace \\n with \n. You have to escape both backslashes of \\n for C, that's why you get four backslashes in the string that has to be replaced.
Result for both:
Both ways need a newly-allocated string to store the converted text, so you have to free the memory after usage.
Related
EDIT
This question ended up being two problems packed into one. Yet, I cannot delete the question. The scope of the original question regarding pointers was solved by #David Ranieri. The mmap/fork/gtk problem will be the scope of a new question and will not be addressed here.
I want to print a value I have stored in memory in a GTK window. The integer must be stored using mmap to be retained during a fork elsewhere in the code. I cannot reference this memory mapped address from GTK without getting a SegFault. Am I doing something wrong here? Is there a better way?
My current strategy:
Reserve memory for an int with mmap at *VAL
Fork process, one half modifies VAL the other half runs GTK
Pass *VAL to app in userdata slot
The userdata now called localval in activate()
Print the value at address localval by converting from gpointer to int.
MWE (this causes a segfault, run at your own risk):
/*
* MMAP Variable SegFault
*/
#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", GPOINTER_TO_INT(*localval));
text_status = gtk_label_new(msg);
gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);
//Activate!
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);
*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), (gpointer *)*VAL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
*ABORT = 1;
}
*ABORT = 1;
return status;
}
In a non-working alternative to this code, I tried changing the print line to:
g_snprintf(msg, sizeof msg, "val: %d\n", &GPOINTER_TO_INT(*localval));
But the compiler thinks I want to use the & as a comparator.
You are passing a dereferenced pointer (a value) to a function expecting a pointer:
int *VAL = ...;
...
g_signal_connect (app, "activate", G_CALLBACK (activate), (gpointer *)*VAL);
switch to
g_signal_connect (app, "activate", G_CALLBACK (activate), VAL); // Do not use a wrong cast (void **)
also, gpointer is an alias of void *, using gpointer *data you get a void **data, not what you want, so
static void activate (GtkApplication *app, gpointer *localval) {
should be
static void activate (GtkApplication *app, gpointer localval) { // without *
finally, to print the value of the pointer use
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);
Context and Question
For reasons, I need to fork my code and update a variable on both forks. The variable is stored in memory via mmap so it is accessible to all processes. On one child process, I increment the variable. How do I tell the GTK application to refresh/update/redraw from the child process?
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;
}
What happens at runtime
When the MWE is run, the terminal dutifully prints the value each time it updates. However, the GTK window forever says "val: 1". We can tell the value stored in mmap is accessible to the GTK process by adding usleep(3000000) in the activate process just before gtk_widget_show_all. In this variant, the window will forever show "val: 4".
The Question Reiterated
How do I make the output on the GTK window match the terminal?
That's because activate is called only once (when the window is loaded/activated) but nothing is refreshing the label once loaded, I did some changes in the code (using a global, very ugly but simple to ilustrate the problem), the "Exit Button" is now a "Refresh Button". Press it and you will see the changes in 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;
}
If you want to refresh the label without pressing a button, you can use g_timeout_add and set a function to be called at regular intervals refreshing VAL.
g_timeout_add solution
To allow for an automatic update of the main loop from the application we can use g_timeout_add as #David Ranieri pointed out. However, the API of GTK3 requires we pass the refresh function slightly differently to g_timeout_add.
Modifying the OP MWE and #David Ranieri's answer:
/*
* 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;
}
The important differences:
We no longer pass the empty widget to refresh like we do when using a callback.
GTK3 must be explicitly told that refresh is a G_SOURCE_FUNC.
I am coding a GUI (using GTK3) for a simple program that performs a simulation and plots the requested graph (using gnuplot).
When I'm using only gnuplot, I have no problem. The graph looks exactly like I want. However, when I try using this same code from GTK, it doesn't work anymore. It truncates all my value. Why ? What should I do to fix this?
This is the expected result (gnuplot without GTK):
And this is the actual result (gnuplot with GTK) :
#include <gtk/gtk.h>
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_widget_show_all (window);
FILE *gp = popen("gnuplot -persist", "w");
fprintf(gp, "plot '-' using 1:2 with lines\n");
int i;
for(i = 0; i < 13; i++){
fprintf(gp, "%d %f\n", i, 0.1*i);
}
pclose(gp);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
I'm trying to compile my web app as a native desktop application in C. However I'm having a bit of trouble grabbing the file path in C.
In PyGTK I would use...
import webkit, pygtk, gtk, os
path=os.getcwd()
print path
web_view.open("file://" + path + "/index.html")
However I'm not sure if I'm just looking in the wrong places or what, but when I search Google I haven't been able to find out how to grab the file path in C which I want to use like this.
gchar* uri = (gchar*) (argc > 1 ? argv[1] : "file://" + path + "app/index.html");
Instead of linking to it in a grotesque manner like so...
gchar* uri = (gchar*) (argc > 1 ? argv[1] : "file://" + /home/michael/Desktop/kodeWeave/linux/app/index.html");
webkit_web_view_open (web_view, uri);
Here's my full project (if helpful).
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <webkit/webkit.h>
static WebKitWebView* web_view;
void on_window_destroy (GtkObject *object, gpointer user_data) {
gtk_main_quit();
}
int main (int argc, char *argv[]) {
GtkBuilder *builder;
GtkWidget *window;
GtkWidget *scrolled_window;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "browser.xml", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window1"));
scrolled_window = GTK_WIDGET (gtk_builder_get_object (builder, "scrolledwindow1"));
g_signal_connect (G_OBJECT (window), "delete-event", gtk_main_quit, NULL);
gtk_window_set_title(GTK_WINDOW(window), "kodeWeave");
web_view = WEBKIT_WEB_VIEW (webkit_web_view_new());
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));
gtk_builder_connect_signals (builder, NULL);
g_object_unref (G_OBJECT (builder));
gchar* uri = (gchar*) (argc > 1 ? argv[1] : "file:///home/michael/Desktop/kodeWeave/linux/app/index.html");
webkit_web_view_open (web_view, uri);
gtk_widget_grab_focus (GTK_WIDGET (web_view));
gtk_widget_show_all (window);
gtk_main();
return 0;
}
You can't use the + operator to concatenate strings in c, you may need snprintf instead, first you need a large enough buffer, may be the constant PATH_MAX will work, it's defined in limits.h, so for example
char uri[PATH_MAX];
char cwd[PATH_MAX];
getcwd(cwd, sizeof(cwd));
if (argc > 1)
snprintf(uri, sizeof(uri), "%s", argv[1]);
else
snprintf(uri, sizeof(uri), "file://%s/index.html", cwd);
/* ^ %s specifier for ^ this char pointer */
the + operator works with your operands, but in a different way, it just performs pointer arithmetic, because the operands are pointers.
I've got a button which when clicked copies and appends the text from a GtkEntry widget into a GtkTextView widget. (This code is a modified version of an example found in the "The Text View Widget" chapter of Foundations of GTK+ Development.)
I'm looking to insert a newline character before the text which gets copied and appended, such that each line of text will be on its own line in the GtkTextView widget. How would I do this? I'm brand new to GTK+.
Here's the code sample:
#include <gtk/gtk.h>
typedef struct
{
GtkWidget *entry, *textview;
} Widgets;
static void insert_text (GtkButton*, Widgets*);
int main (int argc,
char *argv[])
{
GtkWidget *window, *scrolled_win, *hbox, *vbox, *insert;
Widgets *w = g_slice_new (Widgets);
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Text Iterators");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, -1, 200);
w->textview = gtk_text_view_new ();
w->entry = gtk_entry_new ();
insert = gtk_button_new_with_label ("Insert Text");
g_signal_connect (G_OBJECT (insert), "clicked",
G_CALLBACK (insert_text),
(gpointer) w);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (scrolled_win), w->textview);
hbox = gtk_hbox_new (FALSE, 5);
gtk_box_pack_start_defaults (GTK_BOX (hbox), w->entry);
gtk_box_pack_start_defaults (GTK_BOX (hbox), insert);
vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show_all (window);
gtk_main();
return 0;
}
/* Insert the text from the GtkEntry into the GtkTextView. */
static void
insert_text (GtkButton *button,
Widgets *w)
{
GtkTextBuffer *buffer;
GtkTextMark *mark;
GtkTextIter iter;
const gchar *text;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w->textview));
text = gtk_entry_get_text (GTK_ENTRY (w->entry));
mark = gtk_text_buffer_get_insert (buffer);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
gtk_text_buffer_insert (buffer, &iter, text, -1);
}
You can compile this code using the following command (assuming the file is named file.c):
gcc file.c -o file `pkg-config --cflags --libs gtk+-2.0`
Thanks everybody!
Perhaps you could just insert the newline character the same way you insert other characters:
...
mark = gtk_text_buffer_get_insert (buffer);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
/* Insert newline (only if there's already text in the buffer). */
if (gtk_text_buffer_get_char_count(buffer))
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
gtk_text_buffer_insert (buffer, &iter, text, -1);
...
"\n" is a string containing the newline character in C, and it works just fine in GTK (unless it doesn't in Windows for some weird reason).
Unhelpful blurb: The 1 can just as easily be -1 here; it just tells GTK it only needs to read 1 character, which might be a little faster. Granted, that line of code should almost never be a bottleneck :-)