I'm practicing a bit with Gtk+. I've been abel to create a window, with a working menu.
I can create test objects(basically, a square, asking the user to input the side length) and store them in a vector, but I can't list them.
What I want is to show a scrolled window listing all of the stored objects, something like:
_Square 1-side:7_
_Square 2-side:25_
Until now, I could show the scrolled window with a text label, but none of the info about the stored objects.
Here's the code that I have tried:
Gtk::Dialog dialog("Listing Squares",false,true);
dialog.set_default_size(500,30);
Gtk::Button close("Close");
close.signal_clicked().connect( sigc::mem_fun(*this,&Window::onFileListButtonClose) );
Gtk::VBox* vbox = dialog.get_vbox();
Gtk::ScrolledWindow sw;
sw.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC);
/** FETCH FROM ARRAY*/
for(unsigned int i(0); i<vc.size();++i){
Gtk::Label label( "Square number " + i );
sw.add( label );
}
sw.show_all_children();
vbox->pack_start( sw );
vbox = 0;
dialog.add_action_widget(close,1);
dialog.show_all_children();
dialog.run();
[EDIT:]
1) vc is a std::vector. It is a class attribute.
2) The piece of code for asking the user to input the length of the square and storing it in vc.
void Window::onMenuFileNew(void) {
Gtk::Dialog dialog("New Square",true,true);
dialog.set_default_size(70,20);
dialog.set_has_separator(true);
Gtk::Button close("Close");
entry.set_max_length(2);
entry.set_text("");
close.signal_clicked().connect( sigc::mem_fun(*this,&Window::onFileNewButtonClose) );
Gtk::Label lab("Square side length:");
Gtk::VBox* vbox = dialog.get_vbox();
vbox->pack_start( lab );
vbox->pack_start( entry );
vbox = 0;
dialog.add_action_widget(close,1);
dialog.show_all_children();
dialog.run();
}
void Window::onFileNewButtonClose(void) {
int side = atoi( (entry.get_text()).c_str() );
vc.push_back(Cuadrado( side ));
}
Any help would be appreciated. :)
PS: Before trying to list the squares, I created some of them!
According to the documentation, the add member function accepts widgets by reference. This means that the objects you pass here must exist throughout the lifetime of the container referencing them. In the for loop, they cease to exist as soon as the loop makes one iteration, if you create the labels before the loop, they cease to exist at the end of the function. You run into the equivalent of this: Returning a reference to a local or temporary variable.
Now, this is a little bit shooting in the dark because I don't really know Gtk and I don't know if the widget is copied somewhere else, so that the original may be destructed, but it looks the way I described it above from a purely C++ point of view.
Just to make sure this is the culprit, define all your labels globally to your application and see if they appear. If they do, you'll know that you need to declare the labels in a way that they are alive (e.g. on heap) after that function/loop are over (and still can be destroyed appropriately).
Related
EDIT 2:
as suspected the solution was hidden in plain sight. Thanks to #hyde for not only a helpful answer but one which taught me something i had not known before.
EDIT:
I had thought adding a little context would be helpful but perhaps it only has caused some misdirection. I'm sure the solution is simple and right under my nose.
I have a function:
cursorPosition(int y, int x)
{
getyx(_win, y, x);
}
i want this function to modify y and x to contain the cursor position of _win, a window object.
instead it returns y = 0, x = 0, persistently.
my question is what am i missing.
i will leave the rest of the post untouched. But you don't need to go further.
--end transmission--
i have a class object which uses the ncurses library and i am trying to get the position of the cursor in the screen.
I can clearly see that the cursor moves when i call wmove and wprintw methods within the window, but when i check the cursor position it reads y = 0, x = 0. I cannot figure out why it will not retrieve the actual cursor position. getyx(stdscr,y,x ) and getsyx(y,x) seem to work fine which both use stdscr, but i need to use a new window and not the stdscr window.
here are some (relevant) details of my code:
I create class called Window.
Window is a wrapper for an ncurses window object, with methods to print to and modify that window:
class Window
{
public:
// if int is 1: calls initscr and creates a newwin to assign to member variable 'win'
// if int is 0: calls delwin, sets member variable '_win' to nullptr then endwin
setWin(int)
// calls setWin(0) if member variable '_win' is not already nullptr
~Window()
// retrieves the cursor position
cursorPosition(int y, int x)
private:
WINDOW * _win = nullptr;
}
HERE is the problem:
cursorPosition just wraps around '''getyx(_win,y,x)''', using the private member variable '''_win'''.
I will later create a shared pointer to this window object called DISPLAY, and i am doing this:
DISPLAY->setWin(1);
int CURSOR_Y, CURSOR_X;
DISPLAY->cursorPosition(CURSOR_Y, CURSOR_X);
std::string yx = std::tostring(CURSOR_Y) + " " + std::to_string(CURSOR_X);
// prints to '_win' with 'wprintw'.
DISPLAY->print(yx)
however i always get back
"0 0"
And indeed this is true because when i try to use to navigate the window CURSOR_Y and CURSOR_X, anything i output is in the top left corner.
getyx etc are macros, and take internally the address of their y and x macro parameters. So you need to be able to modify the original x and y passed to your function.
Since you are using C++, simply use references:
cursorPosition(int &y, int &x)
Now when the macro internally takes address of x and y, it will get the address of the originals, not just local copies.
PS. This once again demonstrates how macros are quite horrible and create confusing code and confusing error messages (just try getyx(_win, 1, 1); to see).
I have the following code, which initializes a label to match an object's id.
for (int i = 0; i < inputVal; ++i)
{
QLabel *newLabel = new QLabel(p0[i]->id, page);
connect(p0, &Npc::setID, [&]
{ newLabel->text() = p0[i]->id; });
layout->addWidget(newLabel);
}
I'm trying to use the above connect to refresh the value of the label any time I change the value of the object's id. However, this doesn't work due to the label going out of scope and its value becoming inaccessible. Is there any way to access the label's value without declaring it outside of this scope?
This also hinges on the slot executing after the signal function does, which I would assume is the case. If it's not, is there any other way to update dynamic labels as object values change?
You can change your code as follows:
for (int i = 0; i < inputVal; ++i)
{
auto *obj = p0[i];
QLabel *newLabel = new QLabel(obj->id, page);
connect(obj, &Npc::setID, [obj, newLabel]
{ newLabel->setText(obj->id); });
layout->addWidget(newLabel);
}
Explanation: even though the pointer newLabel does indeed go out of scope after the loop iteration is finished, the actual QLabel it points to does not - it is created on the heap so it won't be deleted until something deletes it - in your case the layout would take care of it. So you can capture newLabel pointer by value (the address it points to would just be copied into the lambda) as well as the pointer to your object. You'd also need to use proper setText setter method of QLabel to assign new text to it; text method is a getter, it returns a copy of the text stored within the label, not a reference to it.
I'm entirely new to SDL 2 , and I'm hoping to find some help with making my very first proper program for a class in it. We've been provided with some code already for use in this project, which is why I'm not simply using a BlitSurface function to make this solution. If that is indeed the better solution, I'll switch over to that. This is part of a State to be used when the program runs, showing a title image.
I am getting a break error due to a pointer issue in the following code:
void MenuState::Enter()
{
//Is to load the title image used for the State
Sprite* extBackgroundSprite = met_extSystem.met_pointextSpriteManager- >CreateSprite("../assets/Testimage1.bmp" , 0 , 0 , 768 , 1024);
}
Which refers to a Sprite made by a SpriteManager class and CreateSprite function, as seen here:
Sprite * SpriteManager::CreateSprite(const std::string & point_stringFilePath, int point_intX, int point_intY, int point_intWidth, int point_intHeight)
{
auto iter = met_arraypointextTextures.find(point_stringFilePath); //breaks here
if (iter == met_arraypointextTextures.end())
//If the iterator cannot locate the sprite we need in our already loaded memory,
//it needs to be loaded into our map to create pointers
{
SDL_Surface* extSurface = SDL_LoadBMP(point_stringFilePath.c_str());
SDL_Texture* extTexture = SDL_CreateTextureFromSurface(met_pointextRenderer, extSurface);
SDL_FreeSurface(extSurface);
met_arraypointextTextures.insert(std::pair<std::string, SDL_Texture*>(point_stringFilePath, extTexture));
iter = met_arraypointextTextures.find(point_stringFilePath);
}
//Creates the sprite, adds a new index point via pushback
Sprite* extSprite = new Sprite(iter->second, point_intX, point_intY, point_intWidth, point_intHeight);
met_arraypointextSprites.push_back(extSprite);
return extSprite;
}
I hope this is enough information and code to present my problem. If not, let me know! And thank you in advance.
Turns out the issue was impossible to solve with the information I provided. The pointer did indeed need to be initialized, but with arguments found in the constructor, which I had not provided here.
I'm building a simple generic engine for my true start in the making of games, and I am trying to be somehow organized and decent in the making of my engine, meaning I don't want it to be something I throw to the side once I make what I'm planning to.
I add objects to be displayed, drawObjects, and these can either move, not move, and have an animation, or not have one.
In case they DO have an animation, I want to initialize a single animationSet, and this animationSet will have xxx animationComp inside of it. As I'm trying to be neat and have worked abit on "optimizations" towards memory and cpu usage (such as sharing already-loaded image pointers, and whatever came across my mind), I wanted to not ask for possibly unused memory in arrays.
So I had animationSetS* animationSet = NULL; initially, planning to do a animationSet = animationSetS[spacesINEED]; after, only on the objects that needed animation that I added, being those that aren't animations a NULL and therefore not using memory (correct?).
And then this question popped up! (title)
struct animationComp {
SDL_Rect* clip;
int clipsize;
};
struct animationSetS {
animationComp* animation;
int currentFrame;
int currentAnimation;
int animationNumber;
};
struct drawObject { // Um objecto.
char* name;
SDL_Surface* surface;
bool draw = true;
float xPos;
float yPos;
bool willMove = false; // 0 - Won't move, 10 - Moves alot, TO IMPLEMENT
bool isSprite = false;
animationSetS* animationSet;
};
I dabble alot in my questions, sorry for that. For any clarifications reply here, I'll reply within 10 minutes for the next... 1 hour perhaps? Or more.
Thanks!
Setting the pointer to NULL means that you'll be able to add ASSERT(ptr != NULL); and KNOW that your pointer does not accidentally contain some rubbish value from whatever happens to be in the memory it was using.
So, if for some reason, you end up using the object before it's been properly set up, you can detect it.
It also helps if you sometimes don't use a field, you can still call delete stuff; [assuming it's allocated in the first place].
Note that leaving a variable uninitialized means that it can have ANY value within it's valid range [and for some types, outside the valid range - e.g. pointers and floating point values can be "values that are not allowed by the processor"]. This means that it's impossible to "tell" within the code if it has been initialized or not - but things will go horribly wrong if you don't initialize things!
If this should be really implemented in C++ (as you write), why don't you use the C++ Standard Library? Like
struct animationSetS {
std::vector< std::shared_ptr<animationComp> > animation;
// ...
}
I need to get the number after the button to increment in a for loop. For example, button1 becomes button2, etc. I have tried appending a variable which increments but C++ Builder gives an error saying "Button is not a member of TMain." Is there any way to achieve the end goal or get around this?
You can't construct new identifiers from others at run time. The compiler is correct that Button really isn't a member of your TMain class.
Instead, build the string name of the component you want, and then call your form's FindComponent method to get the component with that name.
for (int i = 1; i <= 2; ++i) {
std::string name = "Button" + IntToStr(i);
TButton* button = dynamic_cast<TButton*>(this->FindComponent(name));
}
That requires that the buttons' Name properties be set accordingly.
Another solution is to forego the component names and put your objects in a proper container, like a vector. For example, you can override the Loaded method (which is where you can be sure all your form's components have been created) and fill a vector there:
void TMain::Loaded() {
TForm::Loaded();
this->m_buttons.push_back(Button1);
this->m_buttons.push_back(Button2);
}
Now when you want to iterate over your buttons, you just iterate over the vector instead:
for (std::vector<TButton*>::const_iterator it = m_buttons.begin();
it != m_buttons.end();
++it) {
// ...
}