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

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.

Related

C++ FLTK How to redraw a box from inside an timerfunction

How do I call a box.redraw from a routine?
I have a timer callback from which I a have to assign a new picture to box1.
My programm crashes at this point.
...
Fl_Window *win = NULL;
Fl_Box *box1 = NULL;
static void get_new_pic(void*) { // Timer callback
const char *filename = "pic2.png";
Fl_PNG_Image png(filename);
box1->image(png);
box1->redraw(); // this kicks the application
Fl::repeat_timeout(2,CB_Hole_Info);
}
int main() {
win = new Fl_Window(240,240); // make a window
box1 = new Fl_Box(0,0,240,180); // widget that will contain image
const char *filename = "pic1.png";
Fl_PNG_Image png(filename);
box1->image(png);
Fl::add_timeout(2, get_new_pic, buff); // setup a timer
win->show();
return(Fl::run());
}
Regards
Your way of adding the image in the timeout is correct. However, you allocate the image on the stack: Fl_PNG_Image png(filename);, so when you leave the timer, the image is automatically deleted together with the stack. When the box is actually drawn, the image is not there anymore.
FLTK does not copy the image. It just links to it.
You'd have to write Fl_PNG_Image *png = new Fl_PNG_Image(filename); and fix the rest of the code to use a pointer and make sure that the image is deleted at the very end.
You declared png1 and png2 as global variables (used in get_new_pic()) but you also declared local variables with the same name in main() which "shadow" the global variables.
Please remove Fl_PNG_Image * from the two assignments in main() so these assignments use the global variables as intended.
Hint: you should also assign a solid background to the box like
box1->box(FL_FLAT_BOX);

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.

Connecting to the realize signal: strange window sizing behavior

I am using Gtkmm3 (Ubuntu) to make a small GUI application. In this application, I have a couple windows to create which basically all follow the same pattern:
registering a main layout (Gtk::Grid);
configuring window properties (icon, title, etc);
configuring layout properties (expanding, sub-layouts, etc);
configuring window widgets (adding them to layouts, labels, etc).
In hope of not having to rewrite all of this logic every time I create a new window, I have written the following base class:
template<typename GtkmmWindow>
class Window
{
public:
Window();
virtual int Show() = 0;
protected:
virtual void ConfigureWindow() = 0;
virtual void ConfigureLayouts() = 0;
virtual void ConfigureWidgets() = 0;
void Init();
Gtk::Grid m_mainLayout;
GtkmmWindow m_window;
};
template<typename GtkmmWindow>
Window<GtkmmWindow>::Window()
{
m_window.add(m_mainLayout);
// When signal 'realize' is sent, 'Init' will be triggered.
// This happens after construction, so virtual methods can
// be used safely:
m_window.signal_realize().connect([this](){Init();});
}
// Initialize child window according to its own needs:
template<typename GtkmmWindow>
void Window<GtkmmWindow>::Init()
{
ConfigureWindow();
ConfigureLayouts();
ConfigureWidgets();
// If this line is removed, no widgets are shown.
m_window.show_all_children();
}
The goal of this class is to make sure points 1 to 4 are implemented by all windows the same way. It does so by calling appropriate virtual methods (to be redefined in concrete child classes) when the realize signal is sent. This is because when the realize signal is sent, I know the window constructors have been called and that I can safely use virtual methods.
For example, here is how I use it to create an application main window:
class MyWindow : public Window<Gtk::ApplicationWindow>
{
public:
MyWindow(Gtk::Application& p_app) : m_app{p_app} {}
int Show() override
{
m_window.show_all();
return m_app.run(m_window);
}
private:
Gtk::Application& m_app;
Gtk::Button m_button;
void ConfigureWindow() override
{
m_window.set_title("SO Question");
// If I set this to false, the window shrinks to fit the button size:
m_window.set_resizable(false);
}
void ConfigureLayouts() override
{
m_mainLayout.override_background_color(Gdk::RGBA("yellow"));
}
void ConfigureWidgets() override
{
m_mainLayout.attach(m_button, 0, 0, 1, 1);
m_button.set_label("Hello");
}
};
This main window sets the main layout to have a yellow background, has a Gtk::Button with label "Hello" registered in the main layout. The problem I have with this strategy is that when I run the code, I get weird window/layout sizing:
Notice the yellow layout is way bigger than the only widget (the button) contained in it. This is the result I would have expected:
That is, the window and main layout should shrink to the size of their only contained widget. Weirdly, if I make the window set_resizable(false), I get the sizing I want, but then I can't resize it anymore, which is often not acceptable.
Questions:
Why is it no so (why is the layout taking so much extra space)?
How can I achieve this without duplicating the base code for every window?
You can build this code using g++ by adding it to:
#include <memory>
#include <gtkmm.h>
// Add here...
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.realize");
std::unique_ptr<MyWindow> mainWindow = std::make_unique<MyWindow>(*(app.get()));
return mainWindow->Show();
}
and running:
g++ -std=c++17 main.cpp -o example.out `pkg-config gtkmm-3.0 --cflags --libs`
That's the default behavior of grid. You can try a simple example, with a single window, a grid and a button. The elements inside grid doesn't expand automatically to occupy the allocated grid size. To do that, you need to add
button.set_hexpand(true);
button.set_vexpand(true);
Here is a good reference on grid vs box. https://people.gnome.org/~ryanl/gtk/html/ch28s02.html

How can I execute the function when button in wxYES_NO is pressed?

Maybe my title is unclear, so I will tell here a more precise explanation:
I am just learning WxWidgets, and I am now trying to make two files: main.cpp and Quit.h. Main.cpp will have the core of the application, and Quit.h will have the class for the quit dialog: Do you really want to quit this application (Yes / No).
Now this is my Quit.h file (without include part):
class Quit : public wxFrame
{
public:
Quit(const wxString& tekst);
};
Quit::Quit(const wxString& tekst)
{
wxMessageDialog* dial = new wxMessageDialog(NULL, _("Do you really want to quit?"), _("Quit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dial->ShowModal();
}
And here I am stuck. I tried with wxDECLARE_EVENT_TABLE(), but I don't know which event stands for this: "on the press of yes button (in wxYES_NO system of buttons)". I can't say: on the press of wxYES_NO because these are two buttons (both YES and NO).
So how can I execute the function when the button YES is pressed?
Thank you!
P.S.
I really apologize for this unclear question, but I hope, that you'll understand. Note that I am just a beginner, so please don't use so many "technical" words and expressions in the answer. I read the documentation, but it uses so many technical expressions and explanation. Also, I read this book.
P.P.S.
Have you noticed that there are a lot of questions on SE now, while there is COVID-19 on its way?
EDIT: When I was making the program on, I came to the other error. Minimal code:
Quit.h
class Quit : public wxFrame
{
public:
Quit(const wxWindow* parent, const wxString& text);
};
Quit::Quit(const wxWindow* parent, const wxString& text)
{
int dialog_return_value = wxNO;
wxMessageDialog* dial = new wxMessageDialog(NULL, text, _("Exit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dialog_return_value = dial->ShowModal();
switch (dialog_return_value)
{
case wxYES:
Close(true);
break;
case wxNO:
Close(false);
break;
default:
Close(false);
};
}
and then I have this line in main.cpp:
void MyFrame::CloseWindow(wxCommandEvent& event)
{
Quit* quit = new Quit(this, _("Do you really want to close the App?"));
}
And then it doesn't work. I can't find the solution, so, if you have some time, please help.
Thank you again!
I would suggest using the wxEvtHandler::Bind<>() function as detailed in the wxWidgets documentaton at https://docs.wxwidgets.org/3.0/overview_events.html. The Bind() function allows dynamic binding of events and the syntax is one line of code as compared to setting up tables to link events to objects.
Additionally see this wxWidgets user forum thread which has detailed explanation for calling member and nonmember methods https://forums.wxwidgets.org/viewtopic.php?t=39817
wxYES_NO is a style flag that tells wxWidgets framework that you want both yes and no buttons in your dialog. Check if the return value of ShowModal() is equal to one of the builtin macros defined as wxYES and wxNO.
See here for the macro definitions https://docs.wxwidgets.org/trunk/defs_8h.html
And you should read up on wxDiaglog. Start here https://docs.wxwidgets.org/trunk/classwx_dialog.html
Do you want to return the value to the caller of Quit::Quit()? Constructors do not return values, you can set a member variable to the value but remember that if the object is destroyed then your member variable is gone too. You have not provided enough information to know what needs to be done for cleanup when you Quit() so I will provide you with the code to check the return value, just fill in what you need in the case body.
Here is how you would check the return value:
class Quit : public wxFrame
{
public:
Quit(const wxString& tekst);
};
Quit::Quit(const wxString& tekst)
{
int dialog_return_value = wxNO; // initialize to a sane default value
wxMessageDialog* dial = new wxMessageDialog(NULL, _("Do you really want to quit?"), _("Quit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dialog_return_value = dial->ShowModal();
// You do not have cancel button so only check wxYES and wxNO
switch( dialog_return_value) // Use switch, scales to more buttons later
{
case wxYES :
/* do something */
break;
case wxNO :
/* do something */
break;
default : /* ignore or handle error */ ;
};
}
You are doing a technical task, it is reasonable to expect that learning "technical" words will be involved.
I checked the code return values are not the same as the input style values, we need to check the return value with wxID_YES instead of wxYES!
I was trying to stick to using as much as possible your code but it makes no sense to me using a plain class to close the application. In that case with wxWidgets you still need to reference your main frame to accomplish the closure. There are easier ways as shown in the example below.
Following is a full working example of an application which simply has a quit button on a frame. You click the button and get the quit message dialog. wxWidgets allows creating dialogs on the stack as opposed to the heap and that is what you need here because the dialog is trivial and will not be reused.
You can copy/paste/compile/run the following code as long as you are using wxWidgets 3+ (I am pretty sure Bind() was added then, may have been slightly earlier)
#include <wx/wx.h>
// toolkit requires defining a wxApp class, OnInit() will be called automatically
// when the wxIMPLEMENT_APP macro is invoked below
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
class MyFrame : public wxFrame
{
public:
MyFrame();
~MyFrame();
private:
void OnExit( wxCommandEvent& event );
// these pointer are owned by the wxWidgets toolkit, do not delete them
// but you need them in a "real" app to add items to the sizer or change
// button properties
wxSizer* m_frame_sizer;
wxButton* m_quit_button;
};
// toolkit requires calling this macro with a wxApp object to bootstrap the GUI framework
wxIMPLEMENT_APP( MyApp );
// OnInit is loosely synonymous with main(), it is where the GUI thread is started
bool MyApp::OnInit()
{
// Create a frame with button
MyFrame* frame = new MyFrame();
// Show the frame with its button
frame->Show( true );
// If return value is false, the wxWidgets framework will kill the app
return true;
}
MyFrame::MyFrame() : wxFrame( NULL, wxID_ANY, "Test Exit" )
{
// wxWidgets requires all controls to be placed in a sizer
m_frame_sizer = new wxBoxSizer( wxVERTICAL );
// Assign the sizer to the frame
this->SetSizer( m_frame_sizer );
m_quit_button = new wxButton( this, wxID_EXIT, "Quit" );
// Put the button into the sizer
m_frame_sizer->Add( m_quit_button, wxSizerFlags().Center() );
// Here we bind the button click event to the OnExit method of MyFrame
// keep in mind that this technique will bind all buttons with id wxID_EXIT to the method
// m_quit_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this) will also work
// to handle the event for just the m_quit_button (notice the lack of wxID_EXIT, it is not needed in this case)
Bind( wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this, wxID_EXIT );
}
MyFrame::~MyFrame()
{
// for illustration, not strictly needed here becasue the entire app is shutting down
Unbind( wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this, wxID_EXIT );
// OR m_quit_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this) for the alternative form
}
void MyFrame::OnExit( wxCommandEvent& event )
{
// Setup a message box with (in order below) the user query text, a title, and style which puts yes/no button and a question mark icon
// Create the message box on the stack as opposed to the heap becasue we only need it here
int answer = wxMessageBox( "Do you rally want to quit?", "Exit App", wxYES_NO | wxICON_QUESTION );
if( answer == wxYES )
{
this->Close( true );
}
// else just return
}
wxYES is a different value than wxID_YES (the codes are 2 vs. 5103). wxMessageDialog::ShowModal returns "one of wxID_OK, wxID_CANCEL, wxID_YES, wxID_NO or wxID_HELP". So the switch statement as written will always trigger the default case. This applies for wx3.1- hopefully those variables will be consolidated in the future as the redundant values do lend themselves to mistakes.
For completeness, the provided switch statement should be:
switch (dialog_return_value)
{
case wxID_YES: //Subtly different from wxYES
Close(true);
break;
case wxID_NO: //Not wxNO
Close(false);
break;
default:
Close(false);
};

Launching Multiple QApplication within a DLL

I have used this link to get me started, [https://stackoverflow.com/a/11056698/5609063][1]
I am trying to create an object which creates its GUI internally, and cleans itself up.
I have a call to quit() as well within lets me close the gui out if I want to force a close or I can just sit on .exec call until the main window is closed.
FooObj.h
class FooObj
{
public:
FooObj(FooObj *obj);
~FooObj();
private:
GUIApp *guiApp;
FooObj *m_fooObj;
}
FooObj.c
FooObj::FooObj()
{
guiApp = new GUIApp(this);
}
FooObj::~FooObj()
{
delete guiApp;
}
GUIApp.h
class GUIApp
{
public:
GUIApp(FooObj *obj);
~GUIApp();
private:
std::thread m_appThread;
void m_RunApp();
}
GUIApp.c
GUIApp::GUIApp(obj)
{
m_fooObj = obj;
m_appThread = std::thread ([this] {m_RunApp();});
}
GUIApp::~GUIApp()
{
if(m_appThread.joinable())
m_appThread.join();
}
void GUIApp::m_RunApp()
{
int argc = 1;
auto a = new QApplication(argc, NULL);
auto win = new FooObjGUI(m_fooObj);
win->show();
a->exec();
return;
}
In GUIApp, there is a call to FooObjGUI which uses FooObj to a gui setup to handle all the data I want to display and what actions I expose to the user.
My intention is to have the user of this code not have to worry about qt and to have it clean itself up.
This code works fine in a dll or exe when I create a single FooObj. I cannot create a second FooObj because I cannot create a second QApplication. I have tried several ideas starting the a->exec() in a separate thread, but the events are not handled and the gui sits idle.
Is there a way to call QApplication across multiple threads within a single dll? Is there a workaround to have both FooObj use the same QApplication and not block the execution of the dll? The only workaround I have for myself at the moment is use 2 copies of the dll, but that is not a good longterm solution.