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

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!

Related

Open multiple different windows in Gtkmm

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);

Qt - Non Modal Dialog before Main Window is created

I've been struggling to do this : I want to show on a window a QWidget, or a QDialog before the MainWindow is created, but I cannot use exec() because it will enter its loop and won't create my MainWindow before I accept or reject the dialog.
The reason I want to do this is to have a widget showing information while the MainWindow constructs itself. I don't want to keep this extra window once the MainWindow is showing up.
I believe the issue comes from the fact that the main window is already created when a.exec() is called and the window won't show up before a.exec(). The solution I found is to use a QDialog instead and call exec() but it blocks the rest of the code which I don't want to happen.
Code :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
StartUpDialog start; //this is my custom QDialog, can be a QWidget if necessary.
qDebug() << "starting up!";
MainWindow w;
start.exec(); //I tried show() but it won't show up.
w.startApp(&start); //this function will do some stuff.
w.show();
//I don't want start to stay after mainwindow shows up
return a.exec();
}
Here is what I tried so far :
I tried to create and show the StartUpDialog while constructing the MainWindow but it won't work out.
Use start.show(), but it won't show before the mainwindow does, both for a QWidget and a QDialog.
Use start.exec(), this does what I want but it's modal and I couldn't make it non-modal with SetModal(false) or setWindowModality(Qt:NonModal).
I also tried to use start.exec() and attempted to reimplement accepted() and exec() so that it automatically calls accepted() as soon as it appears but it will still close the window.
Hopefully you can help me in that issue, and thanks for reading !
UPDATE : Solved thanks to Trap, here is how I made it :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
StartUpDialog start;
QSplashScreen *splash = new QSplashScreen();
StartUpWidget *start = new StartUpWidget(splash);
splash->resize(350,380);
start->show();
splash->raise();
splash->show();
qDebug() << "starting up!";
MainWindow w;
w.startApp(start);
w.show();
splash->finish(&w);
start->deleteLater();
splash->deleteLater();
return a.exec();
}
My only concern is that I use a Gif inside my widget using QMovie and updating it has to be done manually apparently.
If I understand your problem correctly (showing a dialog until your main window is created), you should have a look at the QSplashScreen class : http://doc.qt.io/qt-5/qsplashscreen.html

QMainWindow doesn't .show() but .showFullScreen() works

I have a desktop application that I'm building which checks a QSetting value and if it isn't set shows one QMainWindow class but if it is, shows a different QMainWindow class. The two classes are literally identical at this point as it's early on and don't really contain anything.
In my main function this is what I've got:
int main (int argc, char *argv[]) {
...
if (userToken == "NO_USER") {
LoginWindow w;
w.show();
} else {
MainWindow w;
w.show();
}
return a.exec();
}
The only difference in this between the default project set up from when I created the project is the addition of the conditional window load. Both MainWindow and LoginWindow don't have anything loading other than the ui file associated with them, they're functionally the same.
The weirdness is if I do w.showFullScreen() for the LoginWindow it will show up and take up the whole screen, if I do w.show() nothing at all happens, no compiler warnings|errors, application runs fine, just no window displays.
If I remove the conditional statements and LoginWindow references, MainWindow shows up fine.
Any idea why a call to .showFullScreen() would work but a call to .show() on the same class wouldn't?
I am not sure if this solves you problem, but there definitely is a bug in your code. You are instanciating the window objects on the stack inside a a tight scope, and as you know those objects will be destructed as soon as they go out of that scope. What you are doing is letting them go out of scope before the application is ever started.
Please try this instead:
int main (int argc, char *argv[]) {
...
if (userToken == "NO_USER") {
LoginWindow w;
w.show();
return a.exec();
} else {
MainWindow w;
w.show();
return a.exec();
}
}

Qt: How to open a new main window on push button click and delete original main window?

I have files main.cpp, MainWindow.h and MainWindow.cpp. I have a push button on this original main window. What I want is when I click on the button, it should take me to a new main window and delete the original main window.
Also I want to follow good programming practices. So I am wondering that should new source and header files like SecondWindow.cpp/.h be created or all this be done in MainWindow.cpp where I have definition of the SLOT on_button_clicked()?
You need to:
Instantiate the new window and show it.
Delete the current window once the control returns to the event loop.
void MainWindow::on_button_clicked() {
auto win = new MainWindow();
win->setAttribute( Qt::WA_DeleteOnClose );
win->show();
deleteLater();
}
Make sure that the initial instance of the window is created on the heap:
int main(int argc, char** argv) {
QApplication app(argc, argv);
auto win = new MainWindow;
win->setAttribute( Qt::WA_DeleteOnClose );
win->show();
return app.exec();
}
The answer given by Kuba Ober works.However, you can have only 2 main windows at all times no matter how many times you press the button. It seems to crash the program after the second time you run it.
I believe my solution would be better since you can open as many main windows.
At your MainWindow Header:
private:
Ui::MainWindow *ui;
MainWindow *nWin; //Add This bit of code here
As for your triggered event function:
void MainWindow::on_actionNew_triggered()
{
nWin = new MainWindow;
nWin->show();
}
That should do it.
Thanks.

How to do things upon quitting a Qt app

To my understanding, the following will execute an application which will exit when all associated windows are closed.
int main(int argc, char *argv[])
{
QApplication cal(argc, argv); //Application instance
MainWindow window; //Create window in the stack
window.show(); //Show the window
return cal.exec(); //Execute event loop
}
This will also quit said application.
quitButton = new QPushButton("Quit", this);
connect(quitButton, SIGNAL(clicked()), QApplication::instance(), SLOT(quit()));
How should I go about cleaning up (closing hardware connections and libraries etc.) so that I can be sure everything that needs to happen happens no matter how the app it exited, nor at what point in execution it is exited?
I will have a library open throughout the whole app, and a connection to a USB device at most times. I am able to close this connection easily if the process runs its course, but I want the connection to be closed safely if somebody decides to hit quit or close all the windows before it is done.
There is a virtual function called closeEvent for a QWidget (see Documentation, which is called when the widgets close() operation is called.
Alternatively, if you don't want to have your own Base widget, you can hook yourself to the signal QApplication::lastWindowClosed (see here). It is emitted when the last visible window of your application closes.
You can do the following:
int main(int argc, char *argv[])
{
QApplication cal(argc, argv);
// Allocate resources
int ret = cal.exec(); // start the event loop and wait until it quits
// Free the resources
return ret;
}
The proper C++ way of doing it is:
Ensuring that all of your object instances get destructed.
Ensuring that any resources you hold are held via RAII.
Then you don't need to do anything special. As the objects get destructed when main returns, things get cleaned up.
For example:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QObject foo;
QObject * bar = new QObject(&app);
Widget w;
w.show();
return app.exec();
}
Here, both foo and bar are properly destructed before main returns. Now suppose you hold some non-memory resources, like a file:
class Worker : public QObject {
QFile m_file;
...
void doSomething() {
if (m_file.open()) {
...
}
// No file closing!
}
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker foo;
Widget w;
w.show();
return app.exec();
}
Again, the file gets closed before main returns.
That's how RAII works, in a nutshell. If you have your own classes that wrap OS or other resources, simply ensure that such resources are freed when the classes get destructed - and that the classes are always destructible (it's always safe to destruct them). My pet peeve is QThread - it breaks this expectation, and you have to implement a wrapper to fix it.
For resources that you need to allocate on the heap, you're supposed to be using smart pointers or QObject memory management.
There is a QApplication signal that you can use for cleaning up before the application quits:
QObject::connect(&app, SIGNAL(aboutToQuit()), &MyCleaner, SLOT(cleanUp()));
Have fun!