SFML drawing into a child window

Started by RitoonL, 25 May 2020, 18:31:07

texus

I don't have much time now, so maybe if I have more time to read your post I will understand it better, but right now I'm not sure where your problem is exactly. So I'll just quickly comment on some of the things you said.

QuoteI know that i must use canva->display() in my main or in a fuction called in the main, but i don't know how to do as the compiler wants the canvas to be declared
What exactly is the error? Is there no canvas variable (because no such object is declared there), or doesn't it know tgui::Canvas (because you wouldn't be including TGUI.hpp)?

Quotebut if i use (tgui::Canvas::Ptr &canvas) as argument of the fuction, wich prevent copying the canvas, i can't use the function in the main
Then there is something wrong about the way you call it in your main function. You need to have a reference to an existing object, it won't work if you e.g. try to create a new canvas on the same line as your function call.
That being said, TGUI widgets are wrapped in pointers. Without the "&" you are only copying the pointer, which still points to the same canvas, the canvas never gets copied. Taking the argument by reference would only be marginally faster because the Ptr is a std::shared_ptr which has to count the amount of pointers to the object: if it is copied then it has to increment the counter, if you take it by reference then it doesn't. So don't worry about passing it by value, but if the code doesn't compile when you try to use a reference then there is something wrong.

QuoteEdit : I tried to declare
tgui::Canvas::Ptr canvas;
This is equivalent of code like this:
Code (cpp) Select
int *p;
In the best case p would be a nullptr, in the worst case it points to a random memory address. This is why it crashes if you try to use it, you need to actually create the canvas (so that the pointer points to something):
Code (cpp) Select
tgui::Canvas::Ptr canvas = tgui::Canvas::create();

It might help if you created a small demo application that would show the issue (just a main function with just a few functions next to it that would actually be runnable on my pc). Only being able to see some of the code means that I would have to make assumptions about how the rest looks. I know for example that you did something wrong in main, but I can't know what. So it's a bit disorienting to follow what is going on, and right now I don't have time to really try and figure it out.

RitoonL

#16
Hello Texus

Coding is definitly a interesting puzzle.

tgui::Canvas::Ptr canvas = channels_window(window, gui); // that was the solution

i found the solution myself, i was too focused of what the fuctions were taking and never worried of what they were returning. I needed the first fuction called in the main returning a pointer of the canvas, so i could call my second function inside the loop, passing the canva. The canva is now refreshed. Everything is not perfect yet, but still, i'm progressing !!!

thanks for all the time you spent helping me, i know it's no fun helping newbies, specially when they lack of basic knowledges like me, you're not intended to be my teatcher but you had the patience to :) i'm grateful for this.

Regards,

RitoonL

texus

#17
I see now what the problem was. It's hard to help with how you should pass data to different places because there is a lot of freedom in how to do it exactly without there always being a best way. For the simple case that you have here, returning the value like you are doing is probably the best option, but I'll provide some alternatives that you could keep in mind if you face a similar problem later.

I said earlier that tgui::Canvas::Ptr is a simple pointer and thus copying it isn't a problem. If it would have been a large object then returning it might not have been a good option, passing it by reference would have been better. In some cases you can still return a large object in such a way that it is moved instead of copied, but until you fully understand move semantics it might be a good idea to always assume that a copy would take place and pass large objects by reference parameter.
Code (cpp) Select
tgui::Canvas::Ptr canvas;
channels_window(window, gui, canvas);
void channels_window(sf::RenderWindow &window, tgui::Gui& gui, tgui::Canvas::Ptr& canvas)
{
    canvas = tgui::Canvas::create();
    // ...
}

Design wise it might be better to call "tgui::Canvas::create()" before calling the function, but this example is to illustrate that you can pass an uninitialized object into the function which will initialize it.

Another alternative is to use a class member. It channel_sorting and channels_window wouldn't be free functions but instead were part of the same class then you could simply give that class a tgui::Canvas::Ptr member that you initialize in channels_window and use in channel_sorting. There is no need for passing parameters and return values in such case.

Yet another alternative would be to store the tgui::Canvas::Ptr inside the Channel class itself. This might not be a very good option here because you could have many channels and you would unnecessarily be making each channel class a few bytes larger, but it would also be an option in which you wouldn't need to pass the canvas in channel_sorting and draw_channel.

If you e.g. need sf::RenderWindow, tgui::Gui and tgui::Canvas::Ptr in a lot of places then another option would even become to put those together into a class and pass an object of that class around by reference instead of passing them separately.

An alternative that is specific to TGUI widgets is letting the gui store the widget. You can pass a unique name as parameter to the add function that allows you to retrieve it later:
Code (cpp) Select
gui.add(canvas, "MyCanvas");
tgui::Widget::Ptr widget = gui.get("MyCanvas"); // Get the widget back, but as a base class on which you can't call Canvas-specific function
tgui::Canvas::Ptr canvas = gui.get<tgui::Canvas>("MyCanvas"); // Get the widget back, casted to the right type (might crash if widget with name "MyCanvas" wasn't a tgui::Canvas)

So as long as you have access to the tgui::Gui object, you can get access to any widget that was added to it.

So as you can see there are tons of options to get the canvas in the right place (even more than the ones I wrote), but knowing which design fits your needs best comes with experience by just trying and learning which choices didn't work well. If you are unsure which design is better, the best design is most often the one that requires you to write as little code as possible  (while makes the code as readable and understandable as possible, i.e. not focusing on the line count).


Not really related, but I wouldn't use the "ch(x)" define. It makes the code harder to read. If you really need to shorten the line (e.g. if you need to write several lines containing "tab_Channel[address_channel[x]]") then it would be better to just make a reference to the object:
Code (cpp) Select
auto& ch = tab_Channel[address_channel[x]];
ch->draw_channel(canvas,column*52,row*72);

RitoonL

#18
Very interesting answer,

i really like the class solution ... Because i can make each window as a class and forget it when it's done or drawing all windows in a class and make them interact easily. Now that you opened my eyes on it, i think making a class for the  windows is a good way. As i want to make magnetic windows, they can pass their coordonates to each other. would be better for windows arrangement.

i'll bookmark this and read it again and again ... I really think passing things in C++ code is the hardest for me, i need more comprehension for this. Hope it will come with experience.

Regards,

RLF