When do qml predicates assigned to properties get evaluated? - qt

When writing code like:
Item{
id: item
enabled: backend.property == "X"
}
When does the predicate get evaluated. once? or every time property changes?
furthermore, how does it applies when the property is assigned in a state change: is it evaluated once at state change or each time the property change is signaled?
State {
name: "One"
when: step == 2
PropertyChanges {
target: item
enabled: backend.property == "X"
}

The assignment is evaluated everytime any of the properties taking place in the predicate are changed (by means of the Q_PROPERTY(... NOTIFY signalX)). In your example this can be backend or property.
The PropertyChanges in your example will do the same, it will get evaluated every time any of the properties changes. If you assign true to the explicit property, it will be a once-off evaluation, the moment the state changes.

You can see that yourself by putting breaks or print statements in the property setter. The QML variable is evaluated every time the notify signal is called, i.e. emit propChanged() in the case below. The predicate will be re-evaluated too when emit propChanged() is called, which is logical else you would have inconsistent states.
Q_PROPERTY(QString prop READ getProp WRITE setProp NOTIFY propChanged);
private:
QString m_prop;
public:
void setProp(QString prop) {
if (prop != m_prop) {
m_prop = prop;
// log or break here
emit propChanged();
}
}
QString getProp() const { return m_prop; }
signals:
void propChanged();

Related

How to use a JavaFX binding to disable a button if TextField has invalid input

I got the following code from The Definitive Guide to Modern Java Clients with JavaFX:
updateButton.disableProperty()
.bind(listView.getSelectionModel().selectedItemProperty().isNull()
.or(wordTextField.textProperty().isEmpty())
.or(definitionTextArea.textProperty().isEmpty()));
I would like to modify it so the button is disabled if the String entered into frequencyTextField is not a nonnegative integer. I added a term to the conjunction as shown:
updateButton.disableProperty()
.bind(listView.getSelectionModel().selectedItemProperty().isNull()
.or(isLegalFrequency(frequencyTextField.textProperty()).not())
.or(wordTextField.textProperty().isEmpty())
.or(definitionTextArea.textProperty().isEmpty()));
Although it is probably not relevant, here is the method that tests validity:
private BooleanProperty isLegalFrequency(StringProperty sp) {
System.out.println("isLegalFrequency(" + sp.get() + ")");
try {
int value = Integer.parseInt(sp.get());
return new SimpleBooleanProperty(value >= 0);
} catch (NumberFormatException e) {
return new SimpleBooleanProperty(false);
}
}
My problem is that the button is always disabled. I have established that isLegalFrequency() is called only once, when the scene is being created. This makes sense, since I am passing frequencyTextField.textProperty(), not calling a method on it (which presumably sets up a listener behind the scenes).
Is there a way to modify the program without adding an explicit listener so it behaves as I'd like, or is it necessary to create a ChangeListener on frequencyTextField.textProperty?
Very generally, you can create an arbitrary method:
private Boolean validate() {
// arbitrary implementation here...
// in your case something like
if (listView.getSelectionModel().getSelectedItem() == null) return false ;
if (wordTextField.getText().isEmpty()) return false ;
if (definitionTextArea.getText().isEmpty()) return false ;
if (! isLegalFrequency(frequencyTextField.getText())) return false ;
return true ;
}
and then do
updateButton.disableProperty().bind(Bindings.createBooleanBinding(
() -> ! validate(),
listView.getSelectionModel().selectedItemProperty(),
frequencyTextField.textProperty(),
wordTextField.textProperty(),
definitionTextArea.textProperty()));
The parameters to the createBooleanBinding() method are a Callable<Boolean> (i.e. a method taking no parameters and returning a Boolean) followed by zero or more instances of javafx.beans.Observable (any property or ObservableList, etc, will work). You should include any property (or other observable) here that should trigger a recalculation of the disableProperty() when it changes.

bind something to property of type registered with qmlRegisterSingletonType

Is it possible to bind something to property of registered as singleton type? I can't find correct syntax to do something like
Item {
id: rootItem
MySingleton.anProperty: rootItem.width
}
(above results in: "Non-existent attached object")
MySingleton registered registered via
int qmlRegisterSingletonType(const char *uri, int versionMajor, int
versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine
* ) callback)
There is no MySingleton object or group property in the scope of Item and you cannot use this syntax to bind properties for external objects.
It is possible via the Binding element:
Binding {
target: MySingleton
property: 'anProperty'
value: rootItem.width
}
Since your title and question is ambiguous, maybe what you actually want to do is as simple as:
Item {
id: rootItem
width: MySingleton.anProperty
}

How to use the argument of a Signal in QML StateMachine during SignalTransition

I have a C++ class and I made it possible to be able to create it in QML. Then I have a signal in QML which has an argument representing this object. I am using the QtQml.StateMachine and I am catching triggered signals with SignalTransition. I want to be able to set my signals argument to the next state when the SignalTransition triggers. In code:
This is how my signal looks like in Model.qml:
signal mySignal(CustomObject customObject)
My signal transitioning code in State.qml:
import QtQml.StateMachine 1.0 as SM
// SM.State { ...
Model {
id: model
// ...
}
SM.SignalTransition {
targetState: nextState
signal: model.mySignal
onTriggered: console.log(customObject) // error here
}
// ... }
I get the following error: ReferenceError: customObject is not defined.
When I am emitting the signal I pass my customObject as an argument for the signal.
This is a bit of a hack, but the guard is passed the signal's parameters. The guard is an expression which is evaluated to see if it's true (and if so, the transition applies) but there's nothing stopping you running extra code in there. So:
State {
targetState: nextState
signal: model.mySignal
guard: {
// here, customObject is available, because it was an arg to the signal
// so, stash it away somewhere where you can get at it later
root.currentCustomObject = customObject;
// and explicitly return true, so that this guard passes OK and
// then the transition actually happens
return true;
}
}
One approach would be to have the mySignal handler set a property that can be summarily accessed by the less-flexible SignalTransition, like so:
Model {
id: model
property CustomObject currentObj
onMySignal: currentObj = customObject
}
SM.SignalTransition {
targetState: nextState
signal: model.currentObjChanged
onTriggered: console.log(model.currentObj)
}
Hacky and not tested, but might be acceptable for this case.

How to draw dash instead of null in QSpinBox?

I need to draw dash instead of null in QSpinBox. Also I need to make dash key pressing equalling null key pressing.
How can I do this?
You can use setSpecialValueText();
QSpinBox spinBox;
spinBox->setSpecialValueText(tr("-"));
You can then check if the special value is selected by connecting valueChanged(QString) function. Note that this is different from valueChanged(int) You can then check the value of the passed string in a slot, and if it is equal to special text, you can do something.
main()
{
connect(spinBox, SIGNAL(valueChanged(QString)), this, SLOT(doSomething(QString)));
}
void doSomething(QString valueStr)
{
if(valueStr == spinBox->specialValueText())
// Do something
else
//Convert valueStr to int and do other stuff
}
Or you could do something like this:
main()
{
connect(spinBox, SIGNAL(valueChanged()), this, SLOT(doSomething()));
}
void doSomething()
{
if(spinBox->value() == 0)
// Do something with dash
else
//Do something with the value
}
For your other question, you need to create a keyPressEvent and check if pressed key is dash or not. If it's dash you can call another function to do something. Edit: BTW, the index of specialValueText() is 0.
Edit: Or you can create a QShortcut in your main function.
new QShortcut(QKeySequence(Qt::Key_Minus), this, SLOT(doSomething()));
Edit continued: doSomething() is a slot function. Put, for example void doSomething(); in the private slots: section of your header file. And in the cpp file define a function similar to this:
void MainWindow::doSomething()
{
ui->spinBox->setValue(0);
//This is the slot called when you press dash.
}
Edit still continued:
You need to declare a protected: function in the header like this:
virtual void keyPressEvent(QKeyEvent *event);
Then you need to define this function in your cpp file. Like this:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Minus)
ui->spinBox->setValue(0);
}
You don't have to connect any signals or slots for this function. It's an event.
That means when dash is pressed ui->spinBox->setValue(0);
Because of that, you need to create a spinBox with a range starting from 0.
spinBox->setRange(0, 100);
That means,
if(spinBox->value() == 0)
//Then specialValueText is selected.

Qt - invoking slots that take custom pointers as arguments

I'm trying to hack with Qt's signals and slots, and I ran into an issue where QMetaType::invokeMethod won't properly pass pointer arguments to the slot being called.
call(QObject *receiver, const char *slot, const QList<QGenericArgument> &args)
{
const QMetaObject *meta = receiver->metaObject();
bool success = meta->invokeMethod(receiver, slot,
args.value(0, QGenericArgument()),
args.value(1, QGenericArgument()),
args.value(2, QGenericArgument()),
...
args.value(9, QGenericArgument()));
}
Then I call it the following way:
MyReceiver *receiver;
MyObject *myObject;
call(receiver, "mySlot", QList<QGenericArgument>() << Q_ARG(MyObject *, myObject));
Where class MyObject : public QObject { ... }. I also do Q_DECLARE_METATYPE(MyObject *) and qRegisterMetaType<MyObject *>("MyObject *")
What happens is that the slot on the receiver is being invoked, but with the value of the argument is always 0 no matter what I pass to the call(...) as Q_ARG
Out of curiosity I looked into the auto-generated MOC file of the receiver, and found that the slots are invoked with the following code:
void MyReceiver::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
MyReceiver *_t = static_cast<MyReceiver *>(_o);
switch (_id) {
case 0: _t->mySlot((*reinterpret_cast< MyObject*(*)>(_a[1]))); break;
default: ;
}
}
}
Turns out that the value of _a[1] bears proper address of MyObject *. But the reinterpret_cast turns it into 0.
Now I have the following questions:
1) How to programmatically invoke a slot and make sure that the pointer arguments are properly passed to the slot?
2) What does this *reinterpret_cast< MyObject*(*)>(_a[1]) mean? What the extra parentheses (*) mean, and how to interpret this piece of code?
Ok, I think I figured why it's not working... Q_ARG only will create a pointer to my pointer and store the former. I didn't mention that the call function was part of the Task call meant to invoke a slot later on - when the values wrapped into Q_ARG are already out of scope. Basically Q_ARG only maintains a weak reference to the argument object.

Resources