crash after QGraphicsProxyWidget is removed from a scene - qt

I have a simple scene with a QGraphicsProxyWidget added to it ( and some widget set on it ).
If I try removing it from the scene, like so:
if ( widget )
{
parentScene->removeItem( m_widget ); // m_widget is a QGraphicsProxyWidget
delete m_widget; // I also tried m_widget->deleteLater() here - same result
m_widget = NULL;
}
I get an instant crash - no descriptive callstack though that would tell me what's wrong ( some windows related calls in the callstack, and that's all ).
I'm using version 5.1.1 of the Qt library, I've searched the manual and the net for an instruction how to actually remove such a widget, and it's not documented at all, so I'm not sure if I'm doing something wrong there, or is there a bug in the library.
I'd appreciate any help.
Cheers,
Piotr

Check your if statement: It says widget when you probably meant m_widget. If widget is non-null and m_widget is null, your program will crash, since the calls to removeItem/delete will attempt to reference a null pointer.

I found the problem - seemingly unrelated call to 'prepareGeometryChange' was to blame here.
My widgets were changing size ( I didn't mention it, 'cause it seemed irrelevant at the time ). However I was calling the aforementioned method AFTER I made the change to the geometry, not before, as the manual instructs.
Apparently that method is very important in order to keep the scene's internal data in order, and due to my calling it incorrectly, it was leaving some invalid references to the deleted items.

Related

Qt - signal for when QListWidget row is edited?

I am working in Qt4.7, and I have a QListWidget in my dialog. I have a QString that needs to match the current text in the row of this widget (the individual rows are editable). Looking at the signals associated with QListWidget, there seem to be signals for when a different index is selected but none for when the text of a the currently selected row changes. I thought currentTextChanged(QString) would do it, but it didn't. I also thought to try to connect each individual row to something, but QListWidgetItem doesn't have any built-in signals. Does anyone know of a way to do this? Thanks!
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, changing colors, checking boxes, and anything else that "changes" the item! Predelnik pointed that out in his answer. Some people have tried to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
I guess you need to look into the following signal:
void QListWidget::itemChanged(QListWidgetItem * item)
But be careful because it's being sent every time some property of item changed, not only text. I remember when we ran into the problem once when we changed item colors and got tons of false positive slots called because of that. If you need more fine tuning I guess it's better to write model/view classes yourself and not rely on QListWidget.

Use an QObject found by it's QObjectName

I have a little problem in my program. I have a config file put in settings. I pull from it the names of the object I need to be checked (these are QCheckBox).
I have this piece of code (It compiles and runs but when it's at "cBox->setChecked" it just crash):
void Preproc::on_tBtnManual_toggled(bool checked){
if(checked){
ui->tBtnManual->setText("Systematic");
}else{
ui->tBtnManual->setText("Manual");
settings.beginGroup("Preprocessing");
QStringList keys = settings.childKeys();
foreach(QString configParam,keys){
QCheckBox *cBox = ui->gridLayout->findChild<QCheckBox *>(configParam);
cBox->setChecked(settings.value(configParam).toBool());
}
}
}
I have tried to put ui->cBox->... put it says that cBox is not a child of ui.
If I qDebug(cBox) I have a QObject(0x0) so nothing !
I'm a little new to Qt so maybe it's a simple thing.
Thanks and have a nice day :)
Are you sure that an object is found?
I don't think so (different name? wrong layout?). cBox is 0x0 when nothing is found.
However put a
if (cBox)
before
cBox->setChecked(settings.value(configParam).toBool());
and it will not crash anymore when it doesn't find an object by name.
are you sure the name (content of configParam) is correct?
you can try the search from QApplication
QApplication::instance()->findChild<QCheckBox *>(configParam);
the findChild method performs a recursive search, if the object exists in the hirachie, it will be found. if the object is not found, it could be:
the object does not exist
the object has another name
the object or one of its ancestors has no (NULL) parent
can you post the part of the .ui file with the check box? it would be helpful.

QGraphicsScene::clear() clearing scene but not the view

I use a custom class (Configuration) derived from QGraphicsItem and I add its objects to a QGraphicsScene, which is then displayed in a QGraphicsView. Usual stuff. What Im doing exactly is drawing a tree, in multiple steps, one level a step, each node beeing my custom QGraphicsItem.
Here a screenshot. The tree happens to be sequential in the simple case.
I first draw the root node. The signal that triggers that is fired after the user entered a string.
void MainWindow::drawRootSlot(ConfigTreeBuilder & builder)//this is a slot
{
c_scene->clear(); //the clear cause headache. i'll expain
Configuration* conf = new Configuration(builder.getNodesX(), builder.getNodesY(),builder.getNodesConfig());
//code
c_scene->addItem(conf);
//code
}
Each subsequent Configuration is draw inside another slot.
void MainWindow::configTreeSlot(ConfigTreeBuilder & builder) //SLOT!!!
{
while(builder.chooseNextNode()) {
Configuration* conf = new Configuration(builder.getNodesX(), builder.getNodesY(), builder.getNodesConfig());
//code, while loop
QGraphicsLineItem *edge = c_scene->addLine(QLineF(*(parentsPoint), conf->getLeftOrigin()));
edge->setZValue(-1); //below the Configuration item
c_scene->addItem(conf);
}
}
All works fine when done for the first time. When I enter a new string, resetting the tree, dark magic happens. What I expected to it do is: call drawRootSlot(), deleting the whole tree (c_scene->clear()), draw a new root node. And, if I put a debugger breakpoint inside drawRootSlot() this is exactly what happens! But when I run it (without breakpoints), what I get is this:
The previous tree got mangled, but not deleted. The scene gets indeed cleared of its items (printed that) but the view does not reflect that. But again, when I put a breakpoint inside drawRootSlot() thhe view and the scene are in sync.
I tried to delete the scene object, and instaciate a new one instead of calling c_scene->clear(), to guarantee it empty. Then the changes are reflected on the view (the first time drawing always works).
So, I have no idea what to deduce from these symptoms. It works as expected with a breakpoint or with a freshh QGraphicsScene object. It does not when just using c_scene->clear(). One couldsay I just messed up the parent-object/child-object relation, but clear() does remove items from the view... I tried calling it right after c_scene->addItem().
What is this sorrcery? One that makes me believe I'm not actually stupid?
EDIT: Whats interesting and may be a hint to the real problem, is that when c_scene->clear() is called, the edges of the tree, which are normal QGraphicsLineItems, are indeed deleted in all cases (breakpoint or not). Something to do with them not beeing custom?
Ok, calling QGraphicsView::viewport().update() after QGraphicsScene::clear() solved my problems.
But does anyone have an explanaition to the behavior described above?
EDIT: Upon doing doing something else I stumbled upon the actual core of the problem: I messed up the boundingRect() of my GraphicItems, so it was below the visble item, touching only its lower edge (which got deleted, as seen on the screenshot). So now no calls to any update() methods are neccesary.
I think when you call this fitInView() for the graphicsview it cleans up the view from any artifacts that remain from a previous scene.
You can clear both scene and Graphics View
scene->clear();
ui->graphicsView->items().clear();
graphicsView = name of your graphics view
This code will delete both scene and graphics view

Segmentation fault in Qt application framework

this generates a segmentation fault becuase of "QColor colorMap[9]";. If I remove colorMap the segmentation fault goes away. If I put it back. It comes back. If I do a clean all then build all, it goes away. If I increase its arraysize it comes back. On the other hand if I reduce it it doesnt come back. I tired adding this array to another project and
What could be happening. I am really curious to know. I have removed everything else in that class. This widget subclassed is used to promote a widget in a QMainWindow.
class LevelIndicator : public QWidget
{
public:
LevelIndicator(QWidget * parent);
void paintEvent(QPaintEvent * event );
float percent;
QColor colorMap[9];
int NUM_GRADS;
};
the error happens inside ui_mainwindow.h at one of these lines:
hpaFwdPwrLvl->setObjectName(QString::fromUtf8("hpaFwdPwrLvl"));
verticalLayout->addWidget(hpaFwdPwrLvl);
I know i am not providing much but I will give alink to the app. Im trying to see if anyone has a quick answer for this.
If I do a clean all then build all, it goes away.
This makes it sound as though your build system isn't recognizing a dependency and that a change to that class definition isn't triggering a rebuild of something that should be recompiled when the definition changes.
Make sure class LevelIndicator is defined in exactly one place (generally that would be a header file that gets included by whatever modules need to use a LevelIndicator object). Also make sure that any global/static instances of LevelIndicator objects are following the one definition rule.
Firstly it might not be QColor, that may simply be changing the memory layout enough that a buffer overrun somewhere else triggers a segfault - try a different size QColor ..[1] for example.
Can QColor be used as an array like this, does it have the correct default ctor?

QPlainTextEdit segmentation fault

I have some Qt application with QPlainTextEdit in Tab widget. When try to make a pointer on it
QPlainTextEdit *w = (QPlainTextEdit*)ui->tabWidget->widget(0)
and call a document() method
w->document()
I get a segfault.
But if i call document directly, e.g. ui->mainEdit->document(), then everything works fine.
Can anybody explain me why it happens?
You want to do:
QPlainTextEdit *w = ui->mainEdit;
Then w->document() will return what you want. You are getting the segmentation fault because when you cast ui->tabWidget->widget(0); gives a pointer to a tab page object. When you cast this to QPlainTextEdit* are telling your program to treat a part of memory that does not represent a QPlainTextEdit as a QPlainTextEdit. This causes trouble at the time that you call w->document() because that is in the memory location that it tries to access is not what it would expect from memory which belongs to QPlainTextEdit.
i'm almost sure, that ui->tabWidget->widget(0) return container widget inside of tabWidget. Try qDebug() << ui->tabWidget->widget(0)->metaObject()->className() and see what is printed. It's probably just "QWidget" not "QPlainTextEdit". Your edit is inside of layout of this widget
You can use qobject_cast to make sure that it returns the right type.
QPlainTextEdit *w = qobject_cast<QPlainTextEdit*>(ui->tabWidget->widget(0));
if (w)
{
...
}
It'll return 0 if the type is not of QPlainTextEdit*.
As stated, widget(0) is probably not returning what you wanted - and probably contains a container or some other item, and is probably not the way you want to be accessing your widgets unless there is no other way.

Resources