PDA

View Full Version : QQuickView redraw: setSource() 2nd time leads to Qt-Assert



sedi
6th August 2016, 00:35
Hi,
I have a QuickView interface where the user can edit data.

A reset button (inside the interface) sets the underlying data (an interface object that is set as a context property) back to model data by calling a slot in that very interface object. Unfortunately, the qml content is not being visibly updated.

I'd be happy with just rebuilding the QML content from scratch by setting the QQuickview's source anew after resetting my data, like here:


void ControllerClass::redrawIncEditView()
{
Q_ASSERT(m_quickView);
auto url = m_quickView->source();
// m_quickView->hide();
m_quickView->setSource(url); //<<<<< HERE we have the assert you see below
// m_quickView->show();
}

But calling this leads to a crash in Qt:

ASSERT: "context() && engine()" in file qml\qqmlboundsignal.cpp, line 183

I have no idea why the crash occurs. Ive seen very similar / same error messages on Google, but in quite different contexts, as far as I can see.

I've also tried calling the following methods of QQuickView, but they don't show any effect (probably they just cover the graphical aspects):


m_quickView->update();
m_quickView->requestUpdate();
m_quickView->renderTarget();


Interesting update:

The above approach works when started by a single shot timer. Therefore I have to subclass QQuickView, adding a "redraw"-method that takes no argument (to make it usable with QTimer::singleShot):

class MyQQuickView: public QQuickView {
Q_OBJECT
public slots:
void redraw() { this->setSource(this->source()); }
};
and call that in this way:


void ControllerClass::redrawIncEditView()
{
Q_ASSERT(m_quickView);
QTimer::singleShot(1,m_quickView,SLOT(redraw()));
}


Any idea about what is behind the crash? Is it a threading problem?
Are there better / more elegant ways to force a QML redraw?
Is that hack somewhat safe in the first place? I can use it with 1 ms here - but could this be different on a phone?

anda_skoa
6th August 2016, 10:32
A reset button (inside the interface) sets the underlying data (an interface object that is set as a context property) back to model data by calling a slot in that very interface object. Unfortunately, the qml content is not being visibly updated.

The editing probably replaced the property binding you originally had.

Can you show the QML code?



But calling this leads to a crash in Qt:

Very strange.
How about


const QUrl source = view->source();
view->engine()->clearComponentCache();
view->setSource(source);




Any idea about what is behind the crash? Is it a threading problem?

Unlikely. The trigger comes from within the main UI thread, the view is on that thread.



Are there better / more elegant ways to force a QML redraw?

You don't need to force redraw, you want the displayed data to be updated.
Just forcing redraw of the old data doesn't get you anywhere.



Is that hack somewhat safe in the first place? I can use it with 1 ms here - but could this be different on a phone?
No, that should be ok but you might want to look into solving the actual problem :)

Cheers,
_

sedi
6th August 2016, 21:12
Hi and thanks for your time and attention,


view->engine()->clearComponentCache();
is an interesting idea! I had not tried that before, but unfortunately it doesn't help, too (same crash).

As to the basic structure of what I am doing here: I have different kinds of incidents in my model which all derive from an "Incident" base class. For interaction with qml I cache this in different interfaces which also derive from one "IncEditInterface", which covers the "Incident" base stuff.

On qml side I use a IncEdit.qml which allows the editing of the base class properties of the interface (most of them invisible by default, opening on demand). The additional properties of the derived classes are covered by a Loader that - depending on the actual class - sideloads additional interface elements by taking the source of a class-specific qml file.

Structure:
model:
incident (base)
--> incidentA (derived)
--> incidentB (derived)
--> incidentC (derived)

interface:
incEditInterface (base)
--> incEditInterfaceA(derived)
--> incEditInterfaceB (derived)
--> incEditInterfaceC (derived)

QML
incEdit.qml
{
[base class stuff]
Loader.source = "interfaceA.qml"
[additional base class stuff]
}

The actual qml code is this one (destilled for readability):


import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
[imports]

Flickable {
id: incEditFlickable
[Layout/Size stuff]
Item {
id: incEditBaseItem
[Layout/Size stuff]
property string pupilDisplayNameComposition: IncEditInterface.pupilFullName + " ("+ priv.nick + priv.gender + ")"
QtObject {
id: priv
property string nick: IncEditInterface.pupilNickName ? IncEditInterface.pupilNickName + ", " : ""
property string gender: IncEditInterface.pupilGender === Enums.MALE ? "m" : IncEditInterface.pupilGender === Enums.FEMALE ? "w" : "?"

}
// optionally overlaid dialog items ------------------------------------------------------------------------------------------------------------
Calendar {
z: 500
visible:false
id: datePickerBookedFor
[Layout/Size stuff]
onClicked: {
datePickerBookedFor.visible = false
dateButton.text = Qt.formatDate(date, "ddd dd.MM.yy")
IncEditInterface.bookedForDate = date
dateButton.shrinkToFitFromPixelSize(pupilNameSmall .font.pixelSize)
}
}

// incEditButtons -----------------------------------------------------------------------
RowLayout {
id: incEditButtonRow
[Layout/Size stuff]
CustomIconPushButton {
id: okButton
[...]
}
CustomIconPushButton {
id: adoptButton
[...]
}
CustomIconPushButton {
id: resetButton
[Layout/Size stuff]
text: "Reset"
onPushButtonClicked: {
IncEditInterface.resetEntries()
}
}
CustomIconPushButton {
id: deleteButton
[...]
}

}

// small version of incEdit header --------------------------------------------------------------------------------------------------------------
Rectangle {
id: smallTopItem
[Layout/Size stuff]
visible: true
Rectangle {
id: pupilNameSmallRect
[Layout/Size stuff]
Text {
id: pupilNameSmall
[Layout/Size stuff]
text: incEditBaseItem.pupilDisplayNameComposition
}
}
CustomIconPushButton {
id: showHideTitle1
visible: true
[Layout/Size stuff]
text: "s"
onPushButtonClicked: {
smallTopItem.visible = false;
largeTopItem.visible = true;
}
}
}
// large version of incEdit header --------------------------------------------------------------------------------------------------------------
Rectangle {
id: largeTopItem
[Layout/Size stuff]
visible: false
Rectangle {
id: pupilNameLargeRect
color: "transparent"
[Layout/Size stuff]
Image {
id: pupilPicture
[Layout/Size stuff]
source: IncEditInterface.pupilCustomPictureFileName === "" ? "" : IncEditInterface.paths.customPicturePath+IncEditIn terface.pupilCustomPictureFileName
fillMode: Image.PreserveAspectFit
}
Text {
id: pupilNameLarge
[Layout/Size stuff]
text: incEditBaseItem.pupilDisplayNameComposition
}
SetCombo {
Component.onCompleted: {
groupCombo.currentIndex = groupCombo.find(IncEditInterface.groupName)
}

id: groupCombo
enabled: !datePickerBookedFor.visible
[Layout/Size stuff]
model: IncEditInterface.groupNames
textRole: "text"
onCurrentTextChanged: {
IncEditInterface.groupName = groupCombo.currentText
}
}
CustomIconPushButton {
id: dateButton
[Layout/Size stuff]
text: Qt.formatDate(IncEditInterface.bookedForDate, "ddd dd.MM.yy")
onPushButtonClicked: {
datePickerBookedFor.visible = true
}
}
CustomIconPushButton {
id: showHideTitle2
[Layout/Size stuff]
text: "h"
onPushButtonClicked: {
smallTopItem.visible = true;
largeTopItem.visible = false;
}
}
} //pupilNameLargeRect
} //largeTopItem
Loader {
id: incEditLoader
[Layout/Size stuff]
source: IncEditInterface.loaderSourceUrl
}
SetTextAreaInput {
id: additionalInfoTextInput
[Layout/Size stuff]
title: "Bemerkungen:"
text: IncEditInterface.additionalInfo
onTextChanged: IncEditInterface.additionalInfo = text
}
} //FlickableBaseItem
}

anda_skoa
6th August 2016, 23:59
Is that last SetTextAreaInput one of those for which the data does not change?
I.e the one with id "additionalInfoTextInput"?

Can you show the code for that one? Is it "derived" from QtQuick TextArea?

Cheers,
_

sedi
20th August 2016, 17:49
Sorry for the delayed answer, I could not quite get to the project for private reasons in the last two weeks.
But I am happy to say that with the help of your answer I could resolve the issue.

I've made two stupid mistakes.

When I changed the interface classes properties, I did that by directly changing the private members, not by calling the setter. This way no signal was ever fired.What a splendidly stupid thing to do... :(
I did indeed have problems in my qml items. I destroyed bindings by using some JavaScript interaction. Now, in those cases, to connect with the interface, I use proxy properties which have a corresponding onChanged, that can do the necessary work.


Thank you very much for your time and concern - and for giving me the nudge to solve the actual problem and not to just work around its symptoms!