QML On Item Changed Signal - qt

MyObject
Item {
property int current: 0
}
Can this be configured to emit a signal such that the following works?
Item {
property variant myObj: MyObject {}
onMyObjChanged: doThis()
...
}

cmannet85 has answered your question: it's not possible. Perhaps you could post more code so we can suggest alternative approaches.
In terms of a solution using the information you've provided, you should expose signals that client code should connect to in order to know when the object has changed. Since you said the current property is all that matters, and it already has a change signal, you can use Connections:
Connections {
target: myObj
onCurrentChanged: doThis()
}
Or connect to the signal manually:
Component.onCompleted: {
myObj.onCurrentChanged.connect(doThis);
}
function doThis() {
// ...
}

Related

How do you set up Caliburn Micro guard methods for items not in the view?

I'm trying to get Caliburn Micro going in my project and I'm having trouble understanding guard methods (Can*) in the context of properties that aren't bound to the view (forgive me if I'm getting my terminology wrong).
I've adapted Tim Corey's example to add this snippet:
private bool _connected;
public bool Connected
{
get { return _connected; }
set { _connected = value; NotifyOfPropertyChange(() => Connected); }
}
public bool CanSayHello(bool connected)
{
return connected;
}
public void SayHello(bool connected)
{
Console.WriteLine("Hello!");
}
public bool CanClearText(string firstName, string lastName)
{
if (String.IsNullOrWhiteSpace(firstName) && string.IsNullOrWhiteSpace(lastName))
{
Connected = false;
return false;
}
else
{
Connected = true;
return true;
}
}
And the associated xaml is:
<!-- Row 4 -->
<Button x:Name="ClearText" Grid.Row="4" Grid.Column="1">Clear Names</Button>
<Button x:Name="SayHello" Grid.Row="4" Grid.Column="2">Say Hello</Button>
The SayHello button is never enabled (even though it seems like it should have the same state as CanClearText). The intent is to use Connected as an intermediary property; in the application I'm putting together, the idea is that Connected is actually set by an external Message (from a model--not connected to a view).
This question gets to it a bit, but I'd like to avoid explicitly calling NotifyOfPropertyChange(() => CanSayHello); and let the Caliburn Micro framework do the work for me. I'm pretty new to this, and I'm sure there's something simple I'm missing--thanks for your help.
Caliburn Micro, in this particular, case might not provide a easy solution. You would have to trigger the NotifyPropertyChanged manually for the dependend properties like CanSayHello each time Connected Property changes. However, you could make use of Fody Extensions which could do the same for you, without explicitly typing the code.
For example,
[AlsoNotify(nameof(CanSayHello))]
public bool Connected
{
get { return _connected; }
set { _connected = value; NotifyOfPropertyChange(() => Connected); }
}
This, with the help of Code weaving would do the NotifyPropertyChanged for without having to explicitly do so yourself.

Qt library design with signals

I am trying to design a Qt library which gives the output back to the client code using signals, but I can't quite get my head around it, I think something is wrong.
Say the library exposes a single class A as follows:
class A {
public:
void request(int data);
signals:
void response(int res);
}
So the client code instantiates an A, connects its signal to a slot, and calls request(). I initially chose to use a signal to return the output because A takes some time to elaborate the response, so I want that call to be non-blocking.
My problem is: what if I need to call request() in many different places in my code, and do different things after I receive my response? I think the question is fundamentally on the correct use of signal/slot design of Qt.
To give a concrete example, and hopefully explain myself further, I temporarily solved the issue setting a boolean before the request() to "remind" me what path of execution to take later:
void doingThis() {
doingThis = true;
request(data);
}
...
void doingThat() {
doingThis = false;
request(data);
}
...
public mySlot(int res) {
if (dointThis) {
...
} else {
...
}
}
This is hideous. What am I doing wrong?
I agree with Ludo who commented on your question.
If you pass some random number (identifier) into the request, then A can emit that same random number back with the response signal. Even if you have a bunch of slots connected to that signal, you would make them only handle the signal if the identifier was familiar to them.
class A {
public:
void request(int data, int id);
signals:
void response(int res, int id);
}
void doingThis() {
request(data, 0xaaaa);
}
...
void doingThat() {
request(data, 0xbbbb);
}
...
public mySlotA(int res, int id) {
if (id == 0xaaaa) {
...
}
}
public mySlotB(int res, int id) {
if (id == 0xbbbb) {
...
}
}
In the case above, the id is hard-coded to represent where the call came from. However, you could also randomly generate the ID. If you did that, then you'd need to save the randomly generated ID. The advantage is that you could send several different requests from doingThis() and be able to understand which response belongs to each request when they arrive back in your slot.

QML Connections handle event from service results in segfault

I am having a hard time getting to understand the non-standard C++ world that is qt. I have a class that can emit a 'login' signal, that I want to listen to from QML, this simply segfaults.
class Service : public QObject
Q_OBJECT
{
public:
Service()
{
// get the context, snipped for brevity
rootContext->setContextProperty("service", this);
}
public signals:
void login(bool succcess);
public slots:
void method();
};
I can successfully call 'service.method' from the QML, but if I add a Connections section to listen for the login event, I get a segfault whenever that component is displayed.
Page {
Component {
Column {
...
Connections {
target: service
onLogin: {
console.login("TEST");
}
}
}
}
}
I have tried moving the 'Connections' section out of the columns, but this results in a runtime-error and a white page, because it fails to parse the QML. What am I missing here?
I am using QT Creator 4.2.1 if that is in any way relevant.
Found the problem. Apparantly you cannot have a slot and a signal by the same name - even if they have wildly different function signatures. It doesn't give a compile error, it simply crashes.
After changing the name of the signal it magically started working.

Undefined reference on a binding to id, Scoping rules

I am trying to pass around an object required by some qml components. The problem is that I cannot reference this object without it saying its undefined.
Item {
id: root
property alias db: database // this works for some reason
Database {
id: database
Component.onCompleted: {
connect("sqlite3", "dbname=database.db")
deleteTables();
createTables();
}
}
ContactImageProvider {
id: contactImageProvider
database: db // this works for some reason
database: root.database // This would be undefined
database: database // no loop detected but still null
}
}
So I am very very confused how I am supposed to be able to pass dependencies down though objects without the most confusing naming scheme ever. This could be anything BTW. Maybe I need a color in some control but want to pass it to another control as well. The whole scope name resolution documentation is very unclear. I can't make heads or tails what I'm supposed to be able to do.
Item {
id: root
property Database database: Database {
id: database // if no id then this doesnt work
Component.onCompleted: {
connect("sqlite3", "dbname=database.db")
deleteTables();
createTables();
}
}
property alias db: database
ContactImageProvider {
id: contactImageProvider
database: db // this doesn't work. WHY? idk
database: database // works if id is on
}
}
Also aliasing doesn't seem to work if I make it a property.
To any poor soul after me, it seems that this has to do with the differences between how property names and id names are resolved. What I ended up with was this.
Item {
id: root
property Database database: Database {
// notice no id
Component.onCompleted: {
connect("sqlite3", "dbname=database.db")
deleteTables();
createTables();
}
}
ContactImageProvider {
id: contactImageProvider
database: root.database // I can explicitly get a property
}
}

How to connect a JavaScript function to a property signal?

I'm new to Qml and having some trouble connecting a javascript handler to a property's signal. I have a C++ object with a property and signal.
class CppObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant value READ getValue WRITE setValue NOTIFY valueChanged)
signals:
void valueChanged(const QVariant &);
};
The objects are created through a C++ factory method and I'm able to bind the values
and changes to Qml properties. This all works.
property CppObject obj: cppProxy.PropertyFactory("foo");
Text
{
x: 100;
y: 100;
text: parent.obj.value;
}
For some properties, I'd like to connect the valueChanged signal to a javascript function.
I've been up and down through the Qml documentation and have tried a bunch of stuff without
any luck. I figured something like this should work, but doesn't
function objEventHandler()
{
console.log('objEventHandler() ran')
}
Component.onCompleted:
{
obj.value.valueChanged.connect(objEventHandler);
}
What is the best way to do this?
You can also connect as you've tried in your example, but the form is:
Component.onCompleted:
{
obj.valueChanged.connect(objEventHandler);
}
The signal is not a property of the 'value' object, but of 'obj'.
It's simple using the connections object.
Connections
{
target: obj;
onValueChanged: console.log('changed');
}

Resources