GtkWindow is not loading widgets - c

I am playing with gtk4 and have the following
static void startapp(GtkApplication *app)
{
GtkWindow *window = (GtkWindow*)gtk_window_new();
gtk_window_set_application(window, app);
gtk_window_set_titlebar(window, gtk_header_bar_new());
gtk_header_bar_set_show_title_buttons(
(GtkHeaderBar*)gtk_window_get_titlebar(window), TRUE);
gtk_window_set_default_widget(window, gtk_button_new_with_label("bar"));
gtk_window_present(window);
}
int main (int response, char **name)
{
GtkApplication *app = gtk_application_new ("org.foo", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", (GCallback)startapp, NULL);
response = g_application_run((GApplication*)app, response, name);
g_object_unref(app);
return response;
}
What works
The program starts
The window can set widget via title bar
What isn't
The program is not showing the button widget inside the window
I dont think it has something to do with the int response variable. I did create another variable inside the code body and replaced response = ... ; return response with int foo = ...; return foo
So why isn't the window loading widgets?

In GTK4, you want gtk_window_set_child to set a child of a window (like GTK3, only one child is allowed).
So you want gtk_window_set_child(window, gtk_button_new_with_label("bar")); instead of the line gtk_window_set_default_widget(window, gtk_button_new_with_label("bar"));

Related

How can I "pause" app for user to press a button? GTK

I am making an card game in C using GTK 3.0. I have two functions for two windows, openMenu() to open the menu and newGame() to create a new window with a card table. The player is playing against two bots which will choose their cards randomly. I need the player to start the game with choosing one card.
Open Menu:
static void openMenu(int argc, char *argv[]){
GtkBuilder *builder;
GtkWidget *window;
GObject *button;
GError *error = NULL;
gtk_init(&argc, &argv);
builder = gtk_builder_new_from_file("menu_window_glade.glade");
window = GTK_WIDGET(gtk_builder_get_object(builder, "menu_window"));
gtk_builder_connect_signals(builder, NULL);
gtk_widget_show(window);
gtk_main();
}
New Game:
void newGame(){
GtkBuilder *builder;
GtkWidget *window;
GtkImage *image;
widgetsPtrs *widgets = malloc(sizeof(widgetsPtrs));
char imageStr[] = "image00";
char aImgstr[] = "A0";
char dImgstr[] = "D0";
builder = gtk_builder_new_from_file("game_glade.glade");
window = GTK_WIDGET(gtk_builder_get_object(builder, "game_glade_window"));
gtk_builder_connect_signals(builder, NULL);
gtk_widget_show(window);
shuffleTheDeck();
printAll(Deck);
card *player = malloc(sizeof(card));
card *leftBot = malloc(sizeof(card));
card *rightBot = malloc(sizeof(card));
deal(player, 0);
deal(leftBot, 0);
deal(rightBot, 0);
/***************Puts images into buttons***********/
tmpCard = player;
for (size_t i = 0; i < 6; i++)
{
tmpCard = tmpCard->next;
imgPath[20] = zero + tmpCard->rank;
imgPath[21] = zero + tmpCard->suit;
gtk_image_set_from_file(GTK_IMAGE(widgets->w_playersHandPtr[i]), imgPath);
}
int a = firstTurnCheck();
playersTurn(player, leftBot, rightBot);
}
The problem comes from here: I've come with an idea of a loop, which can be broken only if the global bool is changed, which is changed in a function serving on click events. However, the window with a game table doesn't show, therefore I am never able to click a button, so the app freezes.
void playersTurn(card *player, card *leftBot, card *rightBot){
bool DEFENDERTOOKTHECARDS = false;
bool RBHASMORE = true;
PLAYERCHOOSING = true;
int attackCounter = 0;
int *appearedRanks = calloc(9, sizeof(int));
card *cardsOnTheTable = malloc(sizeof(card)); //cardsOnTheTable[0] is a pointer to the table
char answer;
puts("Player's Turn");
puts("Choose a card.");
/*****************The spot where the mistake might be****************/
while(PLAYERCHOOSING);//PLAYERCHOOSING is a global bool.
puts("You've chosen a card.");
}
On_card_click function:
void on_card_clicked(GtkButton *button, gpointer *data){
gtk_widget_hide(GTK_WIDGET(data));
PLAYERCHOOSING = false;
}
Among other things, this line is a problem: (as you guessed in your comment.)
while(PLAYERCHOOSING);//PLAYERCHOOSING is a global bool.
It will enter and never leave.
At the very least it should have the ability to check a flag to see if a button has been pressed:
while(PLAYERCHOOSING)
{
if(buttonToggled)//define as another global boolean value, and set when button is pressed
{
PLAYERCHOOSING = false;
buttonToggled = false;
}
else Sleep(1000); //sleep for 1 second
}
A better approach is to run the user interface in the primary thread, then put the housekeeping (button press detection and handling ) in a second thread. A callback function could be defined as a button press handler.
(void on_card_clicked(GtkButton *button, gpointer *data) appears to be a callback.)
I don't understand why so many people like using threads for anything. GTK has a main loop, use it (see this answer for more on this). You can't use sleep, infinite loops, or any other blocking calls, otherwise you block the main loop that processes the UI events.
So wouldn't just putting:
PLAYERCHOOSING = true;
in playersTurn, and
PLAYERCHOOSING = false;
in on_card_clicked be enough?
Thanks a lot #ryyker for the advice he's given.
The solution is based on threading. As I said, I've never worked with them before, but it took me like 15 mins to figure out everything I needed, so there is nothing to be afraid of :) There is my solution:
'#include < pthread.h >',
declare pthread_t thread in newGame(),
add pthread_create(&thread, NULL, playersTurn, NULL); into newGame()
you can leave 'while(PLAYERCHOOSING)' loop as it is.
compile with -lpthread flag.
Voila, you are amazing.

How a window gets created in this GTK application?

I'm following this tutorial. I'm currently at the Building applications part. There is one thing that baffles me, and that is how application window gets created in the first place. Let me explain.
This is how the program starts:
#include <gtk/gtk.h>
#include "exampleapp.h"
int
main (int argc, char *argv[])
{
return g_application_run (G_APPLICATION (example_app_new ()), argc, argv);
}
This is easy. The application is started using g_application_run function. The function takes three arguments: an app, argument count and argument vector. Let's see how this app is created.
#include <gtk/gtk.h>
#include "exampleapp.h"
#include "exampleappwin.h"
struct _ExampleApp
{
GtkApplication parent;
};
G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION);
static void
example_app_init (ExampleApp *app)
{
}
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_class_init (ExampleAppClass *class)
{
G_APPLICATION_CLASS (class)->activate = example_app_activate;
G_APPLICATION_CLASS (class)->open = example_app_open;
}
ExampleApp *
example_app_new (void)
{
return g_object_new (EXAMPLE_APP_TYPE,
"application-id", "org.gtk.exampleapp",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}
The line G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION); aliases ExampleApp to GtkApplication (but in a smart way, such that types, variables, etc. associated with GtkApplication are now associated with ExampleApp).
Let's now take a look at ExampleApp *example_app_new(void) function. This function actually returns GtkApplication *, since we associated ExampleApp with GtkApplication. Next, this function calls and returns a new object by calling g_object_new function. That function takes the following arguments:
EXAMPLE_APP_TYPE, which is just GTK_TYPE_APPLICATION
"application-id", which tells that the next argument is the ID of an applicaton
"org.gtk.exampleapp", the ID
"flags", which tells that the next argument is a flag
"G_APPLICATION_HANDLES_OPEN", a flag
NULL, terminator
g_object_new, called like this, returns GtkApplication with the ID of "org.gtk.exampleapp" and with a flag "G_APPLICATION_HANDLES_OPEN". After the program goes back to example_app_new, it exits it and returns a new object of type ExampleApp * A.K.A. GtkApplication * to main. The new app is cast to GApplication in g_application_run using G_APPLICATION macro.
You have now seen what I understand. Now you'll see what I don't understand.
The tutorial linked at the top of this question says that this creates an empty window. In earlier parts of the tutorial (like this one) we used g_signal_connect to call a function when an app is run. For example,
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
calls the function activate when the app app is run using g_application_run. Function activate will in turn usually create a window and populate it either by itself or by calling other functions. That is what baffles me: there is no such thing in our example app program. How then a window gets created?
The catch is in overriding activate and open functions. It says here:
To handle these two cases, we override the activate() vfunc, which gets called when the application is launched without commandline arguments, and the open() vfunc, which gets called when the application is launched with commandline arguments.
Therefore, the function
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
is called when the application is run without arguments.
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
is called when there are given arguments.

GTK3 : gtk_widget_destroy versus gtk_widget_hide

Here a short MCV to illustrate the problem I meet :
I call a dialog box from my main window.
I click on one of the buttons from the dialog (usual way I think)
If I click again on one button, dialog will not display again (I got a bunch of errors instead).
This doesn't happen of I use gtk_widget_hide instead.
(interfaces are designed with Glade3)
typedef struct {
GtkBuilder *builder;
gchar *stuff;
} Context;
void conDisplay(GtkWidget *g, gpointer userdata) {
GtkWidget *dlg, *parent;
Context *ctx=(Context *)userdata;
int ret=0;
g_printerr("clicked\n");
parent=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"MCV"));
if (ctx->stuff) {
g_printerr("Already connected\n");
dlg=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"question"));
gtk_window_set_transient_for(GTK_WINDOW(dlg),GTK_WINDOW(parent));
ret=gtk_dialog_run(dlg);
if (ret==-3) { // OK clicked
g_printerr("OK from Already connected\n");
}
else { g_printerr("Unknown\n"); }
gtk_window_set_transient_for(GTK_WINDOW(dlg),NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
} else {
dlg=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"connect"));
gtk_window_set_transient_for(GTK_WINDOW(dlg),GTK_WINDOW(parent));
ret=gtk_dialog_run(dlg);
if (ret==-1) { // GO clicked
g_printerr("GO\n");
ctx->stuff="Hello";
}
else { g_printerr("Cancel\n"); }
gtk_window_set_transient_for(GTK_WINDOW(dlg),NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
}
}
int main(int argc, char **argv)
{
Context ctx;
GtkWidget *mainwin;
GtkWidget *btnCon;
GError *error=NULL;
/* Init GTK+ */
gtk_init(&argc,&argv);
ctx.builder=gtk_builder_new();
ctx.stuff=NULL;
// Load UI from file.
gtk_builder_add_from_file(ctx.builder,"mcv.glade",&error);
mainwin=GTK_WIDGET(gtk_builder_get_object(ctx.builder,(gchar *)"MCV"));
btnCon=GTK_WIDGET(gtk_builder_get_object(ctx.builder,(gchar *)"con"));
g_signal_connect(btnCon, "clicked", (GCallback)conDisplay, &ctx);
gtk_widget_show_all(mainwin);
gtk_main();
return 0;
}
Thanks for your help !
Best regards.
V.
From GtkBuilder:
A GtkBuilder holds a reference to all objects that it has constructed and drops these references when it is finalized. This finalization can cause the destruction of non-widget objects or widgets which are not contained in a toplevel window. For toplevel windows constructed by a builder, it is the responsibility of the user to call gtk_widget_destroy() to get rid of them and all the widgets they contain.
Since GtkDialog inherits from GtkWindow I would suggest separating mainwin and dlg GtkBuilders and gladefiles and use temporary GtkBuilder in conDisplay:
void conDisplay(GtkWidget *g, gpointer userdata) {
GtkWidget *dlg, *parent;
Context *ctx=(Context *)userdata;
GtkBuilder *builder = gtk_builder_new_from_file("dlg.glade"); //or even pass glade filename in Context
/* rest of the code */
gtk_widget_destroy(GTK_WIDGET(dlg));
g_object_unref(G_OBJECT(builder));
}

Fetching gtk theme background color

I'm almost a gtk newbie, and I'm looking for a way to get the background color for the current theme in gtk. So this code:
GdkColor color = gtk_widget_get_style(mainWindowHandle)->bg[GTK_STATE_NORMAL];
works only after the main window is shown, before returns an strange ugly gray.
Try to attach to the widget's "realize" signal and then grab the style information you want.
static void
widget_realized_cb (GtkWidget *widget) {
GdkColor *color = NULL;
GtkStyle *style = gtk_widget_get_style (widget);
if (style != NULL) {
color = style->bg[GTK_STATE_NORMAL];
/* Do whatever you want with it here */
}
}
void foobar () {
g_signal_connect (mainWindowHandle,
"realize",
G_CALLBACK (widget_realized_cb),
NULL);
}
I've added a
gtk_widget_realize(mainWindowHandle);
before the gtk_widget_get_style and works perfectly!

GTK implementation of MessageBox

I have been trying to implement Win32's MessageBox using GTK. The app uses SDL/OpenGL, so this isn't a GTK app.
I handle the initialization (gtk_init) sort of stuff inside the MessageBox function as follows:
int MessageBox(HWND hwnd, const char* text, const char* caption, UINT type)
{
GtkWidget *window = NULL;
GtkWidget *dialog = NULL;
gtk_init(&gtkArgc, &gtkArgv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
// gcallback calls gtk_main_quit()
gtk_init_add((GtkFunction)gcallback, NULL);
if (type & MB_YESNO) {
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, text);
} else {
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, text);
}
gtk_window_set_title(GTK_WINDOW(dialog), caption);
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_main();
gtk_widget_destroy(dialog);
if (type & MB_YESNO) {
switch (result) {
default:
case GTK_RESPONSE_DELETE_EVENT:
case GTK_RESPONSE_NO:
return IDNO;
break;
case GTK_RESPONSE_YES:
return IDYES;
break;
}
}
return IDOK;
}
Now, I am by no means an experienced GTK programmer, and I realize that I'm probably doing something horribly wrong.
However, my problem is that the last dialog popped up with this function staying around until the process exits. Any ideas?
Hmm, ok. I'd suggest code like this, then:
typedef struct {
int type;
int result;
} DialogData;
static gboolean
display_dialog(gpointer user_data)
{
DialogData *dialog_data = user_data;
GtkWidget *dialog;
if (dialog_data->type & MB_YESNO)
dialog = gtk_message_dialog_new(...);
else
dialog = gtk_message_dialog_new(...);
// Set title, etc.
dialog_data->result = gtk_dialog_run(...);
gtk_main_quit(); // Quits the main loop run in MessageBox()
return FALSE;
}
int MessageBox(...)
{
DialogData dialog_data;
dialog_data.type = type;
gtk_idle_add(display_dialog, &dialog_data);
gtk_main();
// Do stuff based on dialog_data.result
}
The struct is required because you need to pass around a couple pieces of data. The gtk_idle_add() call adds a method to be run when the main loop is running and idle, and the FALSE return value from the display_dialog() call means that it's only run once. After we get the result from the dialog, we quit the main loop. That'll cause the gtk_main() in your main MessageBox() method to return, and you'll be able to access the result from there.
To manage a dialog box with GTK+, use a GtkDialog and gtk_dialog_run() instead of managing a window and a main loop by yourself.
EDIT / ADDENDUM :
What I mean is "just use" : I don't understand why you create a windows you never use and a main loop which seems useless (at least from the piece of code you posted). You can write something as short as :
int MessageBox(HWND hwnd, const char* text, const char* caption, UINT type)
{
GtkWidget *dialog ;
/* Instead of 0, use GTK_DIALOG_MODAL to get a modal dialog box */
if (type & MB_YESNO)
dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, text );
else
dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, text );
gtk_window_set_title(GTK_WINDOW(dialog), caption);
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy( GTK_WIDGET(dialog) );
if (type & MB_YESNO)
{
switch (result)
{
default:
case GTK_RESPONSE_DELETE_EVENT:
case GTK_RESPONSE_NO:
return IDNO;
case GTK_RESPONSE_YES:
return IDYES;
}
return IDOK;
}
}
A few things:
You are creating (and not using) an unnecessary toplevel window, named window. You can just delete these lines:
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
Also, the flow doesn't seem quite right. gtk_main() starts the GTK main loop, which blocks until something exits it. gtk_dialog_run() also starts a main loop, but it exits as soon as one of the buttons is clicked.
I think it might be enough for you to remove the gtk_init_add() and gtk_main() calls, and simply deal with the return value. Also the gtk_widget_destroy() call is unnecessary, as the dialog window is automatically destroyed when gtk_dialog_run() returns.

Resources