How do I reference successive components (button1, button2, etc.)? - c++

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) {
// ...
}

Related

Updating text of a local QLabel as data is altered in C++

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.

How to implement a system to show Properties of an object // building a game engine

I am currently building a game engine.
UML-Diagram:
[MainWindow]
[CNode]
[CNode2D]
[CSprite2D]
Each node has it's own properties (say "name", "width", "height"), where each node-type has different properties.
Now, I want to show the properties of my nodes like this (bottom right):
To reach this, I have a struct which represents every property like
struct propertyTemplate {
propertyTemplate (QString namei, QString valuei, QString expectedi, bool changedi = false) {
name = namei;
value = valuei;
expected = expectedi;
changed = changedi;
}
QString name;
QString value;
QString expected; //What the programm expects;
bool changed = false; //if these are the default values
};
CNode has a vector of propertyTemplate which gets filled in the constructor of CSprite2D
properties.push_back(propertyTemplate("Name", getName(), "string"));
properties.push_back(propertyTemplate("Sprite-link", "", "link"));
properties.push_back(propertyTemplate("Height", "100", "float"));
properties.push_back(propertyTemplate("Width", "100", "float"));
properties.push_back(propertyTemplate("Position X", "0", "float"));
properties.push_back(propertyTemplate("Position Y", "0", "float"));
properties.push_back(propertyTemplate("Rotation", "0", "string"));
When the properties have to be shown, I run this function:
void MainWindow::listProperties () {
if (ui->treeWidget_2->currentItem()->text(ui->treeWidget_2->columnAt(100)) == "Sprite2D") {
CSprite2D *sprite = static_cast< CSprite2D* >(getNodeByName(ui->treeWidget_2->currentItem()->text(ui->treeWidget_2->columnAt(0)))); //gets a pointer to the string judging by nam
if (sprite == NULL) { //if the return value is NULL
} else {
int counter = 0; //counter for the while-loop
//for ( auto momItem : propertyItems) delete momItem; //item
ui->treeWidget_3->clear();
propertyItems.clear();
while (counter < sprite->properties.size()) {
propertyTemplate prop = sprite->properties[counter]; //gets the current item in the property-array
propertyItems.push_back(new QTreeWidgetItem); //gets filled by the while-loop
propertyItems[propertyItems.size()-1]->setText(0, prop.name); //sets the text of the item
propertyItems[propertyItems.size()-1]->setText(1, prop.value);//set the text of the other item
propertyItems[propertyItems.size()-1]->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);;
ui->treeWidget_3->insertTopLevelItem(ui->treeWidget_3->topLevelItemCount(), propertyItems[propertyItems.size()-1]); //appends the items
counter ++;
}
}
}
}
The problem: When the user wants to change the property, the program really doesn't know what setter to call, because the real properties are stored in variables... The array of propertyTemplate is really much just for showing the properties inside of the QTreeWidget.
My toughts are that you could add a function-pointer when initialising the array, but I wasn't able to because the pointers are inside of a function... I also tried a lambda, but
properties.push_back(propertyTemplate("Rotation", "0", "string",[&this] () {setName()};));
gave me a compiler-error (I updated the constructor of propertyTemplate therefore, so don't blame the constructor ;))
I already talked to my rubber-duck and friends, and they both told me to use another approach, but I can't think of one better.
ALSO: I'm new to C++/Qt so I might just be the victim of trivial errors. In this case sorry.
I think you are going about this the wrong way. You should really use the Qt property system for a bunch of reasons, but mainly those two:
First and foremost, Qt has the MOC - the meta object compiler, which generates meta information for every object. That information contains the count of properties, their names and so on. You can iterate the properties and access detailed information about them, you can use that to populate your property editor via a model. This way it will work with arbitrary properties for any object, rather than having some static configuration you will have to go back and do for every new type or type change.
Second - for Qt properties you have QObject::property() and QObject::setProperty() which work in tandem with QVariant that will in most cases be able to handle type conversions. So all you need is to have the property name and a QVariant holding a compatible value, you don't need to know what setters to call and so on. This makes it significantly easier to get and set property values regardless of their type.
The classes you need to look into are QMetaObject and QMetaProperty, possibly also QVariant and QAbstractListModel.
Also, I see you are using GUI elements for data storage, which is a big NO-NO. Don't put data in the GUI, you are inviting future trouble. Keep the data in a data layer, and only access that from the GUI. Put the nodes in a vector, then implement a model adapter for that vector, then use model-view-delegate in the GUI.
So the structure should be something like this:
node
|
nodes vector
|
nodes model <- GUI nodes view <- node delegate
|
selected node
|
QMetaObject/QMetaProperty query
|
property model <- GUI property view <- property delegate
|
set/get

Loop through stackPanel.children genericaly

In code I am dynamically adding controls (e.g. TextBox, ComboBox, Label, etc) that I would like to now loop through and get the values from each applicable (e.g. not Labels) control that a user inputted data for.
foreach (Control control in EditForm.Children)
{
values = new List<string>();
fieldName = control.Name;
if (control is ComboBox)
{
ComboBox cmb = control as ComboBox;
string value = cmb.SelectedValue.ToString();
}
}
The problem is that I get an error during runtime of
Unable to cast object of type 'System.Windows.Controls.TextBlock' to
type 'System.Windows.Controls.Control'.
Is there a more generic class I should be using instead of 'Control'? How can I loop through each control and have access to the needed values (includes the control's Name)
"UIElement" is most likely a safe choice.
If you're only interested elements of a specific type, you can also consider using Linq to filter out elements by class type (don't forget to include a "using System.Linq" directive):
foreach (ComboBox combo in EditForm.Children.OfType<ComboBox>())
{
//...
}

c++ - Creating class instance in function and using it later

Can I do this:
static Toggle GetAutoUpdatedToggle(DWORD key, bool initialState = false)
{
Toggle tempToggle(key, initialState);
autoUpdateToggles.push_back(tempToggle); //This is static member - std::vector<Toggle>
return tempToggle;
}
And I'm also using it later like that:
void Toggle::UpdateAllFromFactory() //This is static function
{
for each (Toggle toggle in autoUpdateToggles)
{
toggle.Update();
}
}
Is this good way of doing it?
UPDATE 1 - After your suggestoins:
static Toggle* GetAutoUpdatedToggle(DWORD key, bool initialState = false)
{
Toggle *pToggle = new Toggle(key, initialState);
m_autoUpdateToggles.push_back(pToggle);
return pToggle;
}
void Toggle::UpdateAllFromFactory()
{
for (std::vector<Toggle*>::iterator it = m_autoUpdateToggles.begin(); it < m_autoUpdateToggles.end(); it++)
{
(*it)->Update();
}
}
No, this is not a good way of doing it, because you pass around copies of Toggle:
GetAutoUpdatedToggle returns a copy of the Toggle that it just pushed into the vector. It's not in itself a wrong thing to do, but any manipulations the caller may do on the returned toggle would not be reflected on the one you pushed onto the vector
The for loop goes through elements of the vector, creating a copy for use inside the loop body. Unless the Toggle itself has a pointer-like semantic, the Update() action would not be reflected on Toggle objects inside the vector.
To fix this issue, make GetAutoUpdatedToggle return a reference to the Toggle object that it just pushed onto the vector, and use a vector<Toggle>::iterator object to iterate through the stored toggles. This would let you operate on the actual objects, rather than on their copies.
Your static function returns a copy of the Toggle. .push_back will also create a copy of the toggle. Thus the Toggle you return is not in the autoUpdateToggles and cannot be updated later.
Toggle myToggle = GetAutoUpdatedToggle(key);
/* ... */
Toggle alternativToggle = myToggle;
// alternativToggle == myToggle is true
Toggle::UpdateAllFromFactory();
// alternativToggle == myToggle is still true
Also note that your current implementation of Toggle::UpdateAllFromFactory(); uses copies of Toggles if you didn't use iterators instead.
You'll have to provide a handle to your Toggle object. This can be a simple pointer or any other object that doesn't loose the identity of the specific Toggle when being copied.

List the contents of a vector in Gtk+

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).