PDA

View Full Version : segfault with QMessageBox in QAbstractItemModel::hasChildren



eric.frederich
27th October 2015, 13:48
It appears if I display something inside the implementation of hasChildren on a QAbstractItemModel I get a segfault.
This is easily reproducible with the simpletreemodel example included in the source tarball.
Simply add the following method to treemodel.cpp (and corresponding method to the header as well).
You can click through the first round of messages. After that when you mouse over one of the tree items it will give you another prompt. After clicking it you'll segfault.



#include <string>
#include <sstream>

// ...

bool TreeModel::hasChildren(const QModelIndex &parent) const
{
std::ostringstream oss;
if (!parent.isValid()) {
oss << "hasChildren root" << std::endl;
} else {
oss << "hasChildren (" << parent.row() << "," << parent.column() << ")" << std::endl;
}
oss << QAbstractItemModel::hasChildren(parent) << std::endl;

QMessageBox::information((QWidget*)QAbstractItemMo del::parent(), "TITLE", QString::fromStdString(oss.str()));
return QAbstractItemModel::hasChildren(parent);

}

Since I'm sure it will be asked, let me explain why I'm doing this.
I have an application which connects to a server but can also work off-line.
While connected a user can drag an item from the server to their local cache.
This does a shallow copy.
If the user then closes and re-opens the application they will see their local copy of that top level item and 1 level of items under it.
Expanding nodes any further than what the client has cached locally will cause a login dialog to appear to log into the server.

What should I do?
I'm open to suggestions.

anda_skoa
27th October 2015, 15:15
The model index might have become invalid.

Basically model indexes are only considere valid as long as the model doesn't change or something else has happend, e.g. events have been processed.
The latter happens inside the message box.

You could try storing the index in a QPersitantModelIndex before you go into the nested event loop.

Ideally of course the model wouldn't use a message box with a nested event loop at all.

Cheers,
_

eric.frederich
27th October 2015, 18:08
Thanks for the reply.

I don't think a QPersistentModelIndex helps here.
There is only a single line after the QMessageBox where I call...

return QAbstractItemModel::hasChildren(parent);
If I were to store the result of this call as a boolean variable at the top of the method and just return it, then I'm not using the index at all after the QMessageBox and it still segfaults.



bool TreeModel::hasChildren(const QModelIndex &parent) const
{
bool ret = QAbstractItemModel::hasChildren(parent);
QPersistentModelIndex tmp = QPersistentModelIndex(parent);
std::ostringstream oss;
if (!parent.isValid()) {
oss << "hasChildren root" << std::endl;
} else {
oss << "hasChildren (" << parent.row() << "," << parent.column() << ")" << std::endl;
}
oss << QAbstractItemModel::hasChildren(parent) << std::endl;

QMessageBox::information((QWidget*)QAbstractItemMo del::parent(), "TITLE", QString::fromStdString(oss.str()));
return ret;
}

anda_skoa
27th October 2015, 18:24
Hmm, what does the backtrace say where it crashes?

Is the parent really a QWidget? A C style cast is always a chance for trouble.

Cheers,
_

eric.frederich
27th October 2015, 20:19
Here is the backtace I got from Qt Creator.
I changed both the index and parent methods to return QPersistentModelIndex and I still get the error.
Again, this is just a simple example from the source tar files. Should be easy to re-produce.
I changed the c style cast to a qobject_cast<QWidget*>() call and still segfaults.


0 QTransform :: type() const 0x7ffff7472030
1 QRasterPaintEngine :: drawImage(QPointF const&, QImage const&) 0x7ffff748e288
2 QRasterPaintEngine :: drawPixmap(QPointF const&, QPixmap const&) 0x7ffff7495efe
3 QPainter :: drawPixmap(QPointF const&, QPixmap const&) 0x7ffff741cafb
4 QGtkPainter :: paintFlatBox(_GtkWidget *, const char *, QRect const&, GtkStateType, GtkShadowType, _GtkStyle *, QString const&) 0x7ffff7657183
5 QGtkStyle :: drawPrimitive(QStyle :: PrimitiveElement, QStyleOption const *, QPainter *, QWidget const *) const 0x7ffff7643e96
6 QTreeView :: drawRow(QPainter *, QStyleOptionViewItem const&, QModelIndex const&) const 0x7ffff782302c
7 QTreeView :: drawTree(QPainter *, QRegion const&) const 0x7ffff78268eb
8 QTreeView :: paintEvent(QPaintEvent *) 0x7ffff782a303
9 QWidget :: event(QEvent *) 0x7ffff731a506
10 QFrame :: event(QEvent *) 0x7ffff76d0fae
11 QAbstractItemView :: viewportEvent(QEvent *) 0x7ffff77ed093
12 QTreeView :: viewportEvent(QEvent *) 0x7ffff782e210
13 QCoreApplicationPrivate :: sendThroughObjectEventFilters(QObject *, QEvent *) 0x7ffff6d982d6
14 QApplicationPrivate :: notify_helper(QObject *, QEvent *) 0x7ffff72c7b9c
15 QApplication :: notify(QObject *, QEvent *) 0x7ffff72ce555
16 QCoreApplication :: notifyInternal(QObject *, QEvent *) 0x7ffff6d9816d
17 QWidgetPrivate :: drawWidget(QPaintDevice *, QRegion const&, QPoint const&, int, QPainter *, QWidgetBackingStore *) 0x7ffff7314869
18 QWidgetBackingStore :: sync() 0x7ffff74df5ca
19 QWidgetPrivate :: syncBackingStore() 0x7ffff73091d0
20 QWidget :: event(QEvent *) 0x7ffff731a296
21 QFrame :: event(QEvent *) 0x7ffff76d0fae
22 QAbstractScrollArea :: event(QEvent *) 0x7ffff7754333
23 QAbstractItemView :: event(QEvent *) 0x7ffff77ecd2b
24 QApplicationPrivate :: notify_helper(QObject *, QEvent *) 0x7ffff72c7bbc
25 QApplication :: notify(QObject *, QEvent *) 0x7ffff72ce555
26 QCoreApplication :: notifyInternal(QObject *, QEvent *) 0x7ffff6d9816d
27 QCoreApplicationPrivate :: sendPostedEvents(QObject *, int, QThreadData *) 0x7ffff6d9b255
28 postEventSourceDispatch(_GSource *, int ( *)(void *), void *) 0x7ffff6dc73f3
29 g_main_context_dispatch 0x7ffff592c9ba
30 g_main_context_iterate.isra 0x7ffff592cd08
31 g_main_context_iteration 0x7ffff592cdbc
32 QEventDispatcherGlib :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6dc6c75
33 QGuiEventDispatcherGlib :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff7369ab6
34 QEventLoop :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6d96caf
35 QEventLoop :: exec(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6d96ffd
36 QDialog :: exec() 0x7ffff778f8dc
37 showNewMessageBox(QWidget *, QMessageBox :: Icon, QString const&, QString const&, QFlags<QMessageBox :: StandardButton>, QMessageBox :: StandardButton) 0x7ffff77b0030
38 QMessageBox :: information(QWidget *, QString const&, QString const&, QFlags<QMessageBox :: StandardButton>, QMessageBox :: StandardButton) 0x7ffff77b011f
39 TreeModel :: hasChildren treemodel.cpp 235 0x404cad
40 QTreeViewPrivate :: hasVisibleChildren(QModelIndex const&) const 0x7ffff78260e2
41 QTreeViewPrivate :: itemDecorationRect(QModelIndex const&) const 0x7ffff78261e4
42 QTreeViewPrivate :: itemDecorationAt(QPoint const&) const 0x7ffff7826474
43 QTreeView :: viewportEvent(QEvent *) 0x7ffff782e254
44 QCoreApplicationPrivate :: sendThroughObjectEventFilters(QObject *, QEvent *) 0x7ffff6d982d6
45 QApplicationPrivate :: notify_helper(QObject *, QEvent *) 0x7ffff72c7b9c
46 QApplication :: notify(QObject *, QEvent *) 0x7ffff72ce8a3
47 QCoreApplication :: notifyInternal(QObject *, QEvent *) 0x7ffff6d9816d
48 QApplicationPrivate :: sendMouseEvent(QWidget *, QMouseEvent *, QWidget *, QWidget *, QWidget * *, QPointer<QWidget>&, bool) 0x7ffff72cddd7
49 QETWidget :: translateMouseEvent(_XEvent const *) 0x7ffff73434bb
50 QApplication :: x11ProcessEvent(_XEvent *) 0x7ffff7341f2c
51 x11EventSourceDispatch(_GSource *, int ( *)(void *), void *) 0x7ffff7369934
52 g_main_context_dispatch 0x7ffff592c9ba
53 g_main_context_iterate.isra 0x7ffff592cd08
54 g_main_context_iteration 0x7ffff592cdbc
55 QEventDispatcherGlib :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6dc6c75
56 QGuiEventDispatcherGlib :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff7369ab6
57 QEventLoop :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6d96caf
58 QEventLoop :: exec(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6d96ffd
59 QCoreApplication :: exec() 0x7ffff6d9c519
60 main main.cpp 67 0x406602

Added after 33 minutes:

Here is a complete self-contained example.
Once you start mousing over the items in the tree you get pop-ups and you segfault.


#include <QtGui>
#include <iostream>

class MyModel : public QStandardItemModel {

public:
bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
void setDialogParent(QWidget* dp) { dialogParent = dp; }
private:
QWidget* dialogParent;
};

bool MyModel::hasChildren(const QModelIndex &parent) const {
bool ret = QStandardItemModel::hasChildren(parent);
std::cout << "returning " << ret << std::endl;
QMessageBox::information(dialogParent, "Hey", "hasChildren was called");
return ret;
}

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

MyModel model;

QList<QStandardItem*> list;
list << new QStandardItem("One");
list << new QStandardItem("Two");
list << new QStandardItem("Three");
model.invisibleRootItem()->appendRows(list);

QTreeView view;
view.setModel(&model);
model.setDialogParent(&view);
view.setWindowTitle(QObject::tr("Simple Tree Model"));
view.show();
return app.exec();
}

eric.frederich
28th October 2015, 13:49
Qt5 standalone example:


QT += core
QT += gui
QT += widgets

TARGET = example
CONFIG += c++11

TEMPLATE = app

SOURCES += main.cpp


#include <QApplication>
#include <QMessageBox>
#include <QPersistentModelIndex>
#include <QStandardItemModel>
#include <QTreeView>

class MyModel : public QStandardItemModel {
bool hasChildren(const QModelIndex &parent) const override {
auto ret = QStandardItemModel::hasChildren(parent);
QMessageBox::information(NULL, "Title", "Message");
return ret;
}
// Override these to make sure any indices returned from this model are persistent
// Doesn't make a differents; still segfaults anyway
QModelIndex parent(const QModelIndex &child) const override {
return QPersistentModelIndex(QStandardItemModel::parent(c hild));
}
QModelIndex index(int row, int column, const QModelIndex &parent) const override {
return QPersistentModelIndex(QStandardItemModel::index(ro w, column, parent));
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView tv;
MyModel model;
QList<QStandardItem*> l;
l << new QStandardItem("One");
l << new QStandardItem("Two");
l << new QStandardItem("Three");
model.invisibleRootItem()->appendRows(l);
tv.setModel(&model);
tv.show();
return a.exec();
}

anda_skoa
28th October 2015, 14:57
Both examples work for me.

"work" as there is an almost uninterrrupted stream of annoying message boxes.

I do get these warnings though


QWidget::repaint: Recursive repaint detected

very likely caused by the nested event loop and the focus change to the modal message box, etc.

Since your backtrace shows the Gtk style plugin, have you tried with a different style plugin?

Cheers,
_

eric.frederich
28th October 2015, 15:55
Crashes with all 3 available styles on my system. GTK+, Fusion and Windows.
This trace is from a different machine running Qt5.5
I've tested with Qt4.8 on RHEL7 and with Qt5.5 on Ubuntu 14.04 (Linux Mint 17.2).

What system were you running on?



0 QRasterPaintEngine :: brushOriginChanged() 0x7ffff7092178
1 QPainter :: setBrushOrigin(QPointF const&) 0x7ffff70a7b39
2 QTreeView :: drawBranches(QPainter *, QRect const&, QModelIndex const&) const 0x7ffff7938614
3 QTreeView :: drawRow(QPainter *, QStyleOptionViewItem const&, QModelIndex const&) const 0x7ffff793ca10
4 QTreeView :: drawTree(QPainter *, QRegion const&) const 0x7ffff793fd1d
5 QTreeView :: paintEvent(QPaintEvent *) 0x7ffff7946a46
6 QWidget :: event(QEvent *) 0x7ffff76f11d8
7 QFrame :: event(QEvent *) 0x7ffff77ec19e
8 QAbstractItemView :: viewportEvent(QEvent *) 0x7ffff790925b
9 QTreeView :: viewportEvent(QEvent *) 0x7ffff7947af0
10 QCoreApplicationPrivate :: sendThroughObjectEventFilters(QObject *, QEvent *) 0x7ffff6895496
11 QApplicationPrivate :: notify_helper(QObject *, QEvent *) 0x7ffff76b04ac
12 QApplication :: notify(QObject *, QEvent *) 0x7ffff76b5630
13 QCoreApplication :: notifyInternal(QObject *, QEvent *) 0x7ffff6895663
14 QWidgetPrivate :: sendPaintEvent(QRegion const&) 0x7ffff76ea499
15 QWidgetPrivate :: drawWidget(QPaintDevice *, QRegion const&, QPoint const&, int, QPainter *, QWidgetBackingStore *) 0x7ffff76eaaaf
16 QWidgetPrivate :: paintSiblingsRecursive(QPaintDevice *, QList<QObject *> const&, int, QRegion const&, QPoint const&, int, QPainter *, QWidgetBackingStore *) 0x7ffff76eb7e4
17 QWidgetPrivate :: paintSiblingsRecursive(QPaintDevice *, QList<QObject *> const&, int, QRegion const&, QPoint const&, int, QPainter *, QWidgetBackingStore *) 0x7ffff76eb63a
18 QWidgetPrivate :: drawWidget(QPaintDevice *, QRegion const&, QPoint const&, int, QPainter *, QWidgetBackingStore *) 0x7ffff76ea652
19 ?? 0x7ffff76bd3d6
20 ?? 0x7ffff76bdc5e
21 ?? 0x7ffff770dd1b
22 QApplicationPrivate :: notify_helper(QObject *, QEvent *) 0x7ffff76b04cc
23 QApplication :: notify(QObject *, QEvent *) 0x7ffff76b5630
24 QCoreApplication :: notifyInternal(QObject *, QEvent *) 0x7ffff6895663
25 QGuiApplicationPrivate :: processExposeEvent(QWindowSystemInterfacePrivate :: ExposeEvent *) 0x7ffff6e39210
26 QGuiApplicationPrivate :: processWindowSystemEvent(QWindowSystemInterfacePri vate :: WindowSystemEvent *) 0x7ffff6e39e9d
27 QWindowSystemInterface :: sendWindowSystemEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6e1efbf
28 ?? 0x7fffee0efde0
29 g_main_context_dispatch 0x7ffff51ebe04
30 ?? 0x7ffff51ec048
31 g_main_context_iteration 0x7ffff51ec0ec
32 QEventDispatcherGlib :: processEvents(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff68eaa87
33 QEventLoop :: exec(QFlags<QEventLoop :: ProcessEventsFlag>) 0x7ffff6893212
34 QCoreApplication :: exec() 0x7ffff689ad3d
35 main main.cpp 39 0x403121


Added after 25 minutes:

Actually, I'm working from home today in a very convoluted way (from windows I VNC to a RHEL6 machine, then ssh with X forwarding to the RHEL7 machine).
In this convoluted set up I get the "QWidget::repaint: Recursive repaint detected" printout but no segfault.

anda_skoa
28th October 2015, 16:31
I am on Debian/Unstable, Qt 5.4.2, not sure which style.

Anyway, mostly an academic problem right? You wouldn't use such a messagebox anyway, even without crash it would make the application unusable.

Cheers,
_