QTreeView insertRows via method crushs / direct call works - c++

I have following strange problem.
I've implemented a QAbstractItemModel to the point that I can insert child nodes to the tree view but something strange occurs when I try to add the nodes via the insertRows() method.
First where all is called:
QApplication a(argc, argv);
QResource::registerResource("Qt5Tutorial.rcc");
QTreeView *treeView = new QTreeView();
treeView->show();
Node rootNode("rootNode");
CameraNode childNode0("childNode0", &rootNode);
CameraNode childNode1("childNode1", &rootNode);
LightNode childNode2("childNode2", &rootNode);
CameraNode childNode3("childNode3", &childNode0);
TransformNode childNode4("childNode4", &childNode2);
TransformNode tryNode("potato");
// setup model
ObjectTreeModel model(&rootNode);
treeView->setModel(&model);
// insert directly via the insert child method
// this works!
childNode0.insertChild(1, &tryNode);
// get the QModelIndex of childNode1
// must be passed in the insertRows() method
QModelIndex index(model.index(1, 0, QModelIndex()));
// the output is "childNode1" what is totally right
qDebug() << "index: "<<static_cast<Node*>(index.internalPointer())->getName();
// output see posted beneath
qDebug() << rootNode.log();
// should insert in "childNode1" -> at 0th position and just 1 Node object
// see the method beneath
model.insertRows(0, 1, index);
// if i try to call the method rootNode.log(); now again, it crashes
return a.exec();
This is the output from the rootNode.log() call:
---rootNode
---childNode0
---childNode3
---potato
---childNode1
---childNode2
---childNode4
As you can see the "Potato" Node is correctly inserted.
View an image
http://www10.pic-upload.de/04.01.13/m65huuqq4ruu.png
But once I try to expand the childNode1 node it crashes. But look at the last comment in the code above. As i mentioned -> if i try to output the tree view now (it iterates through all nodes) it crashes.
When the method is called everything seems to be ok - just when i try to expend the tree view it crashes -> the debug output let me think that all should be ok
The actual error message is a access violation when reading at position ... (translated from German - don't know if its called the same in English)
bool ObjectTreeModel::insertRows(int position, int row, const QModelIndex &parent)
{
beginInsertRows(parent, position, position + row - 1);
Node *parentNode = getNode(parent);
qDebug() << "parentName: " << parentNode->getName();
bool success = false;
for(int i = position; i < row; i++)
{
qDebug() << "inside loop";
qDebug() << "position: " << position << "row: " << row;
TransformNode childNode("insertedNode");
success = parentNode->insertChild(i, &childNode);
qDebug() << "success: " << success;
}
endInsertRows();
return success;
}
The debug output for the method above:
getNode: successful
parentName: "childNode1"
inside loop
position: 0 row: 1
called inserchild
success: true
I have no idea why this happens becuase the debug output seems right and it should be basically the same as insert the node directly via the insertChild method.
I hope that someone has an idea why it doesn't work.
Best regards, Michael

Almost everything is correct. Just this two lines not:
TransformNode *childNode = new TransformNode("insertedNode");
success = parentNode->insertChild(i, childNode);

Related

Issues trying to make a filter using Qt

I'm trying to make my software filter a list of elements.
I think I need to put my data in a TableWidget because I have to display other information.
I also put 3 Line Edit to search respectively in each category (called leName, leSource, leDestination)
so I tried to implement my filter for the name property, for now I have :
void MyClass::on_leName_textEdited(const QString &arg1)
{
for (int i=0;ui->tableWidget->rowCount()-1;i++)
{
qDebug()<<"before if";
if(ui->tableWidget->item(0,1)->text().contains(arg1) //tried with &arg1, I have the same issue, for now
{
qDebug()<<"If validated";
ui->tableWidget->showRow(i);
}else{
qDebug()<<"If not validated"
ui->tableWidget->hideRow(i)
}
}
}
When I hit a key, my soft crashes
I get "Before if", "If (not) validated", "Before if" from qDebug
I launched with debbugger and got Segmentation fault as error
not sure what I could add as detail about my error
Maybe I did nothing in the good way, I'm no expert in Qt nor c++
If you have any idea of what I should do to correct this, I would appreciate it =)
The fact that a cell exists does not imply that it has an associated QTableWidgetItem, so you must verify that it is not null.
QTableWidgetItem * item = ui->tableWidget->item(0, 1);
if(item && item->text().contains(arg1))
{
qDebug() << "If validated";
ui->tableWidget->showRow(i);
}else{
qDebug() << "If not validated"
ui->tableWidget->hideRow(i)
}

Id of an item in QTreeWidget changed

There is something very strange about Qt.
I have a button ui->addPointButton and a QtreeWidget ui->pointListBox. When I click on the button, it adds a point to the tree. mScenePtr is a pointer to the class I put all my points.AddPoint is a class creating a window asking for some information about the point.
void AddPointsWindow::on_addPointButton_clicked(bool clicked)
{
Q_UNUSED(clicked);
AddPoint addPointWindow(mScenePtr->getColor_or_texture());
int addPointWindowResult = addPointWindow.exec();
if (addPointWindowResult == QDialog::Accepted)
{
SVertex vertex = addPointWindow.getVertex();
mScenePtr->addVertex(vertex);
QTreeWidgetItem* itemPtr = new QTreeWidgetItem(ui->pointListBox);
cout << "id" << ui->pointListBox->indexOfTopLevelItem(itemPtr) << endl;
//itemPtr->setText(0,QString::number(mScenePtr->getVertexNumber()));
//itemPtr->setText(0, QString::number(ui->pointListBox->indexOfTopLevelItem(itemPtr)));
itemPtr->setText(0, "hjhjh");
cout << "id" << ui->pointListBox->indexOfTopLevelItem(itemPtr) << endl;
itemPtr->setText(1, QString::number(vertex.x));
itemPtr->setText(2, QString::number(vertex.y));
itemPtr->setText(3, QString::number(vertex.z));
if (color == mScenePtr->getColor_or_texture())
{
itemPtr->setText(4, QString::number(vertex.r));
itemPtr->setText(5, QString::number(vertex.g));
itemPtr->setText(6, QString::number(vertex.b));
}
//ui->pointListBox->insertTopLevelItem(ui->pointListBox->topLevelItemCount(), itemPtr);
cout << "value : " << vertex.x << endl;
}
}
In this exemple, I click twice on the buttons, create two points with vertex.x = 0 for the first and 1 for the second.
Look at the three lines in the middle :
//itemPtr->setText(0,QString::number(mScenePtr->getVertexNumber()));
//itemPtr->setText(0, QString::number(ui->pointListBox->indexOfTopLevelItem(itemPtr)));
itemPtr->setText(0, "hjhjh");
If there is only the third line, the result is
id0
id0
value : 0
id1
id1
value : 1
Everythong is ok.
But if I put one of the two others lines, the result in both cases is :
id0
id0
value : 0
id1
id0
value : 1
How is it possible ? How can the call to ui->pointListBox->indexOfTopLevelItem(itemPtr) or mScenePtr->getVertexNumber() can change the id of the item ?
Qt 5.5
After using setText, the items in the tree might have sorted automatically.
So in the two commented line cases, when you add the number (using setText), the nodes are getting sorted and the earlier node has become the top level item.
That is the reason you are seeing two different IDs "before setText" and "after setText", when you are querying "top level item".
To see the results properly, Turn off the sorting for the tree. (may be in your constructor)
ui->pointListBox->setSortingEnabled(false);

Initialising a pointer to QListWidgetItem Qt 5.8

In my program I have a series of tabs and on each tab there is a combobox and a QListWidget. Based on the selection on the combobox the QListWidget will be populated.
Now what I am trying to achieve is that one the user presses a "APPLY" button after selecting a series of items on the "checkable" list widget for a given selection in combobox, I will read the bool status of each item on the list widget by using a pointer QListWidgetItem pointer to the list widget
This is part of my code;
void MainWindow::on_applyButton_clicked()
{
//Reset list
MainWindow::revenueList.clear();
//Processing income statement
//Select the first item on inc_st_combo
ui->inc_st_comb->setCurrentText("Revenue Growth");
//Create an iterator
QStringListIterator iter(MainWindow::revenue_labels);
//Loop through the list widget and read bool status
while(iter.hasNext())
{
//Index for the QList
int index = 0;
//Create a QListWidgetItem pointer
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
bool status = listItem->checkState();
qDebug() << "Status: " << status << endl;
MainWindow::revenueList.append(status);
}
qDebug() << "List: " << MainWindow::revenueList << endl;
}
My problem is that when I try to initialise the QLsitWidgetItem on the following line;
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
Qt return the following error;
/Users/Vino/Documents/My Stuff/Qt Projects/Fundemental Analysis/FundementalAnalysis/mainwindow.cpp:389: error: invalid use of non-static data member 'inc_st_list'
QListWidgetItem *listItem = new QListWidgetItem(iter.next(),Ui_MainWindow::inc_st_list);
~~~~~~~~~~~~~~~^~~~~~~~~~~
How do I initialise the QListWidgetItem pointer to point at a particular listWidget on the form?
If you want a pointer to an already existing object you won't use new, you need to assign it the address of the existing object:
int pos = 0; //determine the right position
QListWidgetItem *listItem = ui->inc_st_list->item(pos);

is it possible to "update" a QTextCursor?

In a QTextEdit object, let's say I want to know the character's position under the mouse cursor.
I can write...
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
mycursor = this->textCursor();
qDebug() << "pos=" << mycursor.position();
}
... it works (the mouse position changes from 0 to the last index of the last character) but the mousePressEvent() method creates a new cursor every time an event occurs. It bothers me since I don't know the "cost" of such a creation.
So, why not create a cursor attribute and use it in mousePressEvent() ?
Something like :
class MyQTextEditObject : public QTextEdit {
Q_OBJECT
public:
// [...]
QTextCursor cursor;
}
MyQTextEditObject::MyQTextEditObject(QWidget* parent) : QTextEdit(parent) {
// [...]
this->cursor = this->textCursor();
}
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
qDebug() << "pos=" << this->cursor.position();
}
But the position doesn't change anymore, as if it was fixed. So, is there a way to somehow update the cursor ? Or is the cost of repeated creation of a QTextCursor insignificant ?
update : writing something like...
mycursor= this->cursorForPosition(mouse_event->pos());
... creates a new cursor and seems to be the equivalent to :
mycursor= this->textCursor();
In your first example, instead of
mycursor = this->textCursor();
qDebug() << "pos=" << mycursor.position();
why do not you call it directly as?
qDebug() << "pos=" << this->textCursor().position();
Because in python
self.textCursor().position()
works.
Also, I am not sure but in your second example maybe you need to set the "cursor" as "textCursor" again with setTextCursor().
void MyQTextEditObject::mousePressEvent(QMouseEvent* mouse_event) {
this->setTextCursor(cursor)
qDebug() << "pos=" << this->cursor.position();
}

Qt - Updating in the function called by QSignalMapper

I have a QTreeWidget in which each of its items has a QComboBox in a column. I have connected it to a slot with a QSignalMapper, and I'm successfully retrieving both the item and the index in the combobox when it is triggered. I did it like this:
foreach(Workplace *wp, allWorkplaces){
QTreeWidgetItem *workplaceItem = new QTreeWidgetItem;
workplaceItem->setText(0, wp->workplaceName());
workplaceItem->setText(1, wp->workplaceDescription());
myWorkplaceUi->treeWidget->addTopLevelItem(workplaceItem);
QComboBox *combo = new QComboBox();
combo->addItems(allShiftModels);
combo->setAutoFillBackground(true);
ShiftModel *shiftModel = qobject_cast<ShiftModel *>(wp->usedShiftModel);
myWorkplaceUi->treeWidget->setItemWidget(workplaceItem,2, combo);
if(shiftModel && !shiftModel->shiftModelName().isEmpty()){
qDebug()<<"after the cast: "<< shiftModel->shiftModelName();
combo->setCurrentIndex(combo->findText(shiftModel->shiftModelName(), Qt::MatchExactly));
}else{
combo->setCurrentIndex(combo->findText("None", Qt::MatchExactly));
}
connect(combo, SIGNAL(currentIndexChanged(int)), signalMapper, SLOT(map()));
signalMapper->setMapping(combo, QString("%1").arg(wp->workplaceName()));
}
connect(signalMapper, SIGNAL(mapped(const QString &)),this, SLOT(changed(const QString &)));
My objective is, after retrieving both the Workplace and the ShiftModel, to update them in the instances of my already created Workplaces. So, basically, I try to find the Workplace and the ShiftModel that were selected, because depending on the ShiftModel selected, I will change a pointer to the ShiftModel in the Workplace class:
class Workplace : public QObject
{
Q_OBJECT
public:
(...)
ShiftModel *usedShiftModel;
(...)
}
And the changed slot:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
QString wp = position;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
QString sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
}else{
qDebug() << "cast failed!";
return;
}
}
}
}
So, my problem with this is, when I click one of the comboboxes, successfully retrieve both the item and index selected, but then, when I try to traverse them with the two foreach loops in the slot, it does not work as I expected. I hoped that every time I clicked on an index in one of the comboboxes, this would be called, and it is. Although, for some reason, the method I'm using to match what the user selected with what was already instatiated doesn't work.
Also, it looks like it only hits both the 1st workplace on the allWorkplaces list as well as the 1st shiftmodel on the ShiftModels list, and this is my problem.
If anyone knows how to fix this or has any ideas to share, please let me know. Thank you.
The problem is this:
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
If either the workplace name does not match, or the shift model name does not match, the relation between the work place and its currently linked shift model is removed and your function returns.
I could restructure the two for-loops for you, but there is an easier and less error-prone way:
Note: I marked some code paths with "TODO" which I skipped due to lack of time. You should be able to figure them out yourself though.
// Set up hashes for quick lookup
QHash< QString, Workplace* > workplaceHash;
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
foreach( Workplace* workplace, allWorkplaces )
{
workplaceHash.insert( workplace, workplace->workplaceName() );
}
// TODO: Do a similar thing for the shift models here
// Find the selected workplace
if( !workplaceHash.contains( position ) )
{
// TODO: Error handling (An unknown/No workplace was selected)
return;
}
// else: A valid workplace was selected
Workplace* selectedWorkplace = workplaceHash.value( position );
// TODO: Retrieve the name of the shift model (stored in variable sm)
// Find the selected shiftmodel
if( !shiftplaceHash.contains( sm ) )
{
// No shift model was selected
selectedWorkplace->usedShiftModel= 0;
return;
}
// Else: Both work place and shift model were selected
Shiftplace* selectedShiftModel = shiftplaceHash.value( sm );
selectedWorkplace->usedShiftModel = selectedShiftModel;
A few ideas for refactoring:
You could maybe do the creation of the hashes outside of this method and store them in member variables. Just make sure that the hash is always updated when a workplace is added or removed.
You could make spotting errors easier by extracting parts of the code into separate methods, e.g. QString getSelectedShiftModelName() etc.
So, In the end I figured out my loops were really messed up... This is working now:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
qDebug() << allWorkplaces.size() << " workplaces";
qDebug() << allShiftModels.size() << " ShiftModels";
QString wp = position;
QString sm;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
}else{
qDebug() << "cast failed!";
return;
}
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
qDebug() << "found match!: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = shiftmodel;
return;
}else if(workplace->workplaceName()==wp && sm=="None"){
qDebug() << "clear match: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = 0;
return;
}
}
}
}