setModelData crashes program
-
In your case, you should call findChild on
editor
. Your QComboBox is in a layout inside a QWidget so you can't cast it like that directly.An alternative would be to create a custom widget which wraps the QComboBox and that you could use in the same manner.
-
Thank you. I redid it as recommended and now it looks like this:
#include "whatfixviewdelegate.h" WhatFixViewDelegate::WhatFixViewDelegate(QObject* parent) : QStyledItemDelegate(parent) { } QWidget* WhatFixViewDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(option); Q_UNUSED(index); QWidget* w = new QWidget( parent ); QComboBox* whatComboEditor = new QComboBox(w); whatComboEditor->setFrame(true); whatComboEditor->setStyleSheet("background-color:rgb(255,217,229);" ); whatComboEditor->setEditable(true); whatComboEditor->setMaximumWidth (100); whatComboEditor->setMinimumWidth (100); whatComboEditor->setMaximumHeight (30); whatComboEditor->setMinimumHeight (30); whatComboEditor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QVBoxLayout* whatLayout = new QVBoxLayout; whatLayout->addWidget (whatComboEditor, 0, Qt::AlignCenter); whatLayout->setMargin (2); w->setLayout (whatLayout); QSqlQuery queryLoadWhat("SELECT What FROM What_Table ORDER BY What asc", db); while(queryLoadWhat.next()) { QString whatItem = queryLoadWhat.value(0).toString(); whatComboEditor->addItem(whatItem); } return w; } void WhatFixViewDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { QComboBox* whatComboEditor = editor->findChild<QComboBox*>(); if(whatComboEditor) { whatComboEditor->addItem(index.model()->data(index, Qt::EditRole).toString()); whatComboEditor->setCurrentText(index.model()->data(index, Qt::EditRole).toString()); } } void WhatFixViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(index); editor->setGeometry(option.rect); } void WhatFixViewDelegate::setModelData(QWidget* editor, QAbstractItemModel* fixModel, const QModelIndex& index) const { QComboBox* whatComboEditor = editor->findChild<QComboBox*>(); if (whatComboEditor) { QString newWhatText; newWhatText = whatComboEditor->currentText ();//crashes qDebug() << "newWhatText: " << whatComboEditor->currentText (); fixModel->setData (index, newWhatText, Qt::EditRole); fixModel->submit (); } }
Now it doesn't crash, but when I add an item from the QComboBox, all the data from the line disappears like this:
When I reload the table all the data are back and the newly chosen item is also there (it was saved in the db). What can I add to prevent the data from disappearing? -
@JNBarchan said in setModelData crashes program:
And always check the return result of a
qobject_cast<>
, for safety, certainly while developing! :)In my nice (non-Qt) C# background, this kind of casting is built into the language. For
qobject_cast<>
we write:ComboBox cb = editor as CombBox;
where the
as
operator returnsnull
ifeditor
cannot be cast toComboBox
at runtime. We use this ifeditor
might not be aComboBox
.However, if
editor
must be/is intended to be aComboBox
, we write:ComboBox cb = (ComboBox) editor;
where the direct cast throws a runtime Exception if
editor
is not aComboBox
(i.e.as
would returnnull
). This saves a lot of code/accidents.It would be nice if Qt offered a "variant" of
qobject_cast<>
in C++ which checked and automatically threw an Exception if the return result is NULL/0....? -
@VRonin said in setModelData crashes program:
you just need to add
Q_ASSERT(whatComboEditor);
to have thatAh, OK. It's just that you have to remember to write that every time after each
qobject_cast<>
, it would be nice to have them combined in one statement. I suppose you would write:Q_ASSERT(whatComboEditor = qobject_cast<>)
but is
Q_ASSERT
code removed in non-debug code and/or does it evaluate its argument more than once (assuming it's a macro)?I would like a
qobject_assert_cast<>
:)EDIT: Yes, I see you cannot use
Q_ASSERT(whatComboEditor = qobject_cast<>)
in non-debug as the code gets removed. -
That's not specific to Q_ASSERT, it's the same for the standard C++ assert. Always test the result of a function and not the function itself.
-
@VRonin said in setModelData crashes program:
If you want to use it in code that should still be present in release mode you can use
Q_ASSUME
Um,
I don't think soare you sure: http://doc.qt.io/qt-5/qtglobal.html#Q_ASSUME states it's a compile-time hint, and you get "undefined behaviour" if the condition is false. We need a runtime check of the return result ofqobject_cast<>
.EDIT: Unless the docs are misleading, and the true implementation is via https://git.merproject.org/faenil/qtbase/commit/3b0ed624351441a2d7be45cf9582fd36955ae860 , which uses
Q_ASSERT_X
? -
Q_ASSUME
in debug mode is likeQ_ASSERT
in release mode, ifwhatComboEditor
is null the program would crash anyway so you can't do much worse.The undefined behaviour comes from when you use the return value:
if(Q_ASSUME(false))
is undefined behaviour. if it just wraps an operation you do not check anyway,Q_ASSUME
does nothing to your compiled binary in release mode -
@VRonin
That wasn't quite the point, though. I was talking about writing the single-line statement (if Qt won't offer aqboject_assert_cast<>
):Q_ASSERT(whatComboEditor = qobject_cast<>)
versus
Q_ASSUME(whatComboEditor = qobject_cast<>)
The point being that it does the assignment. So we do need the expression to evaluate, just we don't need/care to do the "assertion" part of testing the result in release build. This would not work for
Q_ASSERT
as (I believe) it's a macro which produces nothing (no code at all) in release build. That was when you suggestedQ_ASSUME
.