I'm stuck on how to manage multiple windows in SFML. Basically, I'm able to open a new window, but it won't let me close it. Here is the new window header and implementation.
Header:
class PopWindow {
private:
sf::RenderWindow popWin;
sf::Color popWinColor = sf::Color(50, 50, 50);
sf::Event popWinEvent{};
public:
// Default constructor
PopWindow();
// While pop is Open
int winOpen();
};
Implementation:
#include "PopWindow.h"
PopWindow::PopWindow() {
popWin.create(sf::VideoMode(500, 500), "Open Project");
}
int PopWindow::winOpen() {
while (popWin.isOpen()) {
while (popWin.pollEvent(popWinEvent)) {
if (popWinEvent.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::F2)) {
popWin.close();
}
popWin.clear(popWinColor);
popWin.display();
}
}
return 0;
}
Weird thing is if I declare a PopWindow object in the main class file and call object.winOpen(), it works totally fine. However, if the object is declared in one of other class files, I can only open it but not close it.
I appreciate your help.
I copied that code and checked it out and I see that it works fine if you close the windows in the opening order. For example if you declared window1 and window2 and call window1.winOpen() and then window2.winOpen(), then until you close the first window - window1 - the second not really started to handle closing event because window1.winOpen() did not finish yet. So you can drag the second window (which obviously opened on the first) aside and then close the first one and then close the second one.
But if you try to close the second before the first it will wait until you close the firs and then they both closed together.
Related
I'd like to change to a certain page in a notebook when deleting the window and do some work before effectively deleting the window.
The code below gives 1 for get_current_page, but the page isn't effectively changed to 1.
What should be the solution to this problem?
Form::Form()
{
add(box);
notebook.set_size_request(800, 600);
notebook.set_show_tabs(false);
box.pack_start(notebook, Gtk::PACK_SHRINK);
label_intro.set_text("Intro");
box_intro.pack_start(label_intro);
box_intro.show();
label_exit.set_text("Preparing clean exit ... Please wait!");
box_exit.pack_start(label_exit);
box_exit.show();
notebook.insert_page(box_intro, "Intro", 0);
notebook.insert_page(box_exit, "Exit", 1);
signal_delete_event().connect(sigc::mem_fun(*this, &Form::is_deleted));
set_title("title");
resize(800, 600);
show_all();
}
bool Form::is_deleted(GdkEventAny *any_event)
{
notebook.set_current_page(1);
std::cout << "current_page " << notebook.get_current_page() << std::endl; // gives 1
std::this_thread::sleep_for(2000ms);
return Window::on_delete_event(any_event);
}
class Form : public Window
{
public:
Form();
private:
bool is_deleted(GdkEventAny *any_event);
private:
// Form
Box box;
Notebook notebook;
Box box_intro, box_exit;
Label label_intro, label_exit;
};
Sorry for the late answer, your question made my work more than I thought!
Working with the Glib::TimeoutSource class (which is ridiculously under documented...), I was able to hack my way around this limitation.
Basically, my strategy was, on a single click, to run the delete-event signal handler two times:
once to update the notebook. In this case we return true to indicate the handler has "fully handled" the signal, and propagation should not happen, hence not closing the window. At this point, the user sees the change in the UI, but no work has been done. In this pass, we also set a Glib::Timeout to later call the close method on the window, calling once again the delete-event signal handler.
twice to do the work. This time, we do the real work (UI has already been updated). Once the work is done, we propagate the handler and close the window.
Here is this code*:
#include <chrono>
#include <iostream>
#include <thread>
#include <gtkmm.h>
using namespace std::chrono_literals;
class Form : public Gtk::Window
{
public:
Form();
private:
Gtk::Box box;
Gtk::Notebook notebook;
Gtk::Box box_intro;
Gtk::Box box_exit;
Gtk::Label label_intro;
Gtk::Label label_exit;
// This flag indicates if a first call to the "delete-event" signal
// has been done. On a second call to this event, this should be
// set to "true" to alter the handler's behaviour.
bool flag = false;
};
Form::Form()
{
add(box);
notebook.set_size_request(800, 600);
notebook.set_show_tabs(false);
box.pack_start(notebook, Gtk::PACK_SHRINK);
label_intro.set_text("Intro");
box_intro.pack_start(label_intro);
box_intro.show();
label_exit.set_text("Preparing clean exit ... Please wait!");
box_exit.pack_start(label_exit);
box_exit.show();
notebook.insert_page(box_intro, "Intro", 0);
notebook.insert_page(box_exit, "Exit", 1);
signal_delete_event().connect(
[this](GdkEventAny *any_event)
{
if(!flag)
{
// First time in. We change the notebook page:
notebook.set_current_page(1);
// If we block right away and don't return from
// this handler, the GUI will freeze. Hence, we set
// a timer to "close" the window in 10ms. Not that
// closing the window will call once more this handler...
Glib::signal_timeout().connect(
[this]()
{
close();
return false; // Disconnect after on call...
},
10
);
// So we change the flag value, to alter its behavior on the
// next pass.
flag = true;
return true; // Fully handled for now... leaving the handler.
// This will allow the main loop to be run and the
// window to uptate.
}
// On the second run, we do the work:
std::this_thread::sleep_for(1900ms);
// And we close the window (for real this time):
return Window::on_delete_event(any_event);
}
);
set_title("title");
resize(800, 600);
show_all();
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
Form window;
window.show_all();
return app->run(window);
}
* I took the liberty to use lambda expressions, as I think they are more readable and encapsulated, and I wasn't sure if you knew about them. In any case, take what feels best.
I understand this is some sort of a hack, but after having tried a lot of things, I have come to believe this is the best we can achieve without dealing with more involved multi-threading strategies (which I am no expert of, sadly).
Hopes this, at least temporarily, solves your issue.
I'm writing a Game Engine just for practice but I still stuck with the first chellange. The Window Manager.
https://github.com/thebenius/SDL
I've created a GitHub Repo to show you the code but don't worry. Its not much. But I absolutely don't know where is my mistake.
In the Code I create three Windows and I Manage the input for SDL_QUIT to stop the game loop and for SDL_WINDOWEVENT_CLOSE to close the windows.
Everything works fine until the last window is closed. As far as I know, now the SDL_QUIT Event must be emitted by SDL. But the Gameloop goes on.
I think I maybe have a kind of memory leak and there is still a windows saved. But I checked the window stack (Window::windows hashmap) it is empty. And also the variables in main are cleared.
I also tried to additionally clear the window and renderer variable in the hash map
Window::~Window() {
// Delete Window and Renderer
SDL_DestroyRenderer(Window::windows[this->windowID]->renderer);
SDL_DestroyWindow(Window::windows[this->windowID]->window);
Window::windows[this->windowID]->renderer = nullptr;
Window::windows[this->windowID]->window = nullptr;
// Delete Window from map
Window::windows.erase(this->windowID);
// Delete Window and Renderer
SDL_DestroyRenderer(this->renderer);
SDL_DestroyWindow(this->window);
// Reset Pointer
this->renderer = nullptr;
this->window = nullptr;
Nothing worked.
I'm new in C++ and SDL. I hope you can help me out.
Thank you o11c,
Your answer was the riddles solution.
I just put SDL_Quit() out of the Destructor. This obviously blocked the Event Handler to catch SDL_QUIT. So I put it to the constructor in atexit()
After that (don't know why before not) I got an Segfault when deleting the window pointer in main. I deleted that and just set them all to nullptr.
Now the WindowManager works properly. Thank you for your help
I think SDL_QUIT is only an hook called if you call SDL_Quit(), to give the user the opportunity to do some quit stuff, the manual:
You should call this function even if you have already shutdown each initialized subsystem with SDL_QuitSubSystem(). It is safe to call this function even in the case of errors in initialization
You can use this function with atexit() to ensure that it is run when your application is shutdown, but it is not wise to do this from a library or other dynamically loaded code
To catch a window close event see SDL_WindowEvent, SDL_WINDOWEVENT and SDL_WINDOWEVENT_CLOSE, the id of the closing window is given as argument.
* \file SDL_quit.h
*
* An ::SDL_QUIT event is generated when the user tries to close the application
* window. If it is ignored or filtered out, the window will remain open.
* If it is not ignored or filtered, it is queued normally and the window
* is allowed to close. When the window is closed, screen updates will
* complete, but have no effect.
*
* SDL_Init() installs signal handlers for SIGINT (keyboard interrupt)
* and SIGTERM (system termination request), if handlers do not already
* exist, that generate ::SDL_QUIT events as well. There is no way
* to determine the cause of an ::SDL_QUIT event, but setting a signal
* handler in your application will override the default generation of
* quit events for that signal.
*
* \sa SDL_Quit()
SDL_EventType#SDL_QUIT
An SDL_QUIT event is generated when the user clicks on the close button of the last existing window.
You shouldn't call SDL_Quit() in the destructor, but only once before leaving the application (is suggested to use it with atexit())
--- a/main.cpp
+++ b/main.cpp
## -32,17 +32,18 ## int main() {
}
-
// Delete Windows
- delete window;
- delete window2;
- delete window3;
+ // delete window;
+ // delete window2;
+ // delete window3;
// reset pointer
window = nullptr;
window2 = nullptr;
window3 = nullptr;
+ SDL_Quit();
+
// Close Program properly
return 0;
}
--- a/video/window.cpp
+++ b/video/window.cpp
## -51,7 +51,7 ## Window::~Window() {
// Shutdown if last window closed
if(this->windows.empty()) {
// Shutdown Video System
- SDL_Quit();
+ // SDL_Quit();
std::cout << "shuted down Video" << std::endl;
}
}
I need to create a simple GUI which displays images, the images in this example can change and the GUI will need to update it's contents.
I wrote the following update function in my widget class:
void myClass::updatePic() {
QPixmap pix("./pic.png");
int width = ui->picLabel->width();
int height = ui->picLabel->height();
ui->picLabel->setPixmap(pix.scaled(width,height,Qt::KeepAspectRatio));}
I try to use it in the following manner:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
myClass w;
w.show();
sleep(3);
w.updatePic();
sleep(3);
w.updatePic();
sleep(3);
return a.exec();}
But the window just opens and does not display the images until we get to the a.exec() line, and then it opens the last image. What am I doing wrong?
EDIT:
Clarification, the trigger for changing the images comes from an external program (specifically, the gui will be a node in ros, and will be triggered by another node). Is there a way to push a button not from the gui via an external program? the timer will work but I dislike this "busy wait" style solutions.
Thanks for the suggestions so far
exec runs the QT event loop, which includes rendering widgets.
So move your updatePic call into your widget and activate it by for example a button or in the show event
At first learn more about event loop. In particular, you must know that all events like paintEvent or resizeEvent are usually called on corresponding events handle. The events handle is usually called by the event loop, i.e. inside of exec function.
Let's unite answers of #MohaBou and #RvdK. You need to handle timer shots after the exec call. Use QObject::timerEvent for this.
myClass::myClass()
{
<...>
// This two variables are members of myClass.
_timerId = startTimer(3000);
_updatesCount = 0;
}
myClass::~myClass()
{
<...>
// For any case. As far as I remember, otherwise the late event
// may be handled after the destructor. Maybe it is false, do
// not remember...
if (_timerId >= 0) {
killTimer(_timerId);
_timerId = - 1;
}
}
myClass::timerEvent(QTimerEvent *event)
{
if (event->timerId() == _timerId) {
if (_updatesCount < 2) {
updatePic();
++_updatesCount;
} else {
killTimer(_timerId);
_timerId = - 1;
}
}
}
The startTimer method here adds especial timer event to the event query every 3 seconds. As all events, it may be handled only when the event loop will take control and all earlier events are handled. Because of it you can have a duration if many "heavy" events are handled.
EDIT: sorry, I didn't understand #MohaBou at first read. His answer with explicit QTimer is also good enough (but I still don't understand a part about modality).
The function exec also renders the child widgets. exec() blocks the application flow while show() doesn't. So, exec is mainly used for modal dialogs.
I recommend to update it in your custom witget by using a refresh timer. Use a QTimer to update the image every 3 secs:
QTimer* timer = new QTimer(this);
timer->setInterval(3000);
connect(timer, SINGAL(timeout()), this, SLOT(updatPicture()));
Update your picture in your custom slot:
MainWindow::updatePicture() {
updatePic()
}
If you want, you could use a lambda function:
connect(timer, &QTimer::timeout, this, [&w]() {
updatePic()
});
In a particular situation I need my command line based C++ application to launch a quick dialog using gtkmm 2.4. I could really use some direction here.
I tried launching the dialog as a standalone without initializing the top level window:
Gtk::Main kit( NULL,NULL );
Gtk::Window toplevel;
MyApp::myDialog d(toplevel);
int result = d.run();
This created my dialog but it doesn't close when the ok or cancel button is hit and none of quit/delete/hide api calls I could find could get rid of it. It only goes away when the program exits (even if it is created in a method which exits earlier). I'm guessing this is in part because it needs an active main window to handle some of its lifetime/visibility management. If I could make it respond normally to the ok/cancel buttons I would be all set!
Next I tried creating and launching the main window properly and launching the dialog from within the constructor of the main window. (It takes the Gtk::Main as an argument so I could try killing that directly.)
class cprompt : public Gtk::Window
{
public:
cprompt(Gtk::Main* prompt){
MyApp::myDialog* d = new MyApp::myDialog (*this);
std::cout << "ABOUT TO RUN DIALOG" << std::endl;
int result = d->run();
std::cout << "RAN DIALOG" << std::endl;
d->hide();
delete d;
std::cout << "CALLING QUIT" << std::endl;
this->hide();
Gtk::Main::quit();
prompt->quit();
//None of the above calls do anything. The empty 'top level' window hangs around and blocks until manually closed.
std::cout << "CALLED QUIT" << std::endl;
};
virtual ~cprompt(){};
};
Now the dialog works as expected, but the main window pops up after the dialog is closed (an empty gray square with an exit button) and I can't find a way to hide or exit it outside of clicking the exit button. All the calls I make to close it or quit the gtk loop automatically are inside the constructor so I'm guessing they aren't valid at that point. If I could make the whole operation shut down after the dialog returns in the window constructor, again I would be all set.
My next approach is going to be to use the top level window itself as the dialog, but I was hoping to avoid this because the dialog I need is already provided by another library and I'll have to re-implement the ui from scratch if I can't launch the dialog straight up.
Had the same problem with Gtk. To fix it, I neeeded to manually close the window and then do the gtk loop iterations. My code looks like this (for a filechooser_dialog) :
gint result = gtk_dialog_run(GTK_DIALOG(m_fileDialog));
if(result == GTK_RESPONSE_ACCEPT)
{
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(m_fileDialog));
m_selectedFileName = std::string(filename);
g_free(filename);
}
gtk_window_close(GTK_WINDOW(m_fileDialog)); //Close the dialog manually
while (gtk_events_pending()) //until there are no more events :
gtk_main_iteration_do(false); //process the main iteration
I am using FLTK. I have a window with a variety of buttons the user can click to perform some action. In my int main() i have a switch statement to handle all of these. When the user clicks exit the switch statement is setup like so:
case Exit_program:
cout << "save files and exit\n";
do_save_exit(sw);
This goes to the do_save_exit function that creates a exit confirmation window with two buttons yes (exit) and no (don't exit). I got the yes button to work, exits the program, but the no button mean i should just hide the confirmation window. This is the follow functions:
void yes(Address addr, Address)
{
exit(0);
}
void no(Address addr, Address)
{
}
void do_save_exit(Window& w)
{
Window quit(Point(w.x()+100, w.y()+100), 250, 55, "Exit confirmation");
Text conf(Point(15,15),"Do you really want to save and exit?");
Button yes(Point(60, 20),35,30,"Yes",yes);
Button no(Point(140, 20),35,30,"No",no);
quit.attach(conf);
quit.attach(yes);
quit.attach(no);
wait_for_main_window_click();
}
The problem is, when i click the no button it goes to void no, but I can't go anywhere from there. I just want to do quit.hide() but the no function doesn't have sight of the quit window (out of scope). How should I proceed? Thank you
P.S: I have thought about using a pointer to the quit window and then using the pointer to quit the window in the no function but am not sure how to do that exactly.
The Fl_Window callback is called when an attempt is made to close the window. The default callback hides the window (and if all windows are hidden, your application ends). If you set your own window callback, you can override this behaviour, so as not to hide the window:
// This window callback allows the user to save & exit, don't save, or cancel.
static void window_cb (Fl_Widget *widget, void *)
{
Fl_Window *window = (Fl_Window *)widget;
// fl_choice presents a modal dialog window with up to three choices.
int result = fl_choice("Do you want to save before quitting?",
"Don't Save", // 0
"Save", // 1
"Cancel" // 2
);
if (result == 0) { // Close without saving
window->hide();
} else if (result == 1) { // Save and close
save();
window->hide();
} else if (result == 2) { // Cancel / don't close
// don't do anything
}
}
Set your window's callback wherever you set up your Fl_Window, e.g. in your main function:
window->callback( win_cb );
You probably need to look at using a modal (i.e., dialog) window. Look at <FL/fl_ask.h>
if (fl_ask("Do you really want to save and exit?"))
save_and_exit();
The header also has functions for the popup's font, title, etc.
When you build you don't get an error or warning? The problem is probably that you have both global functions names yes and no and also local variables called just the same. Rename either the functions of the variables.
No need to use hide().
You can simply use exit(0); in a callback.