Using signals and the ->connect member function.

Started by desocupado, 16 October 2015, 21:05:21

desocupado

So. I'm trying to make a button do something. I've got a this button created inside a member function of my "main_menu" class. I'm trying to make the button call another member function of "main_menu" class, and I'm not having much success. The commented line is the offending one, and I've tried different arguments on that connect function with no success.

If you don't mind me saying, the tutorials could be a little clearer and explain stuff a little more in detail.

Lemme provide some minimal code:

#include "Main_Menu.h"

Quotevoid Main_Menu::start(tgui::Gui *gui, sf::RenderWindow *window)
{
   auto theme = std::make_shared<tgui::Theme>("Black.txt");
   
   tgui::Picture::Ptr main_menu_pic = std::make_shared<tgui::Picture>("splash.jpg");
   
   tgui::Button::Ptr new_game_button = theme->load("Button");
   new_game_button->setText("New Game");
   new_game_button->setPosition(1000, 200);
   new_game_button->setSize(100, 70);
   //new_game_button->connect("bpressed", &Main_Menu::button_pressed, this);

   gui->add(main_menu_pic);
   gui->add(new_game_button);

   while (window->isOpen())
   {
      sf::Event event;
      while (window->pollEvent(event))
      {
         if (event.type == sf::Event::Closed)
            window->close();

         gui->handleEvent(event);
      }

      window->clear();
      gui->draw();
      window->display();
   }
}

void Main_Menu::button_pressed()
{
   std::cout << "Button pressed" << std::endl;
}

texus

The first parameter to the connect function is the name of the trigger to bind which should be "pressed", not "bpressed".
If that is not the problem then post the error you are getting, the other parameters seem to be correct though.

QuoteIf you don't mind me saying, the tutorials could be a little clearer and explain stuff a little more in detail.
It's hard for me to know which things have to be explained better, if you have suggestions on what to add to the tutorial to make it easier to understand then feel free to suggest them.

desocupado

Changing from "bpressed" to pressed solved the issue. Thanks.

About what could be clearer, well, I made that mistake because I didn't know exactly what that parameter meant.
Explaining what a signal is and where it goes when you click the function would go a long way.

Another thing that I wondered about, is what is the scope of the function that is called by the button, once it's connected. I assumed it's the same scope as where the button was created, am I right?

Also, what if the function has a return type? Let's say return type int. How could I catch that int when the function returns?

Sorry if those questions are a bit (or a lot) amateurish, but I guess we all have to start somewhere.

texus

Quotewhat is the scope of the function that is called
The function can be anywhere, just like you can call any function yourself as long as it has been declared before.

QuoteAlso, what if the function has a return type? Let's say return type int. How could I catch that int when the function returns?
This is not possible. You wouldn't be able to access the return value even if the function would return something since the function is called internally and there is nowhere in your code where you would get the return. If a function need to "return" something then give it a reference parameter and pass it with std::ref.
Code (cpp) Select
void f(int& x) { x = 5; }
int i = 0;
button->connect("pressed", f, std::ref(i));
// i is 0 here but will become 5 once the callback is triggered

texus

QuoteAnother thing that I wondered about, is what is the scope of the function that is called by the button, once it's connected. I assumed it's the same scope as where the button was created, am I right?
The question didn't make much sense earlier but I realize now that you might have used the wrong term, I guess the question is rather what the lifetime would be, not the scope.
Technically a function doesn't has a lifetime so if taken literally the question still wouldn't make sense but if the function is a member function of a class then there is the lifetime of the class to take into account. If the class has been destroyed before the callback is send then the function will be called with an invalid 'this' pointer. Trying to access any member variable would lead to a crash.
So the answer that I think you were looking for is that the class object of which you bind the function indeed has to live longer than the button or live until you unbind the signal yourself.

desocupado

#5
Quote
This is not possible. You wouldn't be able to access the return value even if the function would return something since the function is called internally and there is nowhere in your code where you would get the return. If a function need to "return" something then give it a reference parameter and pass it with std::ref.
Code (cpp) Select
void f(int& x) { x = 5; }
int i = 0;
button->connect("pressed", f, std::ref(i));
// i is 0 here but will become 5 once the callback is triggered


I tried using "&i" and it didn't work, but when I wrote "std::ref(i)" thinks worked properly. Why is that? Shouldn't it be the same thing?

texus

#6
When you want to pass functions and parameters in c++ in a flexible way then you need std::function and std::bind, which is why they are used by the tgui callback system. You might want to read a few tutorials about them to understand a bit how they work because unless you use unbound parameters the connect function will work exactly the same as an std::bind call (except for the signal name that you pass as first parameter).

My connect function actually takes care of the std::bind itself to make it easier but the same rules apply as if you write:
Code (cpp) Select
button->connect("pressed", std::bind(f, std::ref(i)));

So the reason why you need the std::ref is all because of how std::bind works. Just consider that std::bind will copy all parameters for later use, if the function takes a reference then it will get a reference to this copied variable. The std::ref is probably something like a pointer, instead of the original variable a pointer to it will be copied and when the function gets called it will be dereferenced for you. I don't know how std::function, std::bind and std::ref work internally, but that is basically how they behave.