Integrating the QUdpSocket Class into QML
-
Hi JKSH - pleas hold off on responding until my next post - I might need to correct my last response...
-
Hi JKSH,
I can't find the documentation on the connect function. I can't find the 2 argument version of connect in the existing documentation, yet it compiles... Can you point me to a reference that shows the 2 argument version of connect?
-
Hi jediengineer,
There is no 2-argument version of connect(). All of them are listed in the "QObject documentation":http://qt-project.org/doc/qt-5/qobject.html#connect, and all of them require at least 3 things:
- The sender (signal emitter)
- The signal
- The slot
And if your sender and receiver are different objects (which is true in your case), you also need to provide a 4th argument:
- The receiver
I'll respond to the rest of your post in detail soon.
-
Thanks!! I worked on a few things over the weekend, and noticed that my code just wouldn't compile no matter what. The compiler showed that it had autodetected MinGW, but it still wouldn't compile. I have since uninstalled and re-installed Qt, and everything compiles just fine now. It actually compiled with no errors, but I am getting "function() not defined" for my functions, which I haven't set up yet completely. Anyways, I'll wait for your response before I post the rest.
-
Hi jediengineer,
Your QML code is looking good!
[quote]
So for starters, I began by making a class of all my functions...
@
class TLA_Funcs
{...}
@Then an object from that class, which is making me wonder if my class is redundant in some form:
@
class LambdaFunc : public QObject
{...}
@
[/quote]A quick intro to some terminologies in object-oriented programming:- A class is like a blueprint or schematic or technical drawing. It describes how an object should be built.
- An object is an actual instance (like a manifestation or a copy) of a class.
So using your example code, both TLA_Funcs and LambdaFunc are classes. You can create TLA_Funcs objects like this:
@
int main() {
TLA_Funcs t1_stack;
TLA_Funcs* t2_heap = new TLA_Funcs();... return 0;
}
@t1_stack is an instance of the TLA_Funcs class that is allocated on the stack, t2_heap is an instance of the TLA_Funcs class that is allocated on the heap. The new keyword in C++ is like a smarter version of malloc fromC (have you used this before?).
So, you don't need to have both TLA_Funcs and LambdaFunc. Just one will do.
And, you only need to create one instance of your class, to manage your UDP connection and to listen for your QML signals.
[quote]
@TLA_FUNCS sys_enable();@
[/quote]I'm not sure what you're trying to do here. This says: Every time you call sys_enable(), it returns a copy of TLA_FUNCS when it is done. Can you explain your train of thought in more detail?Anyway, I'll make your TLA_Funcs class into a QObject class, because you probably don't want your slots to return anything:
@
class TLA_Funcs : public QObject
{
Q_OBJECT
public slots:
void sys_enable(void);
void sys_inhibit(void);
void sys_disable(void);
void sys_self_check(void);
protected:
// ...
};// Or, the above can also be written as:
class TLA_Funcs : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void sys_enable(void);
Q_INVOKABLE void sys_inhibit(void);
Q_INVOKABLE void sys_disable(void);
Q_INVOKABLE void sys_self_check(void);
protected:
// ...
};
@[quote]
@
QQuickItem *item = view->rootObject(); // unsure how to handle these two
MyObject *obj = new MyObject(); // or if I need one instance of these per slot...QObject::connect(item, SIGNAL(sys_enable()); QObject::connect(item, SIGNAL(sys_inhibit()); QObject::connect(item, SIGNAL(sys_disable()); QObject::connect(item, SIGNAL(sys_self_check());
@
[/quote]The QQuickItem instance represents your QML file. The MyObject instance is the object that listens for the QML signals. So in your case, replace MyObject with TLA_Funcs. You only need one instance for your entire program.[quote]My assumption is that each “item” needs to be replaced with a descriptor like “button.enable” so that the final object slot connection would be this:
@Object::connect(button.enable, SIGNAL(sys_enable());@Am I correct? And if I understand this correctly, this will automatically call the function specified when the button is clicked?
[/quote]No. Multiple issues here:- "item" refers to your object that receives the signals. It must be retained.
- sys_enable() is a slot, not a signal.
- Like I said before, in all calls to connect(), you must specify 4 things: the sender, the signal, the receiver, and the slot.
So you want something like this:
@
QQuickItem* item = view->rootObject();
TLA_Funcs* funcs = new TLA_Funcs();QObject::connect(item, SIGNAL(buttonClicked_enable()),
funcs, SLOT(sys_enable()));
@The buttonClicked_enable() signal doesn't exist yet, so we add it to your QML file:
@
// TLA_UDP_Portal.qml
Rectangle {
// Declare the signal
signal: buttonClicked_enable()Button { id: enable_button x: 106 y: 408 text: "Enable" // Emit the signal onClicked: buttonClicked_enable() }
}
@Now, every time the Button emits the clicked() signal, your TLA_UDP_Portal will also emit the buttonClicked_enable() signal. We have to do it this way because the Button is "hidden" inside TLA_UDP_Portal, so the Button's signals are not accessible from the C++ side.
Then, when the buttonClicked_enable() signal is emitted, it will call the sys_enable() slot.
There are a few more paragraphs, I'll address them in a later post. Hope this is enough for some progress!
-
JKSH - this is fantastic. I think I understand the slot much better from your example, thanks!!
Malloc - yes, I've used it before, not often in the DSP work I've done, but I have used it. With your example of:
@TLA_Funcs* t2_heap = new TLA_Funcs();@
normally, you would need to release it with @delete TLA_Funcs;@ would you not? Or is that what @return 0;@ is for? I've never used it with a return. My program should only call the instance of it once though, so I'm not sure I shouldn't need to delete it. I am still trying to determine if I need an endless loop in the main() function to address the incoming packets, and to modify the textbox to output the data coming back from the embedded device... but after reading up on the QML Socket Class, I believe I may not...So as you were writing your response, I went to cplusplus.com and ran through the class structure, and you re0inforced it. My only shortcoming was understanding the way to use it with Q_INVOKABLE - I assumed you had to have a pre-existing class (from reading the Qt notes) in order to to make it's functions Q_INVOKABLE. Thankfully, I now know better. So my class is now this so far, as per your lead:
@class TLA_Funcs : public QObject
{
Q_OBJECT
public slots:
Q_INVOKABLE void sys_enable(void);
Q_INVOKABLE void sys_inhibit(void);
Q_INVOKABLE void sys_disable(void);
Q_INVOKABLE void sys_force_idle(void);
Q_INVOKABLE void sys_autoupdate(void);
Q_INVOKABLE void sys_reset(void);
Q_INVOKABLE void sys_observe(void);
Q_INVOKABLE void sys_dac_test(void);
Q_INVOKABLE void sys_reset_dac(void);
Q_INVOKABLE void sys_read_adc(void);
Q_INVOKABLE void sys_read_mux(void);
Q_INVOKABLE void sys_poll(void);
Q_INVOKABLE void sys_set_dac(void);
Q_INVOKABLE void sys_cpu_reset(void);
Q_INVOKABLE void sys_dac_step(void);
Q_INVOKABLE void sys_cpu_halt(void);
Q_INVOKABLE void sys_self_check(void);
protected:
// to be added later
};@I also looked in my QML code, and I didn't have the @signal: buttonClicked_enable()@ in my code. I'm assuming that I need to define one signal per button and checkbox in the parent rectangle? So I would have:
@Rectangle {
id: main
width: 640
height: 480
color: "#a9cde8"
border.width: 4
border.color: "#000000"signal buttonClicked_enable() signal buttonClicked_Disable() signal checkbox_Checked1() signal checkbox_Checked2() Button { id: enable x: 106 y: 408 text: "Enable" onClicked buttonClicked_enable() CheckBox { id: left_only1 x: 86 y: 3 text: "LEFT" onClicked checkbox_Checked1() } } Button { id: disable x: 106 y: 408 text: "Disable" onClicked buttonClicked_disable() CheckBox { id: left_only2 x: 86 y: 3 text: "LEFT" onClicked checkbox_Checked2() } }
}@
Also, I get an error when I use a colon after signal, so I removed it and it builds with no errors. So then on my C++ side, I would now have in my main function:
@int main (int argc, char*argv[]) {
QGuiApplication app(argc, argv); QQuickView *view = new QQuickView(QUrl("TDK_Lambda_UDP_Portal.qml")); view->show(); QQuickItem *item = view->rootObject(); TLA_Funcs *obj = new TLA_Funcs(); QObject::connect(item, SIGNAL(buttonClicked_enable()), funcs, SLOT(sys_enable()); QObject::connect(item, SIGNAL(checkbox_Checked1()), funcs, SLOT(chkbox1()); QObject::connect(item, SIGNAL(buttonClicked_disable()), funcs, SLOT(sys_disable()); QObject::connect(item, SIGNAL(checkbox_Checked2()), funcs, SLOT(chkbox2());@
This seems to build just fine, so I will try making a dummy function today to see if I did it right. Hopefully, writing back to a text box will be similar!!
-
[quote]normally, you would need to release it with
@delete TLA_Funcs;@
would you not?[/quote]Correct. :) I did not show complete code (that's what the "..." was for)[quote]My program should only call the instance of it once though, so I’m not sure I shouldn’t need to delete it.[/quote]More precisely, you want to create one instance of TLA_Funcs, and keep it for as long as your program runs. That instance will receive all signals from your QML code, and its slots will be executed in response to those signals.
Thus, you wouldn't delete your instance until you are shutting down your program.
[quote]Or is that what
@return 0;@
is for? I’ve never used it with a return.[/quote]return passes the result of this function back to whoever called the function -- See http://www.cplusplus.com/doc/tutorial/functions/ . Functions that don't return anything have "void" as their return type.That's why I said earlier to make your slots return "void", not "TLA_FUNCS".
[quote]So my class is now this so far, as per your lead:
@
class TLA_Funcs : public QObject
{
Q_OBJECT
public slots:
Q_INVOKABLE void sys_enable(void);
Q_INVOKABLE void sys_inhibit(void);
...
}
@
[/quote]You only need one of "public slots" or "Q_INVOKABLE". Having both is redundant.[quote]I also looked in my QML code, and I didn’t have the
@signal: buttonClicked_enable()@
in my code. I’m assuming that I need to define one signal per button and checkbox in the parent rectangle?...
Also, I get an error when I use a colon after signal[/quote]I apologize for the typo in my last post. You're right, there should be no colon after "signal".
There needs to be a colon after "onClicked" though :)
The rest of your QML code looks fine.
You might notice that it gets tedious to add so many signals (one per button). Once you've mastered this method, I'll show you the shorter way of calling C++ functions from QML.
[quote]
@
TLA_Funcs *obj = new TLA_Funcs();QObject::connect(item, SIGNAL(buttonClicked_enable()), funcs, SLOT(sys_enable());
@
[/quote]You don't have a "funcs" object -- you called your object "obj" instead. So the name you use in the connection must match.Note that connections and QML code are not checked at compile-time -- you will only see run-time error messages.
-
JKSH -
Sorry, it slipped my mind with the "return 0" - Most of the "void main(void *pd)" (in my case) functions that I use have always had an endless while(1) loop inside, and never needed the "return 0" - but yes, you're right, void functions always return 0, or just return (depending).
As for the Q_OBJECT and Q_INVOKABLE, I didn't realize they were redundant within the class - I saw an example which I'm struggling to locate, which had it that way, so I took that lead.
Also, "obj" was a typo - I did actually call it "funcs" - sorry about that. I am curious as to that shorter method of calling C++ functions... :-)
-
Ok, so I've entered my signals as you have explained:
@signal buttonClicked_enable()@
and in the button description:
@onClicked:buttonClicked_enable()@On the C++ side:
@QQuickItem *button = view->rootObject;
TLA_Funcs *funcs = new TLA_Funcs();
QObject::connect(button, SIGNAL(buttonClicked_enable()), funcs, SLOT(sys_enable());@Which should be correct if I'm not mistaken. My functions are slotted in the class, and I'm still defining the functions. Part of these functions rely on several checkboxes near the button - so when I enable both halves of the system, I would check both checkboxes. On the C++ side, I have to check the state of those checkboxes, so if I read right, I should be able to do that as such:
@QSettings settings;
QCheckBox* left_INV=ui->left_INV;
QCheckBox* right_INV=ui->right_INV;@and this should create an instance of the checkboxes for my code to access the parameters, correct? Then I would read the checked state as a bool value like so:
@ left_INV->setChecked(settings.value("checkstate").toBool());
right_INV->setChecked(settings.value("checkstate").toBool());@This should make left_INV and right_INV a boolean value that I can refer to in my code, if I'm not mistaken.
I'm going to try adding one full function so that I can check functionality after I build it, and will let you know if I get hung up...
-
[quote author="jediengineer" date="1392133256"]Ok, so I've entered my signals as you have explained:
@signal buttonClicked_enable()@
and in the button description:
@onClicked:buttonClicked_enable()@On the C++ side:
@QQuickItem *button = view->rootObject;
TLA_Funcs *funcs = new TLA_Funcs();
QObject::connect(button, SIGNAL(buttonClicked_enable()), funcs, SLOT(sys_enable());@
Which should be correct if I'm not mistaken.
[/quote]Yep, looks good, except for some typos (you have mismatched parantheses).One very tiny nitpick: Your QQuickItem represents multiple buttons, so I'd pick a different name to "button". :)
[quote]But as for reading the checkboxes and text in general, let me take a stab at it… So there’s the parent button, with the two checkbox children in my sample code I pasted. Am I correct to assume, based on this thread: http://qt-project.org/forums/viewthread/8485 – that to retrieve the state of the checkbox, I would need to do the following:
@
QSettings settings;// point directly to the particular checkbox names QCheckBox* left_INV=ui->left_INV; // this is the name of the checkbox that is the first child of the enable button QCheckBox* right_INV=ui->right_INV; // this is the name of the checkbox that is the second child of the enable button // to load the checkbox settings (I'm assuming this makes left_INV and right_INV a bool value for each?): left_INV->setChecked(settings.value("checkstate").toBool()); right_INV->setChecked(settings.value("checkstate").toBool());
@
[/quote]No. That thread is talking about a checkbox -- an actual graphical user interface component -- implemented in C++. You currently have checkboxes in QML.It is important to note: A C++ QCheckBox is completely unrelated to a QML CheckBox. Qt Widgets (C++) and Qt Quick (QML) are completely different technologies for creating user interfaces.
Also, a QSettings class is for storing program settings into your hard drive. You don't need it (unless you want to do fancy things like... store the IP address on disk).
If you want, you can use C++ widgets to create your user interface for testing your UDP connection to your hardware. But you're almost there with QML, so I'd recommend persevering with QML :)
[quote]after which, C code could address as such:
@
if (left_INV){
// stuff, etc...
}
@
[/quote]Again, no. You have defined left_INV as a pointer to a QCheckBox object -- this is not the checkbox's value. If you call if() on a pointer, it will return true if the pointer is not null.The correct way to check a QCheckBox's state is:
@if (left_INV->isChecked())@(But since you're using a QML CheckBox and not a C++ QCheckBox, you don't need this)
I wrote up to here, then noticed your other investigation at http://qt-project.org/forums/viewthread/38342/ . I'll see if there's anything I can add there.