Logical value does not depend on actual values (M325) - qt

How should this warning be fixed?
Repeater {
model: n
SomeItem {
onValueChanged: if(index === 0) root.myreset()
}
}
(warning is on the === comparison)

I can't reproduce it on Qt Creator 7.0.1 on Windows using Desktop Qt 6.3.0 MinGW kit. According to this bug report QTCREATORBUG-25917 it has been fixed in Qt Creator 7.0.0. That bug report also tells that a workaround for this warning occuring with index property in a delegate (which is a special property exposed from model to delegate) is to specify the parent of the object it belongs to, i.e. model.index.
Repeater {
model: n
SomeItem {
onValueChanged: if(model.index === 0) root.myreset()
}
}

Related

Detection of event "property changed"

There is a property var1 declared in file1.qml. The property is visible in other qml files, but it is not possible to detect its event onChange.
file1.qml:
Item {
id: obj1Id
property int var1: 0
...
Component.onCompleted: {
var1=2
var1Changed() <<<<<<<<<<<< emit signal "changed"
}
onVar1Changed: {} <<<<<<<<<<<< signal is detected
...
}
file2.qml:
Item {
id: obj2Id
property int local_var: 0
...
Component.onCompleted: {
local_var = obj1Id.var1 <<<<<<<<<<<<<<<<< It is OK
}
onVar1Changed: {} <<<<<<<<<<<<<<<<< Error
obj1Id.onVar1Changed: {} <<<<<<<<<<<<<<<<< Error
...
}
Is it possible this approach or in file2.qml have to add property int local_var: obj1Id.var1 and then onLocal_varChanged: {}?
There are few moments in your code that could bring misunderstanding. Makes sense to figure out it first.
You wrote:
Component.onCompleted: {
var1=2
var1Changed() <<<<<<<<<<<< emit signal "changed"
}
but you do not need to manually emit that signal. QML will do it for you. So, actually nothing will change without that line.
You wrote
property int local_var: 0
...
Component.onCompleted: {
local_var = obj1Id.var1 <<<<<<<<<<<<<<<<< It is OK
}
this is totally valid code, but you need to understand that this is one-time assign and not a binding of a value obj1Id.var1 to local_var, e.g. if you change obj1Id.var1 -- do not expect local_var to be changed respectively.
To make a real binding of a value you may use Binding component or Qt.binding for creating property bindings from JavaScript or as you've proposed -- add property int local_var: obj1Id.var1.
In any case you need to have a valid reference to your obj1Id in your file2.qml.
If bindings approach doesn't fit you -- you can use Connections QML Type.
In your case this code should be placed inside file2.qml:
Connections {
target: obj1Id
onVar1Changed: console.log("obj1Id.var1 changed")
}

QML: Dynamic view re-ordering in original model

Implemented QML Dynamic View Ordering by Dragging View Items using this Qt tutorial: QML Dynamic View Ordering Tutorial. Original underlying model is QAbstractListModel descendant in our case. Model stores data in a QList<QObject*> objectList; field type. Works fine, however item ordering changed in proxy DelegateModel only.
How to change items order automatically in original underlying model as well for other C++ and QML consumers where order matters? Or I could we otherway access some resulted (sorted) List Model model from C++ somehow?
Thanks for any help!
In the QML Dynamic View Ordering Tutorial 3 example I've replaced visualModel.items.move() call with my ObjectListModel::move() method like this:
ObjectListModel : public QAbstractListModel:
void ObjectListModel::move(int from, int to)
{
if(0 <= from && from < count() && 0 <= to && to < count() && from != to) {
if(from == to - 1) // Allow item moving to the bottom
to = from++;
beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
objectList.move(from, to);
endMoveRows();
}
}
Delegate component:
DropArea {
anchors { fill: parent; }
onEntered: {
let from = drag.source.DelegateModel.itemsIndex
let to = mouseArea.DelegateModel.itemsIndex
objectListModel.move(from, to)
}
}
And above works perfectly for the ListView and ObjectListModel itself - I have checked: items (and therefore objects) are moved correctly, indexes are correct, C++ consumers works just fine and take new order into account correctly, etc.
However another consumer like MapItemView fails to use the model after beginMoveRows/endMoveRows calls: moved item disappeared on the map and other manipulations with an item crashes the app.
Map {
...
MapItemView {
model: objectListModel
delegate: SomeItemIndicator {
}
}
}
Reported QTBUG-81076 bug, which is confirmed.
Workaround:
Found workaround for now: created 2nd duplicate model which content will be replaced completely on every change in the 1st model on every add/delete/moving(reordering). Above works since beginResetModel/endResetModel works correctly for MapItemView. So MapItemView now utilizes only the 2nd model. So on every 1st model change this method is called for the 2nd model:
QObjectList ObjectListModel::swapObjectList(const QObjectList& newlist)
{
QObjectList oldlist(_objectList);
beginResetModel();
_objectList = newlist;
endResetModel();
return oldlist;
}

Double-Initialisation of values in QML

I have a strange problem, that is burried somewhere within a large project. So far I was not able to reproduce it in a MCVE, but as soon as I succeed, I will turn it in.
It is quite a simple missbehavior. Basically I have an QtObject with properties, that I set with initial values as such:
TestObj.qml
QtObject {
id: root
property int val1: { console.log('set val', root); return 42 }
Component.onCompleted: console.log('Constructed Object', this)
}
!!! With this example, I do not reproduce the error !!!
The output in my project would be now:
set val TestObj_QMLTYPE_44(0x33799fa8)
set val TestObj_QMLTYPE_44(0x33799fa8)
Constructed Object TestObj_QMLTYPE_44(0x33799fa8)
So, though the object is only created once, the initial property assignment is performed twice.
As I have no idea, where to look for the culprit, I can't produce a reproducable example, but maybe someone stumbled uppon the same situation already and found a solution.
A solution would be beneficial, as this issue results in multiple instantiations of some objects, that I can not destroy.
Created Bug-Report: maybe they find a way to solve this issue withouth hacky workarounds
The problem are circular references:
Circular references are resulting in a strange behavior when creating objects.
TestObj1.qml
import QtQuick 2.0
QtObject {
property Item paps
property int myVal: { console.log('myVal'); paps.val }
}
TestObj2.qml
import QtQuick 2.0
Item {
id: root
property int val: { console.log('set val', root); return 42 }
Component.onCompleted: console.log('Constructed Object')
TestObj1 {
id: to1
paps: root
}
}
Result:
qml: myVal
qml: set val TestObj_QMLTYPE_4(0x2c0bafb0)
qml: set val TestObj_QMLTYPE_4(0x2c0bafb0)
qml: Constructed Object
The probalbe cause for this is, that the statement {console.log('set val', root); return 42 } has not been processed, when it is allready assigned to myVal, therefore that statement is executed twice.
While this is no problem in normal situations, it might lead to problems, as long as we don't have dynamically created objects in those properties.
TestObj3.qml
import QtQuick 2.0
Item {
id: root
property QtObject obj: { console.log('set obj'); return objPrototype.createObject(root) }
Component.onCompleted: console.log('Constructed Object', obj)
TestObj4 {
id: to1
paps: root
}
Component {
id: objPrototype
QtObject {
id: op
Component.onCompleted: console.log('PropertyObject created', op)
}
}
}
TestObj4.qml
import QtQuick 2.0
QtObject {
property Item paps
property QtObject myObj: paps.obj
Component.onCompleted: console.log(myObj)
}
Result:
qml: set obj
qml: PropertyObject created QObject(0x2c124708)
qml: set obj
qml: PropertyObject created QObject(0x2c1246f8)
qml: Constructed Object QObject(0x2c1246f8)
qml: QObject(0x2c1246f8)
So you can see, the object is indeed created twice.
There are some workarounds:
Don't use circular references - but that would be booring.
Don't use dynamic object creation, as it always produces problems - I could just create the object, and either use property alias obj: myObjectID or property QtObject obj: myObjectID. This has the downside, that if the component is reusable, that the user can't replace that object or the object is still created, wasting memory.
Assign the property in Component.onCompleted if the property is still empty (not overwritten by the user of the reusable component). This has the downside, that the object is not available, uppon creation and a lot of Can't read property ... of null errors will appear. They will hopefully don't break the app though.
Create a nasty binding loop Write: property QtObject obj: (obj ? obj : objPrototype.createObject(root)). It will throw a warning, but c'mon. It's just a binding loop, that will be detected and broken.
Nothing nice in those workarounds, but maybe something usable.

How can I pass a QML object reference into Qt C++?

I am attempting to create something like jQuery's autocomplete as a widget in QML and Qt C++. Toward that end, I created a C++ AutoCompleteListener child of QObject and then register it with:
qmlRegisterType<AutoCompleteListener>(
"foo.AutoCompleteListener",0,1,"AutoCompleteListener");
Then, I instantiate the listener and the AutoCompleteForm like:
import QtQuick 2.5
import com.foo.AutoCompleteListener 0.1
Item {
AutoCompleteForm { id: autocomplete_form }
AutoCompleteListener { id: listener }
}
How can I pass a reference to the QML object AutoCompleteForm into AutoCompleteListener?
I tried passing the autocomplete_form field into:
Q_INVOKABLE void set_autocomplete_form(QQmlComponent *autocomplete_form);
on the onCompleted signal:
Item {
AutoCompleteForm {
id: autocomplete_form
Component.onCompleted: {
console.log("AutoCompleteForm completed");
listener.set_autocomplete_form(autocomplete_form);
}
}
AutoCompleteListener {
id: listener
Component.onCompleted: {
console.log("AutoCompleteListener completed");
}
}
}
However, the reference is a nullptr even though both AutoCompleteListener and AutoCompleteForm have been instantiated:
Instantiating AutoCompleteListener and parent is QObject(0x0)
qml: AutoCompleteListener completed
qml: AutoCompleteForm completed
Setting autocomplete_form = QObject(0x0)
How can I get a reference to the AutoCompleteForm or AutoCompleteListener's QML parent? I want to avoid crawling down the entire QML hierarchy with something like:
QObject* f = mView.rootObject();->findChild<QObject *>("AutoCompleteForm");
I plan to support having multiple AutoComplete widgets instantiated in parallel so a relative path (../AutoCompleteForm) to manipulate the QML objects seems better than having to crawl through the tree.
How can I pass a QML object reference into Qt C++?
You can't, the language was not designed for that. But you can get references from within C++ using findChild and findChildren. But read below for your real solution.
Also, I think your question is about an XY Problem. You have a problem X and you think Y solves it, so you ask for Y.
Correct solution for your original problem:
I am attempting to create something like jQuery's autocomplete as a
widget in QML and Qt C++.
In order to solve your real problem you need to use property bindings correctly. QML is a declarative language and self obsession with imperative programming makes it difficult to be straightforward.
Use this pattern for QML:
AutocompleteForm{
id: form
text: "Search here..."
suggestedTerms: helper.results
}
AutoCompleteHelper{
id: helper
searchFor: form.text
}
And for C++ implement
class AutoCompleteHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QString searchFor READ searchFor WRITE setSearchFor NOTIFY searchForChanged)
Q_PROPERTY(QStringList results READ results NOTIFY resultsChanged)
public:
AutoCompleteHelper() {}
virtual ~AutoCompleteHelper() {}
QString searchFor() const { return m_searchFor; }
QStringList results() const { return m_results; }
public slots:
void setSearchFor(QString searchFor)
{
if (m_searchFor == searchFor)
return;
m_searchFor = searchFor;
emit searchForChanged();
// 1. Search for it...
// 2. Some time later fill m_results
// 3. Then: emit resultsChanged()
}
signals:
void searchForChanged();
void resultsChanged();
private:
QString m_searchFor;
QStringList m_results;
};
Then you will see it magically works, because as soon as you change the form.text the binding sets the value in helper.searchFor, which then immediately fires the C++ slot where you can react even instantaneously, and in C++ the emission of resultsChanged() magically fills form.suggestedTerms.
You will find this pattern is extremely efficient so you'll even want to delay it by restarting a timer in C++. Furthermore, it is also beautifully declarative and clean.
How can I get a reference to the AutoCompleteForm or
AutoCompleteListener's QML parent?
The only alternative to rootObject()->findChild() appears to be QQmlProperty::read. There is several of read() function overloads so you can specify the context more precise. You have to provide the name for the object you would like to fetch and make it a property of some root object to start with. I like the general article on this subject of interfacing between QML and C++.
To accomplish precisely what you want or access the parent of certain known property you can try QQmlContext::parentContext together with QQmlProperty::read that accepts the context and see if the empty object name allows to resolve the object then.
You can pass it as a QVariant:
Q_INVOKABLE void QmlLink::pass_object(QVariant v)
{
YourObject* tempObject = (YourObject*) v.value<void *>();
...
}
qml:
my_bound_property.pass_object(the_object);

How to tell if Flex is run in debug mode?

I want to declaratively turn on and off logger for my Flex application and it seems that my usual way of determining if it's debug mode or not works only sometimes. The code for it:
isDebugSWF = new Error().getStackTrace().search(/:[0-9]+]$/m) > -1;
Do you know a better way for it?
Edit:
I posted answer below.
The static property Capabilities.isDebugger in the flash.system.Capabilities class specifies if the Flash player installed is a debug version or not. It requires Flash Player 9 or AIR 1.0 as the minimum version.
Keep in mind though that debug Flash Player installers are publicly available, and that there is nothing stopping users from installing the debug version of Flash. If have something sensitive that you want to hide, using conditional compilation would be a better option.
The previous approach was based on great article . One of the comments suggested that in Release the stacktrace is null, so I modified it properly:
protected function configureLogger() : void
{
if(!isDebugPlayer()|| !isDebugBuild())
{
// stop logging
Logger.hide = true;
}
else
{
// resume logging
Logger.hide = false;
}
}
private function isDebugPlayer() : Boolean
{
return Capabilities.isDebugger;
}
/**
* Returns true if the swf is built in debug mode
**/
private function isDebugBuild() : Boolean
{
var stackTrace:String = new Error().getStackTrace();
if(stackTrace == null || stackTrace.length == 0)
{
//in Release the stackTrace is null
return false;
}
//The code searches for line numbers of errors in StackTrace result.
//Only StackTrace result of a debug SWF file contains line numbers.
return stackTrace.search(/:[0-9]+]$/m) > -1;
}
This way I can finally configure ThunderBoltAS3 logger depending on current build type.
Looks like you are looking for the same, as answered in another post. Look here: How can you tell programmatically if a Flex App is running in debug mode?
We are using conditional compilation at our project :
Adobe Documentation

Resources