Property binding not updating - qt

I am continuously getting data for my application as it runs, but I am having a bit of trouble displaying the data once I have read it in and stored it in a map.
When I try to display the data in the QML, it simply displays zero, despite the fact that I can see it updating in the application output.
I access the value in QML using property bindings (I was under the impression that these led headingSensor to be updated whenever carData.headingSensor changed?):
property int headingSensor: carData.headingSensor
Text { text: "Heading: " + headingSensor }
In my data class I have:
Q_PROPERTY(int headingSensor READ getHeadingSensor NOTIFY headingSensorChanged)
int headingSensor;
In the c++ implementation I originally had:
int data::getHeadingSensor(){
return data.value(heading)[headingSensorReading];
}
Where it returns the value in the map which is being updated with the incoming information.
This I realized, probably doesn’t work, because the property is dependent upon the headingSensor variable, which is itself not being updated despite the correct value being returned. So, I thought if I changed it to update the headingSensor value and return that it might work.
So in my data aquisition logic I wrote a method to update the variables as well.
data.insert(key, value);
updateVariables();
}
}
}
void data::updateVariables(){
headingSensor = data.value(heading)[headingSensorReading];
}
int data::getHeadingSensor(){
return headingSensor;
}
While this led to the headingSensor variable being updated in addition to the value in the map, the correct value is still not displayed in the QML display. It simply displays 0 (its default value when it is initially displayed since it has not gotten a value from incoming data yet).
So, I am wondering, how can I get the value of sensorHeading displayed in the QML to update as the value of it and/or the value in the map changes in C++? Do I need to do something like:
Connections {
target: carData
onSensorHeadingChanged: updateValues
}
EDIT:
Trying something like this, the onSensorHeadingChanged never fires. I am not sure why, since the value of sensorHeading clearly changes as I watch it in the application output
Connections{
target: carData
onHeadingSensorChanged: console.log("It's noting the change!")
}

It is the responsibility of the C++ element writer to emit headingSensorChanged() in order to cause the binding to be updated.
This tutorial is a good place to start when implementing a C++ element.
In your case you need to do something like this:
void data::updateVariables(){
int sensorReading = data.value(heading)[headingSensorReading];
if (headingSensor != sensorReading) {
headingSensor = sensorReading;
emit headingSensorChanged();
}
}
Note that we don't emit the change notifier unless there really is a change. This prevents needless JS evaluations, and also removes the possibility of binding loops.

Related

Detect changes in a property after the value is changed

I'm currently working in a project with Qt QML and I have a type that has a property (holds a JavaScript Object), and its reference changes constantly.
What I want to do is to detect the changes in this property and compare with a cached one.
I took a look into Qt Property Binding, but the callback function is executed before the change occurs and the property gets the value of the return of the callback. I want something to be executed after that (the property change), because I need to have the property dataset updated before emitting the signals (in the code the signals are emitted with kind of reflection thing, in the forEach callback).
What I've done so far:
Item {
id: root
property var dataset
Component.onCompleted: {
dataset = Qt.binding(function(){
return MediaWatcherHelper.formatDataset(internals.metadata);
// I want this code to execute after the binding occurs, but I obviously can't execute things after the return.
MediaWatcherHelper.detectChanges(root.dataset, internals.cache)
.forEach(it => root[`${it}Change`](dataset[it]));
internals.cache = root.dataset; // And update the cache
});
}
signal trackIdChange(string trackId)
signal albumChange(string album)
signal artistChange(var artist)
signal titleChange(string title)
signal urlChange(url url)
}
You always can keep the previous value and update it accordingly.
property int myCurrentValue: 10
property int myLastValue: myCurrentValue
onMyCurrentValueChanged: {
console.log("current: " + myCurrentValue + ", last:" + myLastValue)
myLastValue = myCurrentValue;
}

Connect signal to intrinsic handler in JavaScript

Take MenuItem as an example, normally in QML, specifying the handler for the triggered signal is simple:
MenuItem {
onTriggered: {
console.log("Hey");
}
}
Now if I want to do the same thing, but instead to a dynamically created MenuItem, e.g. via Menu.addItem(), then what is the syntax like to connect and specify the signal handler?
I didn't expect this to work, but here is a working solution:
function onTriggered() {
console.log("Hey");
}
var newItem = myMenu.addItem("Item 1");
newItem.triggered.connect(onTriggered);
Nevertheless is there a better way? Above I defined a custom function that happened to be named onTriggered, but it can be named anything, right? So this code piece doesn't make use of the built-in handler, that's why I'm wondering if there's a neater solution?
More importantly, later on I've noticed further problems with this approach: in a for loop, if there is a temporary variable used by the handler, things don't work any more:
for (var i = 0; i < myArray.length; i ++) {
var info = myArray[i];
var newItem = myMenu.addItem("Item " + i);
newItem.triggered.connect(function() {
console.log(info);
});
}
Here you'll see that console prints the last info in myArray for all added menu items when triggered. How can I properly set up independent handlers for each individual menu item?
In addition to the comments, you can easily make it "easier":
Menu {
id: myMenu
function add(text, handler) {
var newItem = addItem(text)
newItem.triggered.connect(handler)
}
}
And there you have it, problem solved, now you can simply myMeny.add("Item 1", onTriggered)
As for the result you get in the loop and functor, that's because of JS's scoping rules. Check the linked answer for details how to work around that.
So this code piece doesn't make use of the built-in handler
Don't think of onSignal as a handler, it is just a hook to attach a handler. Think of it as the declarative connection syntax. Sure, you can also use the Connection element in declarative, but it only makes sense when the situation actually merits it.
I think this confusion stems from some other language / framework which does generate handler methods for you. A onSignal is different from function onSignal() { expression } - the latter is a handler function, the former is handler hook, which just connects the signal to the bound expression.eval(). The Qt documentation too refers to onSignal as a handler, which IMO is technically and conceptually wrong, since the handler is the code which gets executed, the handler is whatever you bind to onSignal.
So you can rest easy, the code you are worried about does not result in any sort of redundancy or inefficiency and doesn't leave anything unused and is in fact the correct way to do things in QML.
All that being said, you can have "built in handlers", but it is a very different thing:
// SomeItem.qml
Item {
signal someSignal
onSomeSignal: console.log("I am a built in handler")
}
// main.qml
SomeItem {
onSomeSignal: console.log("I am another handler")
Component.onCompleted: {
someSignal.connect(function(){console.log("Yet another handler")})
someSignal()
}
}
And the output in the console will say:
qml: I am a built in handler
qml: I am another handler
qml: Yet another handler
As you see, it not really a handler, but a connection hook. There is no shadowing, no "replacing / not using the built in handler", there is just a signal with 3 connections to the evaluation of three expressions.
Using signal.connect() with a named function does come with one advantage, you can later signal.disconnect(namedFunction) if you need to remove a built in or another handler. I am not sure if you can do this if you use onSignal: expr since you don't have a way to reference that anonymous expression. Note that if you use onSignal: namedFunction() this will not work, you will not be able to signal.disconnect(namedFunction) because the signal is not directly connected to that function, but to an anonymous expression invoking it.

Qt QImageReader won't loop, uncontrollable

really quick problem here
I'm trying to use QImageReader to read the frames of a Gif, but when the animation finishes, it won't loop. I can't even control which frame is loaded, as using either the ImageReader's read() function to return a QImage OR using QPixmap::fromImageReader() both cause the QImageReader to automatically jump to the next frame. The problem is that they both use the same logic where if it is at the end of the animation, return only null instead of resetting.
Here is the way I'm attempting to work with Gifs: My class has a QTimer and a QImageReader. Upon TimeOut of the timer, I call my "nextFrame()" slot.
void GifPlayer::nextFrame()
{
if (img->currentImageNumber() == img->imageCount()-1)
{
img->jumpToImage(0);
}
else
img->jumpToNextImage();
this->lbl->setPixmap(QPixmap::fromImageReader(img));
}
I must be making some fundamental problem here - can someone please help me? I've even updated to the latest version of Qt, and it hasn't helped
QMovie sounds like a better fit for your use case, provided you can use that instead. The following gives the basic idea and will work with a GIF, looping automatically:
...
void MyWindow::init() {
movie = new QMovie("mymovie.gif");
if(!movie->isValid()) {
qFatal("Movie not valid");
}
movie->stop(); // Ensure stopped.
}
void::MyWindow nextFrameSlot() {
this->lbl->setPixmap(movie->currentPixmap());
this->movie->jumpToNextFrame();
}
...
This isn't something I've tried, but the QImageReader class has the following three methods:
int currentImageNumber () const
For image formats that support animation, this function returns the sequence number of the current frame.
int imageCount () const
For image formats that support animation, this function returns the total number of images in the animation.
bool jumpToImage ( int imageNumber )
For image formats that support animation, this function skips to the image whose sequence number is imageNumber.
It seems to me like it would be possible with the first two methods to determine when you've read the last frame of the animation, and then use jumpToImage to jump back to the first frame before the next read().

Making a reference-counted object in D using RefCounted!(T)

How do you use std.typecons.RefCounted!(T) to make a reference-counted object in D?
I've tried to figure out what std.array.Array does internally by looking at the source, but while I can read the source, I just can't figure what a "payload" is or how it all works when there's things like bitwise struct copying involved, as well as why some things are duplicated in the internal and external structure.
Could anyone provide an example or a link on how to use it to, say, wrap a simple Win32 HANDLE?
Thanks!
Disclaimer: I haven't tested my claims, just read the documentation.
Payload is referring to what is being stored. In your case the payload is the Win32 HANDLE. Since HANDLE is just an integer you wouldn't want to do:
auto refHandle = RefCounted!HANDLE(WhatGetsMeAHandle());
Because a Windows function will need to be called when the handle goes out of scope.
In std.containers.Array what you saw was a struct called Payload, which had a field called _payload. The structure is going to be the storage of the data, accessed through _payload. This provides a level of indirection to be utilized later.
You will notice that RefCounted is actually used on the Array structure. This means the destructor for that struct will only be called when the reference count is 0. So the ~this() inside of Payload is where you would want to clean up the your HANDLE.
What is happening: since struct is a value type, every time the structure goes out of scope the destructor is called, there isn't one for Array, but Payload is wrapped in a RefCounted, the destructor for RefCounted!Payload is also called. And only when the reference count reaches zero is the destructor for Payload itself called.
Now, RefCounted itself has reference semantics, this means that having an Array a, you can then assign to auto b = a; and everything will be copied over, but RefCounted has a postblits defined meaning the data will not be copied, but the reference count will be
incremented.
I will now try and provide you with a wrapper outline for what you want. It will probably help you visualize the information above, but it may not be entirely correct. Let me know if something needs fixing.
struct MyWinWrapper {
struct Payload {
HANDLE _payload;
this(HANDLE h) { _payload = h; }
~this() { freeHandleHere(_payload); }
// Should never perform these operations
this(this) { assert(false); }
void opAssign(MyWinWrapper.Payload rhs) { assert(false); }
}
private alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data;
private Data _data;
this(HANDLE h) { _data = Data(h); }
}
Since there is no default constructor for a struct you will probably want to provide a free function that returns this structure.

Connecting multiple signals to a single slot in Qt

I'm trying to keep track of the textChanged() signal on for handful of QTextEdits. I want to do the same thing regardless of the text edit emitting the signal: uncheck its associated checkbox in a QListWidget if it becomes empty and leave it checked otherwise. The function I have so for is as follows:
void MainWindow::changed()
{
QString tempStr = ui->hNMRedit->toPlainText();
if(tempStr != "")
{
ui->checkList->item(0)->setCheckState(Qt::Checked);
}
else
{
ui->checkList->item(0)->setCheckState(Qt::Unchecked);
}
}
With the current approach, I would have to make a function like this for every QTextEdit; each function containing virtually identical code. If I stored each of the text edits in an array (so I could find their associated index in the QListWidget), would it be possible for me to have a slot like this?
void MainWindow::changed(QWidget *sender) // for whichever text edit emits the
// textChanged() signal
{
QString tempStr = sender->toPlainText();
if(tempStr != "")
{
// I would potentially use some sort of indexOf(sender) function on the array I
// mentioned earlier here... a little new to Qt, sorry
ui->checkList->item(array.indexOf(sender))->setCheckState(Qt::Checked);
}
else
{
// same as above...
ui->checkList->item(array.indexOf(sender))->setCheckState(Qt::Unchecked);
}
}
Is this possible or should I just create a separate slot for every text edit?
Please let me know if any further clarification is needed!
Lastly, I feel like the only meaningful difference between QLineEdits and QTextEdits is the default size. In favor of keeping things consistent, should I just use one of these objects throughout my UI?
Thanks!!!
I think you are missing the point of slots and signals. How are you creating the connections?
Are you trying to check a box when any of the text boxes change? If so use a QSignalMapper to map the textChanged() signals to send a value of true and connect that to the QCheckBox setChecked(bool) slot.
If that is too complicated subclass QCheckBox and create a set of functions checkBox() uncheckBox() so you can toggle states without a variable. Then connect the QTextEdit textChanged() to your subclass checkBox()
If this is not what you are looking for, at least subclass QTextEditto take in a QCheckBox that it can change when the text changes instead of duplicating code for every QTextEdit
All you need is a hash of QAbstractButton*, keyed by QTextEdit*. In the slot, you look up the sender() in the hash, if found you've got the button you need. This is precisely what is done by the QSignalMapper: you can map from a sender QWidget* to your button QWidget*. Use qobject_cast to cast to QAbstractButton*.

Resources