Open multiple different windows in Gtkmm - c++

I've only just started learning how to use gtkmm, and I'm trying to create an application which can have more than one window open at the same time (think, main window and a control panel).
I decided to create the layout in Glade, if that's of any relevance to this question.
My method of displaying two bottles is more or less this:
Gtk::Window* main_window = nullptr;
Gtk::Window* servsettings = nullptr;
int main(int argc, char* argv[]) {
auto app = ...
auto builder = ... // these are exluded for brevity
builder->add_from_file("../src/design.glade");
builder->get_widget("main", main_window);
builder->get_widget("servsettings", servsettings);
app->run(*servsettings);
app->run(*main_window);
}
Instead of opening two windows, this instead opens servsettings, then segfaults when I close that.
Now forgetting about the segfault (I'm utterly confused about that,) I think I can see why it only opens servsettings - I assume this is because I'm running the app, and then that call only exits when the window dies?
The problem is, I can't think of any other way to do it. I experimented with multithreading but decided that it would be better to ask here first.
Before anyone suggests it, this answer does not help me. This is because they had a scope-based issue. I don't.

Use Gtk::Application::add_window(Gtk::Window&).
If all the windows managed by Gtk::Application are closed (hidden) or removed from the application then the call to run() will return.
#include <gtkmm.h>
Gtk::Window* window1, *window2;
int main()
{
auto app = Gtk::Application::create();
Gtk::Button button1("Quit"), button2("Quit");
window1 = new Gtk::Window();
window2 = new Gtk::Window();
button1.signal_clicked().connect(sigc::mem_fun(window1, &Gtk::Window::close));
button2.signal_clicked().connect(sigc::mem_fun(window2, &Gtk::Window::close));
window1->set_default_size(200, 200);
window1->add(button1);
window1->show_all();
window2->set_default_size(200, 200);
window2->add(button2);
window2->show_all();
app->signal_startup().connect([&]{
app->add_window(*window2);
});
return app->run(*window1);
}

I found the answer. For those wondering, I replaced the two app->runs with:
servsettings->show();
main_window->show();
app->run(*main_window);

Related

Is HelloWindow object deleted?

I created a sample GTKMM project on GNOME Builder. The great thing was that a sample hello world code was automatically generated for my sample project. Since C++ source files are organized into three parts:
Header file
Implementation file
Main file
I've modified my sample code in a single cpp file for demonstration:
#include <iostream>
#include <gtkmm.h>
using std::cout;
using Gtk::Application;
using Gtk::Window;
using Gtk::Box;
using Gtk::Button;
using Gtk::Label;
class HelloWindow : public Window
{
Box box;
Button button;
Label label;
public:
HelloWindow();
~HelloWindow();
};
HelloWindow::HelloWindow()
: Glib::ObjectBase("HelloWindow")
, Window()
, box(Gtk::ORIENTATION_VERTICAL)
, button("Clickable button")
, label("Hello World!")
{
set_default_size(320, 240);
bool expand(true), fill(true);
box.pack_start(label, expand, fill);
box.pack_end(button, expand, fill);
add(box);
show_all();
}
HelloWindow::~HelloWindow()
{
cout << "Object successfully destructed!\n";
}
static void
on_activate(Glib::RefPtr<Application> app)
{
Window *window = app->get_active_window();
if (not window) {
window = new HelloWindow();
window->set_application(app);
app->add_window(*window);
}
window->present();
}
int main()
{
auto app = Application::create("io.test.window-state-event");
app->signal_activate().connect(sigc::bind(&on_activate, app));
return app->run();
}
One interesting part about the above code is that app is connected to on_activate signal which means the user gets to run only one instance of this program. And if he tries to run another instance the previous still running window will instead be presented.
However, there is the use of new keyword on on_activate() and that confuses me a bit. Is the object really deleted when the user closes the HelloWorld window? What I've learnt about C++ new keyword is that one must remember to delete any object allocated with the former keyword.
Moreover, the destructor message "Object successfully destructed!" isn't printed when the window is closed.
Chances are there is an intentional leak, but it's "controlled". The author knows that this method will be called only once. The author also knows the memory needs to be active the entire lifetime of the application. When the application closes, that memory will be freed one way or another (albeit the destructor will never be called, but in this case, there is nothing imperative that would need to be done)
It's perfectly fine in this scenario.
If you want to ensure the Window object gets deleted, you could keep a unique_ptr of the Window and it will dispose itself (thanks to #underscore_d comment):
#include <memory>
static std::unique_ptr<Window> window;
static void
on_activate(Glib::RefPtr<Application> app)
{
if (!window) {
window = std::make_unique<HelloWindow>();
window->set_application(app);
app->add_window(*window);
}
window->present();
}
int main()
{
auto app = Application::create("io.test.window-state-event");
app->signal_activate().connect(sigc::bind(&on_activate, app));
return app->run();
}
At the end of the day, I am sure the author wanted to keep this "Hello, World" example simple and concise and didn't want to add in some code that doesn't really need to be there in order to keep it simple and concise.
Take a look at the section about managed widgets in the GTKMM documentation. You should use some variation of Gtk::make_managed:
if (!window) {
window = Gtk::make_managed<HelloWindow>();
window->set_application(app);
app->add_window(*window);
}
This way, the window's lifetime will be managed by the application.

Gtkmm - Proper way to close a window and then show another

I am building a gtkmm application. The program open with a setting window asking the user to specify some information, and when sanity checks are done, this window should be closed, and the maih window of the application should open.
Right now, opening the main window, and hiding the setting window completely close the application.
From the setting windows, I am doing:
MainWindow* main_window = new MainWindow();
main_window->show();
this->hide();
How can I get the behavior described above ?
Apparently, you can add and remove windows from a Gtk::App. Would it does what I described, and does it mean I would have to pass to my window the Gtk::App pointer ? Thanks.
What seems to be the proper solution is to pass to the window the application pointer (m_app), add the new window to it, show that window and hide the current one. Removing the current one from the application will let the run() function return:
MainWindow* main_window = new MainWindow(m_app);
m_app->add_window(*main_window);
main_window->show();
this->hide();
m_app->remove_window(*this);
delete->this;
This work, but this might not be the proper way of doing things.
Although the question is quite old, I will show my approach which may help someone else to deal with this task.
I use a general application object which holds all window objects: MainApplication.cpp
MainApplication::MainApplication(int argc, char **argv)
{
// Creating the main application object as first
mainApp = Gtk::Application::create(argc, argv, APPLICATION_ID);
// Create the entry window
createEntryWindow();
}
int MainApplication::run()
{
if (!isRunning) {
// Set the current window to entry window for startup
currentWindow = entryWindow;
return mainApp->run(*entryWindow);
} else {
return -1;
}
}
void MainApplication::createEntryWindow()
{
// Load the entry_window.glade layout with the Gtk::Builder Api
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("../layout/entry_window.glade");
// Calls the constructor of my own derived widget class which details are specified inside the builder file
builder->get_widget_derived(WND_ENTRY, entryWindow);
// Set this main application object to the new window
entryWindow->setMainApplicationContext(this);
}
MainApplication.h
static const int WS_ENTRY = 100;
static const int WS_SOMETHING = 200;
class MainApplication {
public:
MainApplication(int argc, char* argv[]);
int run();
void switchCurrentWindow(int specifier);
private:
void createEntryWindow();
private:
Glib::RefPtr<Gtk::Application> mainApp;
Gtk::Window* currentWindow = nullptr;
EntryWindow* entryWindow = nullptr;
bool isRunning = false;
};
The MainApplication object will be created inside the main() and after that run() is called: main.cpp
int main(int argc, char* argv[])
{
// Create main application object
MainApplication mainApplication(argc, argv);
// Starts the event loop
// No events propagate until this has been called
return mainApplication.run();
}
The EntryWindow.cpp looks like this (just a simple example):
EntryWindow::EntryWindow(BaseObjectType* object, const Glib::RefPtr<Gtk::Builder>& refGlade)
: Gtk::Window(object), builder(refGlade)
{
// Set widgets to builder
builder->get_widget(btnName, btn);
// Set on click methods for signal_clicked
btn->signal_clicked().connect(sigc::mem_fun(*this, &EntryWindow::onBtnClicked));
}
void EntryWindow::onBtnClicked()
{
mainApplicationContext->switchCurrentWindow(WS_SOMETHING);
}
void EntryWindow::setMainApplicationContext(MainApplication* mainApplication)
{
this->mainApplicationContext = mainApplication;
}
EntryWindow.h:
class EntryWindow : public Gtk::Window {
public:
EntryWindow(BaseObjectType* object, const Glib::RefPtr<Gtk::Builder>& refGlade);
void setMainApplicationContext(MainApplication* mainApplication);
protected:
void onBtnClicked();
protected:
const Glib::RefPtr<Gtk::Builder> builder;
Gtk::Button* btn;
private:
MainApplication* mainApplicationContext = nullptr;
const Glib::ustring btnName = BTN_NAME;
};
So now when the button was clicked, you can switch the windows with following function inside the MainApplication class:
void MainApplication::switchCurrentWindow(int specifier)
{
// Check if the passed specifier exist
int tmpSpecifier = 0;
switch (specifier) {
case WS_ENTRY:
tmpSpecifier = WS_ENTRY;
break;
case WS_SOMETHING:
tmpSpecifier = WS_SOMETHING;
break;
default:
tmpSpecifier = 0;
}
// If the specifier exist
if (tmpSpecifier != 0) {
// Increase the use counter of the main application object
mainApp->hold();
// Hide the current window
currentWindow->hide();
// Remove the current window
mainApp->remove_window(*currentWindow);
} else {
return;
}
switch (tmpSpecifier) {
case WS_ENTRY:
currentWindow = entryWindow;
break;
case WS_SOMETHING:
currentWindow = somethingWindow;
break;
}
// Add the new current window
mainApp->add_window(*currentWindow);
// Show the new window
currentWindow->show();
// Decrease the use counter of the main application object
mainApp->release();
}
Summary: Create an Object which holds all windows. So whenever you need to have a new window, you have to create it inside this object. This main application object will be called by the main() and there it will call run() when the application is ready to start. After that you will handle which window is shown and hidden by the main application object only.
In response to your answer: delete->this is a pure syntax error, and even without ->, writing delete this is usually a code smell. Barring that, what you did seems like it will work, if perhaps not be as intuitive as it could be.
However, doing things in that sequence is not always possible. For example, you may not know what the next Window will be. Perhaps which window opens next depends on an HTTP response that may take a while to arrive.
The general solution is to call Application.hold() before removing the Window. Calling .hold() increments the use count of the GApplication, just as adding a window does. The app quits when its use count is zero. Saying its life is controlled by windows is just a quick way to approximate an explanation of that (and is obviously only relevant to GtkApplication, not the base GApplication). Removing a window decrements the use count.
So, in this case, you would now go from a use count of 2 to 1 - not 1 to 0 - so removing the 1st window would no longer make the app quit. Then, after you add the 2nd window, however much later that occurs, call .release() to remove the extra use count, so the remaining window(s) now exclusively control the application's lifetime again.

What is the difference between Gtk::Main and Gtk::Application::create?

Both create Gtk windows but I can't really understand what is going behind the scenes. I tried binding a signal to a button to quit the window but the program got a SIGSEGV when using Gtk::Application::create. When I changed the program to follow Gtk::Main conventions. Everything Worked fine.
Gtk::Application::create program(fails to work):
auto app = Gtk::Application::create(argc, argv);
Gtk::Button *button = new Gtk::Button("Quit");
button->signal_clicked().connect(sigc::ptr_fun(Gtk::Main::quit));
Gtk::Window window;
window.set_default_size(200, 200);
window.add(*button);
button->show();
return app->run(window);
Gtk::Main program(works):
auto app = Gtk::Main(argc, argv);
Gtk::Button *button = new Gtk::Button("Quit");
button->signal_clicked().connect(sigc::ptr_fun(app.quit));
Gtk::Window window;
window.set_default_size(200, 200);
window.add(*button);
button->show();
app.run(window);
return 0;
Why doesn't it work
The first code gets SIGSEGV because you are calling static Gtk::Main::quit when using Gtk::Application.
You could probably use sigc::mem_fun to call Gio::Application::quit inherited in Gtk::Application but that's not a good idea when using Gtk::Application::run(Gtk::Window&):
If you call Gio::Application::quit() while a window is connected to the application, and then return from main() without removing the
window from the application, the application's destructor will not be
called.
And to be honest I don't know how, because Glib::RefPtr returned by Gtk::Application::create() doesn't have a way to get the object:
Unlike most other smart pointers, RefPtr doesn't support dereferencing through * object_ptr.
Close the window instead
From Gtk::Application::add_window(Gtk::Window&):
If all the windows managed by Gtk::Application are closed (hidden) or removed from the application then the call to run() will return.
#include <gtkmm.h>
int main()
{
auto app = Gtk::Application::create();
Gtk::Button *button = new Gtk::Button("Quit");
Gtk::Window window;
button->signal_clicked().connect(sigc::mem_fun(&window, &Gtk::Window::close));
window.set_default_size(200, 200);
window.add(*button);
window.show_all(); //for some widgets (I don't remember which) show() is not enough
return app->run(window);
}
With no information on the specific error, it is hard to know what the problem really is. One thing I see though is that you have no application ID. See this example. You can also check this to know more about applications IDs.
Basically, I would try something like:
int main(int argc, char** argv)
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
// ^^^^^^^^^^^^^^^^^^^ <- add something like this (see below)
Gtk::Button *button = new Gtk::Button("Quit");
button->signal_clicked().connect(sigc::ptr_fun(Gtk::Main::quit));
Gtk::Window window;
window.set_default_size(200, 200);
window.add(*button);
button->show();
return app->run(window);
}
Note that you can (and should) change the application ID for your own needs. Carefully read the second link to see the convention and choose your ID carefully.
Hope this helps!

Divide Gtkmm program into functions (part 1)

I know that this will seem a very basic question, but actually it is not something obvious because of the use of pointers, scopes and GTK especific types of variables and others. I really was not able to find an answer.
I have got to divide the GUI related part of a Gtkmm program into functions, but something seems to be wrong.
To make it clear, here is an example, There is the WORKING code in CODE1.cpp, and it must be divided into something similar to CODE2.cpp (not yet working).
The first one is a window containing only a label, the second is the same, but the label is created inside a function.
Where is the error? What is missing? Any tip or help would be appreciated.
Codes mentioned are the following:
CODE1.cpp:
#include <gtkmm.h>
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
Gtk::Label labela;
labela.set_text("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}
CODE2.cpp:
#include <gtkmm.h>
Gtk::Label etiqueta (string x)
{
Gtk::Label labela;
labela.set_text(x);
return ( labela );
}
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
etiqueta("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}
I guess your problem is that the Gtk::Label does not appear. That's because:
a) You are creating it as a local variable in the scope of the function and it is then released at the end of the function. Maybe you mean to use new (with Gtk::manage()) and return a Gtk::Label* instead of a Gtk::Label.
b) You don't use the return value from your function. There is no labela variable in your main() function.
I don't mean to sound harsh, but you need to read your compiler warnings and you need to read a beginner's C++ book. It's hard to learn C++ just by guessing or by hoping that it's like other languages such as Java.

GTKMM Error: Void Value Not Ignored As it Ought to Be

I am trying to create a simple window in GTKMM that contains a box. I've got the window part working, but I can't get my box code to work. I am following along with this tutorial
I think the tutorial is a little bit dated because Anjuta (the IDE I'm using) generated some different code. Here is my code that should add a box:
#include <gtkmm.h>
#include <iostream>
#include "config.h"
using namespace Gtk;
int main (int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
main_win->set_title ("Image-Viewer");
Gtk::Box *box = Gtk::manage (new Gtk::Box());
box ->set_orientation (ORIENTATION_VERTICAL);
box->set_spacing(6);
*main_win -> add(*box);
if (main_win)
{
kit.run(*main_win);
}
return 0;
}
In the code on the tutorial the window is not created in the same way. As you can see below, the window in my code is being created so that it is in the heap, rather than the stack. (or at least I think [I am new to C++]). I know that items in the heap should be used like a pointer, so for the add function I did that, (rather than using the dot notation described in the tutorial). When I run this code, I get an error stating the following:
error:void value not ignored as it out to be
The error is pertaining to the add method being called on the window. Can somone tell me what I'm doing incorrectly? Thanks
This instruction:
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
Declares a pointer to Gtk::Window. Later, you do:
*main_win -> add(*box);
This is incorrect, because you basically try to apply operator -> after you already dereferenced the main_win pointer - and the result of this dereferencing is not a pointer itself, but a reference to an object of type Gtk::Window.
To fix the problem, remove the extra dereferencing:
main_win -> add(*box);
NOTE:
I do not know Gtk::Window and its member function add(), but if it is the case that add() accepts a pointer as its argument, then you also shouldn't dereference box.