Displaying 2 windows in SFML.

Hello! I've made a program in C++ using SFML library (helped by CodeAsassin) which parctically creates a window, puts in it a picture (like a button) and if you press on that picture, the program will do something. In that case, I want to show a window. More exact, when the user press that image (button), I want to appear another window. I've implemented something but I have one little problem: when I press the image, that window appears but It dispppears quickly. I want it to appear and to stay there until I close it.
This is the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<SFML/Graphics.hpp>
#include<SFML/Audio.hpp>
#include<SFML/Window.hpp>

int main()
{
    sf::RenderWindow wnd(sf::VideoMode(400, 500), "Window");

    sf::Image img;
    sf::Music msc;
    if(!msc.OpenFromFile("rds.ogg"))
    return EXIT_FAILURE;
    if(!img.LoadFromFile("xqp.jpg"))
    return EXIT_FAILURE;
    sf::Sprite spr(img);
    spr.SetPosition(300, 300);
    while(wnd.IsOpened())
    {
        sf::Event ev;
        while(wnd.GetEvent(ev))
        {
            if(ev.Type==sf::Event::Closed)
            wnd.Close();
            if(((wnd.GetInput().GetMouseX())>300)&&((wnd.GetInput().GetMouseY())<322)&&((wnd.GetInput().GetMouseX())<344)&&((wnd.GetInput().GetMouseY())>300))
            {
                 if(wnd.GetInput().IsMouseButtonDown(sf::Mouse::Left))
                 {
                             sf::RenderWindow wn(sf::VideoMode(300, 400), "WND");
                             wn.Clear();
                             wn.Display();

                 }
            }
        }

        wnd.Clear();
        wnd.Draw(spr);
        wnd.Display();
    }
    return EXIT_SUCCESS;
}
Thanks.
1
2
3
4
5
6
7
                 if(wnd.GetInput().IsMouseButtonDown(sf::Mouse::Left))
                 {
                             sf::RenderWindow wn(sf::VideoMode(300, 400), "WND");
                             wn.Clear();
                             wn.Display();

                 }


'wn' here is your window. You made it local to this if block, so as soon as this if block exits, your 'wn' object goes out of scope and is destroyed, which kills the window.

You'll have to give 'wn' a broader scope if you want it to live longer.

Also, multiple windows gets tricky, since you'll need to poll events for each window. Right now your code is polling events for your main window 'wnd' (ie: wnd.GetEvent). You'll have to do the same with any other windows you want (like 'wn').

HOWEVER, GetEvent isn't really good to use when you have multiple windows because it waits for an event to happen. Consider, for example, your program is waiting for events to occur on 'wnd', but the user is doing all sorts of things on 'wn'. The 'wn' events would be piling up and you wouldn't be processing any of them because your program is stuck until 'wnd' gets an event.

You'll need to make a more dynamic event poller if you want this to work. One which checks events on all windows.


Basically, adding a new window adds a bunch of complexity that might not be worth it. Don't get me wrong, it's certainly doable (and once you understand what's going on, it isn't really that hard), but is it really necessary for what you want?
Actually you helped me. I think I've done it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<SFML/Graphics.hpp>
#include<SFML/Audio.hpp>
#include<SFML/Window.hpp>

int main()
{
    sf::RenderWindow wnd(sf::VideoMode(400, 500), "Window");

    sf::Image img;
    sf::Music msc;
    if(!msc.OpenFromFile("rds.ogg"))
    return EXIT_FAILURE;
    if(!img.LoadFromFile("xqp.jpg"))
    return EXIT_FAILURE;
    sf::Sprite spr(img);
    spr.SetPosition(300, 300);
    while(wnd.IsOpened())
    {
        sf::Event ev;
        while(wnd.GetEvent(ev))
        {
            if(ev.Type==sf::Event::Closed)
            wnd.Close();
            if(((wnd.GetInput().GetMouseX())>300)&&((wnd.GetInput().GetMouseY())<322)&&((wnd.GetInput().GetMouseX())<344)&&((wnd.GetInput().GetMouseY())>300))
            {
                 if(wnd.GetInput().IsMouseButtonDown(sf::Mouse::Left))
                 {
                             sf::RenderWindow wn(sf::VideoMode(300, 400), "WND");
                             while(wn.IsOpened())
                             {wn.Clear();
                             wn.Display();}

                 }
            }
        }

        wnd.Clear();
        wnd.Draw(spr);
        wnd.Display();
    }
    return EXIT_SUCCESS;
}

Thanks!
Last edited on
Ehhh... you might have problems with this.

For one you're not checking for a close event in your 'wn' window, so it'll be impossible to close. You also do a deadlock spin as long as the window is open, which means you wont' be able to process any events for your main 'wnd' window as long as 'wn' is open.

So basically, clicking that button is going to deadlock your program.
You're right but the problem is a bit different. If I put sf::RenderWindow wn(sf::VideoMode(300, 400), "WND"); at the beginning, near the main window, on the screen will appear both windows (without pressing that button). I've modified a bit the program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<SFML/Graphics.hpp>
#include<SFML/Audio.hpp>
#include<SFML/Window.hpp>

int main()
{
    sf::RenderWindow wnd(sf::VideoMode(400, 500), "Window");
        sf::Image img;
    sf::Music msc;
    
    if(!msc.OpenFromFile("rds.ogg"))
    return EXIT_FAILURE;
    if(!img.LoadFromFile("xqp.jpg"))
    return EXIT_FAILURE;
    sf::Sprite spr(img);
    spr.SetPosition(300, 300);
    while(wnd.IsOpened())
    {
        sf::Event ev;
        while(wnd.GetEvent(ev))
        {
            if(ev.Type==sf::Event::Closed)
            wnd.Close();
            if(((wnd.GetInput().GetMouseX())>300)&&((wnd.GetInput().GetMouseY())<322)&&((wnd.GetInput().GetMouseX())<344)&&((wnd.GetInput().GetMouseY())>300))
            {
                 if(wnd.GetInput().IsMouseButtonDown(sf::Mouse::Left))
                 {
                             sf::RenderWindow wn(sf::VideoMode(300, 400), "WND");

                             while(wn.IsOpened())
                             {  sf::Event evv;                     //those 
                                if(evv.Type==sf::Event::Closed)    //are
                                return EXIT_FAILURE;                // the modifications
                                wn.Clear();
                                wn.Display();
                             }

                 }
            }
        }

        wnd.Clear();
        wnd.Draw(spr);
        
        wnd.Display();
    }
    return EXIT_SUCCESS;
}
It does the same as the other code. How shall I do it?
1
2
3
4
5
6
7
                             while(wn.IsOpened())
                             {  sf::Event evv;                     //those 
                                if(evv.Type==sf::Event::Closed)    //are
                                return EXIT_FAILURE;                // the modifications
                                wn.Clear();
                                wn.Display();
                             }


You never initialize 'evv'. It is useless here.

EDIT: and again, the while(wn.IsOpened()) means you're spinning on 'wn', which means 'wnd' will be completely unresponsive until 'wn' is closed. /EDIT

Anyway, if you want to to this "the right way" you'll have to really reorganize a lot of stuff. Remember, each window gets its own events and if you want the program to remain responsive, you'll need to poll ALL open windows and handle their events. If you're not processing a window's events, that window is unresponsive.

GetEvent will not work for this because it waits on a single window's event handler. So basically if you have multiple windows and you're using GetEvent, you're doing it wrong.

One way to solve this would be to have a global window manager that keeps track of all the open windows, and polls/dispatches all their events (using whatever function SFML gives you to poll events... it might be PollEvent, but I'd have to check and I'm too lazy to check right now).

The simplest way to accomplish this would be to derive a class from sf::RenderWindow, and give it some kind of 'handle event' pure virtual function. Then derive more classes from that one which represent each of your window.

As you can see it's very involved and you're not going to do this by just throwing a line or two in main(). Managing multiple windows is a relatively complicated task... so again I have to ask... is this really worth it? What kind of program are you making?
Last edited on
I am making this program for myself. I just want to do more things in this new library. Actually I want to make a program like this: http://speedy.sh/ZjCMq/intrebare.exe. This program is made in WINAPI. It is so hard to display 2 windows?
Last edited on
I've found something: sf::WindowListener. I really don't know how to use it but I think it's about "listening windows" or get events for windows. This is the negative part of the documentation of that library: It don't explains every function separately.
Can anyone of this forum help me? I am really stuck in it.
Topic archived. No new replies allowed.