Updating text of a local QLabel as data is altered in C++ - 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.

Related

QT - QTableView removeRow() crashing

This function should remove a row from my QStandardItemModel attached to a QTable View.
void ModManager::delete_Addin(int index)
{
QString addinId;
int i;
addinId = tableModel->item(index,0)->text();
for(i=0;i<modList->size();i++)
{
if(modList->at(i)->Id() == addinId)
{
delete modList->takeAt(i);
break;
}
}
tableModel->removeRow(index);
}
The strange thing is that the program crashes at the last instruction, tableModel->removeRow(index);
And its not going out of range because tableModel->item(index,0) is valid.
What could it be, then?
the code does not present relativity between modList and tableModel. tableModel->item(index,0) was valid before changing modList, while tableModel->rowAt(index) becomes invalid after modifying. There are a few possibilities:
Modifying modList affects tableModel, as #vahancho implies. This can be verified by commenting out the for loop or changing the order of lines. This can be lead by use modList as the real data of tableModel, for example, are you implementing a custom QTableModel by returning modList->at(i) as QTableModel::Data and returning modList->count() as QTableModel::rowCount()?
modList does not affect tableModel, but the item was referenced somewhere else. this cannot be tell from the code.

How to speed up access to QList<qreal> exposed from C++ to QML

I have some problem with speed to access to QList<qreal> property.
I have declared:
Q_PROPERTY(QList<qreal> circlePointsX READ circlePointsX NOTIFY circlePointsXChanged);
QList<qreal> circlePointsX(void) const
{
return mCirclePointsX;
}
and in QML file, I made
pArea.circlesPointsX = paintAreaHelper.circlePointsX;
and after that some code is reading point by point:
var cPointsX = circlesPointsX;
var cPointsY = circlesPointsY;
var noOfPoints = circlesPointsX.length - 4;
for (var i = 0; i <= noOfPoints; i+=4)
{
ctx.moveTo(cPointsX[i], cPointsY[i]);
ctx.lineTo(cPointsX[i+1], cPointsY[i+1]);
ctx.lineTo(cPointsX[i+2], cPointsY[i+2]);
ctx.lineTo(cPointsX[i+3], cPointsY[i+3]);
ctx.lineTo(cPointsX[i], cPointsY[i]);
}
of course the type of property is var
property var circlesPointsX;#
and assignment:
var cPointsX = circlesPointsX;
does not speed up anything, because it's just copying the reference.
I debuged it, and for every single loop access, the c++ method is called.
I would like to copy the data from c++ once and access it from "local qml copy" instead of calling c++ getter every time.
The documentation sheds some light on it:
If the sequence is exposed as a Q_PROPERTY, accessing any value in the sequence by index will cause the sequence data to be read from the QObject's property, then a read to occur. Similarly, modifying any value in the sequence will cause the sequence data to be read, and then the modification will be performed and the modified sequence will be written back to the QObject's property.
If the sequence is returned from a Q_INVOKABLE function, access and mutation is much cheaper, as no QObject property read or write occurs; instead, the C++ sequence data is accessed and modified directly.
So, your solution is to declare circlePointsX as:
Q_INVOKABLE QList<qreal> circlePointsX() const { return mCirclePointsX; }
You should drop the circlePoints property, or rename it to something else.
Nitpick: Putting void in the parameter list is a C-ism that has no place in C++. The reason for it in C was that void foo() is equivalent to void foo(...). This is no longer the case in C++.

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

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

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

Receiving assert failure on Reference Call

(Disclaimer: I have removed the Qt tag in case the problem is in my syntax / understanding of the references involved here)
I have a foreach loop with an object Member. When I enumerate through the list and try to access a member field, the debugger stops and I get a message:
Stopped: 'signal-received' -
The assert failure is:
inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }
I have checked if the member is NULL, and it isn't. I have tried re-working the code, but I keep failing on this simple call.
Some thing's I missed out. MemberList is a singleton (definitely initialized and returns a valid pointer) that is created as the application launches and populates the MemberList with Members from a file. When this is created, there are definitely values, as I print them to qDebug(). This page is literally the next page. I am unsure as to how the List items can be destroyed.
The code is as follows:
int i = 0;
QList<Member*> members = ml->getMembers();
foreach (Member* mem, members)
{
QString memID = mem->getMemberID(); // Crash happens here
QListWidgetItem *lstItem = new QListWidgetItem(memID, lsvMembers);
lsvMembers->insertItem(i, lstItem);
i++;
}
The Member classes get is as follows:
QString getMemberID() const;
and the actual function is:
QString Member::getMemberID() const
{
return MemberID;
}
The ml variable is received as follows:
QList<Member*> MemberList::getMembers()
{
return MemberList::getInstance()->memberList;
}
Where memberList is a private variable.
Final answer:
I decided to rework the singleton completely and found that I was not instantiating a new Member, rather reusing the previous object over and over. This caused the double reference. S'pose thats pointers for you. Special thanks to Troubadour for the effort!
If mem is not null it could still be the case that the pointer is dangling i.e. the Member it was pointing to has been deleted.
If Member inherits from QObject then you could temporarily change your QList<Member*> that is stored in ml (assuming that's what's stored in ml) into a QList< QPointer<Member> >. If you then get a null QPointer in the list after calling getMembers or at any point during the loop then the object must have been destroyed at some point.
Edit
As regards the singleton, are you sure it's initiliased properly? In other words does MemberList::getInstance() return a valid pointer or just a random uninitialised one?
Edit2
Since we've exhausted most possibilities I guess it must be in the singleton somewhere. All I can suggest is to keep querying the first item in the list to find out exactly where it goes bad.