Problems with Qt iterators in QLinkedList - qt

I know I will be asked for a minimal example, but I am not sure to be able to come up with one at all, or it won't be very minimal. But I may still learn valuable knowledge from this discussion.
So I have a series of data items of a given class organized in a QLinkedList. The user is provided with an interface that allows to navigate through this data and edit it. In particular, some of the possible editing actions require multiple entries in the QLinkedList to be modified - for example all subsequent or all previous entries. Items can also be added or removed to the list, and then edited.
In the code, I simply keep an iterator up to date that points to the current data item in the chain, called m_currentItem. I then use it to display the data at the current item, loop from the current to the end or to the beginning. I require this iterator to be valid throughout the whole execution and lifetime of the application, even if items are added or removed. When the user presses "next" or "previous" the iterator is simply incremented or decremented. When the user deletes an item in the list (except the last), the iterator then points to the next entry and its content is displayed. When a user adds an item in the list, m_currentItem is now made to point to this new item.
This is where things go pretty wrong : On those backwards or forward loops. They often (but not always ...) go over the end condition and continue into unknown data until a major crash ensues. Sometimes it also loops back to the first element in the list (!!!) and then it's an infinite loop for ya.
Here's an example taken from my code, anything obviously wrong ? Anything else I should know about Qt iterators ?
EDIT :
Just learned about reverse iterators ! It seems recommends using them instead of the -- overloaded operator on standard iterators ! But how do I make m_currentItems a reverse iterator ?
Also I read about implicit sharing ... I don't really do anything like that I don't think, but does that go to show that I shouldn't be using long-living iterators like I do ?
include <QLinkedList>
struct Member
{
int memberID;
}
struct Item
{
/* A map of members, each mapped to an ID */
QMap<int, Member> m_members;
QString name;
}
typedef QLinkedList<Item> LinkedItems;
LinkedItems m_items;
LinkedItems::iterator m_currentItem;
...
if(m_currentItem != m_items.end())
{
for(LinkedItems::iterator iter = m_currentItem+1; iter != m_items.end(); iter++)
{
// Do things on *iter
}
}
A more complex implementation : in each linked Items are found Members which contain data, and an ID. It is possible for the user to propagate an ID in all previous items located before an item located (that must itself be located before the current item) that he is asked to choose.
E.g. if we have A-B-C-D-E-F and the user is on E, he can choose to propagate a member ID of E to C and previous items (so A and B, too - but not to D).
Here's the code. Most or all the iterator loops are subject to unexpected behaviors.
void renameAllPreviousMembers(int memberIDToPropagate)
{
/* If first item, then there is no previous altogether */
if(m_currentItem != m_items.begin())
{
if(m_currentItem.m_members.contains(memberIDToPropagate) == false)
{
QMessageBox::critical(nullptr, tr("Rename members in previous items"),
tr("There is no member with the selected ID in the current item."),
QMessageBox::Ok);
return;
}
bool chose_item;
LinkedItems::iterator itemIter;
QStringList previousItems;
QStringList previousItemsBetterOrder;
QMap<QString, LinkedItems::iterator> previousItemsMap;
previousItemsMap.clear();
/* Find all previous items and store them in a QStringList and a map between those strings and actual items */
std::cout << "Search previous items : " << std::flush;
for(itemIter = m_items.begin();
itemIter != m_currentItem;
itemIter++)
{
/* Possibly add conditions on some items that we do not want to apply values to. */
std::cout << itemIter->name << ", " << std::flush;
previousItems << itemIter->name;
previousItemsMap.insert(itemIter->name, itemIter);
}
std::cout << "finished." << std::endl << std::flush;
QStringList::reverse_iterator riter;
for(riter = previousItems.rbegin(); riter != previousItems.rend(); riter++)
{
previousItemsBetterOrder << *riter;
}
QString name_previous_item = QInputDialog::getItem(nullptr,
QString("Previous item"),
QString("Previous item to start from : "),
previousItemsBetterOrder, 0, false, &chose_item);
if(!chose_item)
{
return;
}
/* Decode chosen previous item by retrieving it in the map */
LinkedItems::iterator chosenPrevIter = previousItemsMap.value(name_previous_item);
bool chose_member;
/* Find all existing IDs in previous item */
QStringList previousIDs;
QMap<int, Member>::const_iterator memberIter;
/* Retrieve all members from the chosen previous item */
std::cout << "Search IDs in chosen previous item : " << std::flush;
for(memberIter = chosenPrevIter->m_members.begin(); memberIter != chosenPrevIter->m_members.end(); memberIter++)
{
previousIDs << QString("%1").arg(QString::number(memberIter->ID));
std::cout << memberIter->memberID << ", " << std::flush;
}
std::cout << "finished." << std::endl << std::flush;
/* If no member then no lunch */
if(previousIDs.size() == 0)
{
QMessageBox::critical(nullptr, tr("Rename previous member"),
tr("There are no members in the selected previous item."),
QMessageBox::Ok, QMessageBox::Ok);
return;
std::cout << "Rename previous members finished with no members in chosen previous item." << std::endl << std::flush;
}
QString string_member_id_from_previous = QInputDialog::getItem(nullptr,
QString("Previous member number"),
QString("Member ID from previous item to be used : "),
previousIDs, 0, false, &chose_member);
if(chose_member)
{
int member_id_from_previous = string_member_id_from_previous.toInt();
/* Update member ID at and before chosen previous item */
std::cout << "Update member ID before chosen previous item : " << std::flush;
for(itemIter = chosenPrevIter;
itemIter != m_items.begin();
itemIter--)
{
std::cout << itemIter->name << std::flush;
if(itemIter->m_members.contains(member_id_from_previous))
{
/* Take and reinsert to new ID. */
std::cout << "+" << std::flush;
itemIter->m_members.insert(memberIDToPropagate, itemIter->m_members.take(member_id_from_previous));
}
else
{
/* Do nothing. */
}
std::cout << ", " << std::flush;
}
std::cout << "finished." << std::endl << std::flush;
}
}
else
{
/* Do nothing. */
}
}
else
{
QMessageBox::critical(nullptr, tr("Apply to previous members"),
tr("This is the first item. There are no previous items."),
QMessageBox::Ok);
}
std::cout << "Apply to previous members finished." << std::endl << std::flush;
}

Related

Point Cloud Library and While Loop

i am new to C++ and PCL. i wish to save the values of pointer in while loop and wanted to display the saved one . Here is part of my code . Please guide how to save the value of "coefficients->values[0] ,coefficients->values[1], coefficients->values[2], coefficients->values[3]" in an array each time loop runs.
// While 20% of the original cloud is still there
while (cloud_filtered->points.size () > 0.20 * nr_points)
{
// Segment the largest planar component from the remaining cloud
seg.setInputCloud (cloud_filtered);
seg.segment (*inliers, *coefficients);
if (inliers->indices.size () == 0)
{
std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
break;
}
std::cerr << "Model coefficients: " << coefficients->values[0] << " "
<< coefficients->values[1] << " "
<< coefficients->values[2] << " "
<< coefficients->values[3] << std::endl;
}
I am assuming that you are following this example code since the snippet you added in your question is almost to same. If this is the case, then you can declare a std::vector<pcl::ModelCoefficients> just before the while loop and push the coefficients into that like
std::vector<pcl::ModelCoefficients> coeffs;
while(...){
...
coeffs.push_back(*coefficients);
}
Also check the documentation for pcl::ModelCoefficients here which is nothing but a header and a vector of floats. Note that defining the coeffs as a vector of shared pointers and pushing pointers to the coefficients will not work in this case since previously pushed coefficients will be overwritten by seg.segment(*inliers, *coefficients);.

Doubleclick on QTreeView shall always return first columns value

I have my QTreeView where whole rows were selected:
ui->treeView->setSelectionBehavior (QAbstractItemView::SelectRows);
...and already filled with ID-Number <--> Description. All are structured as a tree. I can click it, and retreive corresponding selection via:
ui->lineEdit->setText( modelIndex.data(Qt::DisplayRole).toString() );
and I already connected:
connect(ui->treeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onSelectedTreeView(QModelIndex)));
When I click on a ID-Number, it works perfectly, and my modelIndex.data() returns the ID-Number.
When I click on the description, my modelIndex.data() returns its correct description (second column). BUT I would like to have again the corresponding ID-Number (first column).
I appreciate any help. Thanks in advance.
You can use the sibling(int row, int column) method of QModelIndex
In python:
def onSelectedTreeView(index):
firstColumnIndex=index.sibling(index.row(),0)
print(firstColumnIndex.data())
I guess you could do:
ui->lineEdit->setText(modelIndex.sibling(modelIndex.row(),0).data(Qt::DisplayRole).toString());
You can change treeView selectionBehavior to SelectItems. Than use index.row() and index.column().. And dont forget that it always start from 0
onSelectedTreeView()
{
//Put all your selected indexes into QModelIndexList
QModelIndexList _indexes = ui->treeView->selectionModel()->selectedIndexes();
// For each loop for every selected index..
foreach (QModelIndex index, _indexes)
{
//if your index data == 0 it means that you clicked into ID-number field.
//So you can easly see which index you clicked with qdebug
qDebug() << "Row = " << index.row() << "\t Column = " << index.column();
// So if you want to get always index of first column(ID-Number) use QAbstractItemModel
if(index.column().toInt() != 0 ) //if your index not equal to 0
{
const QAbstractItemModel* absModel = index.model();
// change index of absModel from index.column() to 0;
QModelIndex changedIndex = absModel->data(absModel->index(index.row(), 0), Qt::DisplayRole).toInt();
qDebug() << "Row = " << changedIndex.row() << "\t Column = " << changedIndex.column();
}
}
}

QTreeView::dropMimeData - setting values for a new child

I am trying to drop some mime encoded text onto a tree view. It is working - the dropMimeData() method is called, I can decode the mime data into the strings that were dropped, I can insert a child into the model which shows up in the view, but ... I can't find a way to set the text value of the new item/row to the string dragged and dropped (or any string for that matter).
Here is some of the code i've tried inside the dropMimeData() method:
if ( ( row == -1) && (column == -1) && parent.isValid() ) {
int mdlidx = this->data(parent, Qt::DisplayRole).ModelIndex;
qDebug() << "mdlidx: " << mdlidx;
// treet text - the text of the cell that gets dropped onto
QString tt = this->data(parent, Qt::DisplayRole).toString();
qDebug() << "tree text: " << tt;
TreeItem *item = this->getItem(parent);
int ccnt = item->childCount();
qDebug() << "ccnt: " << ccnt ;
if ( item->insertChildren(0, 1, 0) ) {
qDebug() << "Child Inserted";
// how do I access the new child item here ???
} else {
qDebug() << "Failed";
}
How do I access the new child item in order to set the text that would be visible in the view?
I'm using the QStandardItemModel, if that makes any difference.
My solution to this is to create a signal and slot - I emot the signal in the dropMimeData() method and the slot is in a part of the code that has the view and model, so can easily update the model.
I send the mime data and the parent across using the signal.
I'm not sure if this is the correct way of doing this, but it is working.

Do you lose the reserved space in a Qt string with the QString::clear() function?

I am working on a loop where I want to copy a few characters from a string to another. I know the limit is around 20 characters so I want to do this outside the loop:
QString name;
name.reserve(25);
That way I have a buffer ready to be filled and Qt avoids reallocating it every time a name is parsed. Only, to get the name I do something like this:
for(int i(0); i < 20 && *s != '\0'; ++i)
{
name += *s;
}
which means I have to reset the name each time. How can I do that and be sure that the reserved space doesn't get lost every time?
// will reserved memory be lost after this call?
name.clear();
// would that be more likely to keep the memory buffer?
name = "";
The documentation does not seem to say one way or the other.
The complete set of loops goes something like this:
QString name;
name.reserve(25);
for(QChar const *s(input.data()); *s != '\0'; ++s)
{
...snip...
if(<some condition>)
{
name.clear() // losing reserved data here?
for(int i(0); i < 20 && *s != '\0'; ++i)
{
name += *s;
}
...snip...
}
...snip...
}
Calling QString::clear() will cause your reserved space to be lost. Consider the following:
QString s;
s.reserve(25);
qDebug() << "Before Clear: " << s.capacity();
s.clear();
qDebug() << "After Clear: " << s.capacity();
Output:
Before Clear: 25
After Clear: 0
The most efficient way to remove the contents of the string without losing your reserved space is to call QString::resize():
QString s;
s.reserve(25);
qDebug() << "Before Resize: " << s.capacity();
s.resize(0);
qDebug() << "After Resize: " << s.capacity();
Output:
Before Resize: 25
After Resize: 25
In the implementation of QString::resize(), a call to resize(0) for strings with reserved capacity amounts to setting the internal size value to 0 and setting the first character of the internal buffer to '\0'.

qt - qprocess start in a loop

I am calling a process in a loop. Need to ensure that one process ends before it starts again. how is it possible.
void MainWindow::setNewProjectInQueueList()
{
// this is already gotten in queueList now loop thru the list and add project
QStringList arguments;
projNm = ui->lineEditCreateProject->text();
qDebug() << " projNm " << projNm;
for (int j= 0; j < queueList.length(); j++)
{ if (! QString(queueList[j]).isEmpty())
{
// call process
// QString queueName = queueList[j];
arguments << "-sq" << queueList[j];
qDebug() << " arguments sq " << queueList[j];
procQueueList.start("qconf",arguments);
}
}
// and append for each queue with new project name
// and store into the system
}
Brgds,
kNish
Call QProcess::waitForFinished() to wait until the process terminates.
Using the waitForFinished approach from within a loop in the main thread will freeze the application. Instead, putting the loop in a separate thread, or making a queue of processes to start and then launch upon the finished signal from the previous one is good alternatives.

Resources