I'm new to C and am trying to build a web browser using GTK, but I just can't get tabs to work!
I think this is the relevant code:
#include<gtk/gtk.h>
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *scrollable_window;
GtkWidget *new_tab_button;
GtkWidget *tab_label;
GtkWidget *notebook;
void new_tab() {
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollable_window, tab_label);
}
int main(int argc, char** argv) {
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request(GTK_WIDGET(window), 1300, 768);
tab_label = gtk_label_new ("tab");
scrollable_window = gtk_scrolled_window_new(NULL,NULL);
vbox = gtk_vbox_new(false,false);
hbox = gtk_hbox_new(false,false);
hbox_web_view = gtk_hbox_new(true,0);
notebook = gtk_notebook_new ();
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
gtk_widget_show(notebook);
gtk_container_add(GTK_CONTAINER(window), scrollable_window);
gtk_container_add(GTK_CONTAINER(scrollable_window), vbox);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
gtk_container_add(GTK_CONTAINER(vbox), hbox_web_view);
new_tab_button = gtk_button_new_with_label("New tab");
g_signal_connect(new_tab_button, "clicked", G_CALLBACK(new_tab), web);
gtk_widget_show_all(window);
gtk_main();
return 0;
Everything works, but there is no notebook showing when I open the program. There is a 'new tab' button but whenever I click it it closes the program!
I think I just don't quite understand how gtk notebooks work. Could anyone help me out?
Your code indicates that you are just calling functions without reasoning why you should or not call each function.
The gtk_container_add() adds a widget to another widget that can be a container, i.e. that can contain other widgets, e.g. a GtkBox or the GtkWindow itself.
You are creating the notebook correctly, but you don't add it to any container in the widget hierarchy that has window as it's natural root.
Also, you create a scrollable_window only once but in real life — unless your design is optimizing memory and you know what you're doing — you need multiple scrollable_window instances, in fact you need a web view and all the children you're adding in your code to the window container.
Having mulitple instances of scrollable_window means that you should create each instance in your new_tab() callback, which BTW has a wrong signature, I don't know which the right signature is but it's certainly not that.
Usually GTK gives you
The object that generated the signal as the first parameter of the callback.
A pointer to your own data, that you can use to avoid global variables and make your code more robust and reusable.
There are some callbacks that have extra parameters, you just need to see the signal's documentation to determine which is the correct signature.
Some callbacks (if not all, I don't remember) return a gboolean to indicate whether or not your callback handled the event.
Ideally then,
Create the notebook.
Add the notebook directly to the window container, it should be your root widget.
Create a "browser" in your new_tab() callback, and add it to your notebook. Use the first parameter which is mostly sure of type GtkWidget and is a pointer to your notebook thus eliminating the need for it to be global, don't use global variables, ever.
Use the gpointer data — probably the last parameter to your callback — to store any context data that you need to share with your main() function.
The ideal way to achieve independency, and completely avoid global variables is to create a structure to store your application state, although modern Gtk+ has mechanisms for this, it's still a good idea to store your data somewhere and pass it around to callbacks via the last parameter to g_signal_connect() and receive it as the last parameter to your callbacks.
Related
According to GObject reference
g_signal_connect_swapped(instance, detailed_signal, c_handler, data); connects a GCallback function to a signal for a particular object. The instance on which the signal is emitted and data will be swapped when calling the handler.
I don't quite get what this means. Does this mean that the data will point to the object pointed to byinstance and instance will point to the object that was pointed to by data or am I making a mistake here?
If former is the case then what is the logic behind this?
You understand correctly.
This allows you to do tricks like the following: You have a button (let's call it button), that is supposed to hide another widget (let's call it textview) when pressed.
You can then do
g_signal_connect_swapped(button, 'clicked', G_CALLBACK(gtk_widget_hide), textview);
to achieve that. When the button is pressed, it generates the 'clicked' signal, and the callback is called with textview as the first argument, and button as the second. In this case the callback is gtk_widget_hide() which only takes one argument, so the second argument is ignored, because that's the way the C calling convention works.
It's the same as the following, but shorter.
static void
on_button_clicked(GtkButton *button, GtkWidget *textview)
{
gtk_widget_hide(textview);
}
...elsewhere...
g_signal_connect(button, 'clicked', G_CALLBACK(on_button_clicked), textview);
Basically it saves you from having to write an extra function if you hand-code your interface. Of course, there may be some far more practical use that I've never understood.
So there is an application I would like to patch. It uses GtkWidget as its topmost container. I would like to do what follows, check number of screens, if it is >2 then check the width of the screen the app is executed on (focus is on screen). I could use Xinerama but first I would like to try native Gtk things. What I do is:
num_monitors = gdk_screen_get_n_monitors (screen);
Then I found a function gdk_screen_get_monitor_at_window(screen, ?window?) but it takes screen and GtkWindow as its argument. Unfortunately I have no window, only widget. I tried to get window by gtk_widget_get_parent_window(widget) or gtk_widget_get_window(widget) these always result in giving me GDK_IS_WINDOW (widget) failed. On the other hand gtk_widget_get_has_window(window) returns 1.
How can I determine screen the app was run on ? I need it to set width hints right.
You're probably failing to get the window because the widget isn't realized and/or mapped, yet. You'll get NULL from gtk_widget_get_window() if it isn't.
The return value from gtk_widget_get_has_window() is probably static; a particular class of widget either always has a window, or never has one.
Try forcing the widget to be realized with gtk_widget_realize(), before extracting the window. If that fails, also try to map it.
You might want to investigate this from the other end, i.e. decide which screen you want/expect the widget to use, and look up that screen's info, without starting from the widget itself.
The window must be actually shown before the function returns you the correct screen the window resides on. This makes your window manager apply its placement policy. To ensure the widget is shown you can use gtk_main_iteration(), calling it until there are no more pending events.
Example code:
#include <stdio.h>
#include <gtk/gtk.h>
int main(int argc, char** argv)
{
gtk_init(&argc, &argv);
GtkWidget*const widget=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(widget);
while(gtk_events_pending())
gtk_main_iteration();
GdkWindow*const window=gtk_widget_get_window(widget);
GdkScreen*const screen=gdk_window_get_screen(window);
const int monitor=gdk_screen_get_monitor_at_window(screen,window);
printf("Monitor number: %d\n",monitor);
}
I have a GUI application which is written in C/gtk+(v 2.x). If I clicked on a menu item of the main window, following function will get called and will show a dialog.
dialog_ui * create_dialog ()
{
dlg = malloc (sizeof(dialog_ui));
dlg->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
...
gtk_widget_show_all (dlg->window);
return dlg;
}
This is the dialog_ui structue
typedef struct _dialog_ui
{
GtkWidget * window;
...
} dialog_ui;
This is the dlg variable in the header file.
dialog_ui * dlg;
My problem is, i'm calling the malloc in the create_dialog function. But I can't figure out from where can I call free, because GUI is event driven. I thought about destroy-event, but I want to know the correct way to do this. Any ideas.. ?
Often times you have a modal dialog which you call using gtk_dialog_run() rather than gtk_widget_show(). The gtk_dialog_run() function will not return until the user closes the dialog, returning a response which you can use to determine if they clicked Yes, Ok, Cancel, Close, etc. You an see an example of that in the GtkDialog API documentation.
Since you're just showing your own GtkWindow, your main application code could connect to the "destroy" signal of the GtkWindow and free the struct that references the widget there.
As a side note, rather than using malloc, you may want to take a look at some of the Memory Allocation functions provided by GLib.
When you don't need the dialog anymore, and you're not going to access it anymore (for example when it's closed) you can free the allocated memory. Doing it in the destroy event is a good place.
In virtually every example code of GTK+ I've seen so far,
the widgets of the GUI are all defined inside the main function.
At first I adopted this, but then found it highly inconvenient when
e.g. manipulating multiple widgets from a single callback function.
Of course I could just use the 'data' gpointer for that, but wouldn't I
have to wrap every widget I want to manipulate in some sort of struct first
to pass it as the 'data' argument?
Anyway, to not be bound by this, I just started defining all Widgets outside the main
function, so I can easily access them across all function. Are there any drawbacks to this style?
The drawbacks are the same as the drawbacks for using any global variable. This page has a good overview of situations when you should not (and when you should) use global variables. If you look near the bottom, you will see under "Really bad reasons to use global variables":
I don't want to pass it around all the time.
I'm afraid this is kind of what your objection sounds like. However, as the page I linked to also mentions, if you are just writing short or one-off programs, then the ease of using global variables probably outweighs the drawbacks.
The usual way in medium-sized programs is to create a struct and populate it in main(), and pass it to the callbacks:
typedef struct {
GtkWidget *window, *button, *textfield;
} Widgets;
int main(int argc, char **argv) {
gtk_init(&argc, &argv);
Widgets *w = g_slice_new0(Widgets);
w->window = gtk_window_new(... etc...
...
g_signal_connect(w->button, "clicked", G_CALLBACK(on_clicked), w);
...etc...
gtk_main();
g_slice_free(Widgets, w);
return 0;
}
In large programs, a better way is to create your own classes representing main windows, preferences dialogs, etc., and pass those classes to the various callbacks.
I tried to call,
g_io_scheduler_push_job(job_func, ¶m, NULL, G_PRIORITY_HIGH, generator_cancellable);
In my C/gtk+ application for running job_func() in another thread then main program. But have segfault when I call this function, and debugger said that: ** userdata attempt to difference a generic pointer**
The job_func() code is,
gboolean job_func(GIOSchedulerJob *job, GCancellable *cancellable, gpointer user_data)
{
JobParam* job_param = (JobParam*)user_data;
build(NULL, job_param->mw);
return TRUE;
}
Where JobParam,
typedef struct _JobParam
{
GtkWidget* widget;
MainWin* mw;
}JobParam;
Where MainWin,
typedef struct _MainWin
{
GtkWindow parent;
GtkWidget* scroll;
GtkWidget* box;
GtkUIManager *uimanager;
} MainWin;
And build,
void build(GtkWidget* widget, MainWin* mw)
{
gtk_list_store_clear(mw->model);
}
How can I fix it?
Thank you.
You're messing with widgets (and related stuff) off the main thread, DON'T DO THAT.
GTK functions are not thread safe unless noted otherwise in the documentation, you cannot manipulate widgets (and I'm fairly certain the GtkTreeModel implementations) in any thread except the one running the main loop. Basically just don't. I think you might be able to do this if AND ONLY IF the GtkListStore is doesn't have any GtkTreeView attached to it. (and if you're doing lots of changes it's a good idea to disconnect it first anyway, saves redraws and lots of useless events)
EDIT: the reason I mention disconnecting the TreeView from the ListStore is because I expect that you're trying to do gtk_list_store_clear() in a separate thread because it's taking too long, it's a common problem, any time you're going to add/remove a large number of rows to/from a TreeModel you should first do gtk_tree_view_set_model(treeview, NULL);
Without seeing the full code, it's hard to determine what is going on. My guess is that param is allocated on the stack based on the way you're passing it in, so the address is invalid when your job_func is actually called is invalid and dereferences garbage.
Try allocating your param using malloc and then freeing it. You can pass in a GDestroyNotify which you can call free within.