How to fire off QML-animation from C++ - c++

I connected C++ and QML via a mediator-class and have everything working in both directions but this one puzzles me.
This is how I connect the mediator-class:
// Initialize Mediator between QML and C++
QmlCppMediator m_qmlCppMediator;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("cppInterface", &m_qmlCppMediator);
How to fire off an ordinary Property-Animation from within C++ ?

Ok I can answer this myself already.
I went for an approach described here http://qt-project.org/doc/qt-4.8/qdeclarativeanimation.html
I bind the “state” of the object which I try to animate to a Q_PROPERTY in the C++ interface.
The different states are linked to transitions (in QML) which do the animate the object.

Rather an easy way would be to define a JavaScript function inside the QML file itself, lie this:
function startAnimation() {
animationID.running = true;
}
Now call this code from C++, simple!

Related

Mock a C++ class instantiated in QML

I am trying to write a plugin that contains some QML files and some C++ classes that provide lower-level functionalities and communicate with another application. They are used by the QML components.
I want to be able to manage the life time of these C++ objects from QML (i.e. they should be created when the QML file is loaded and destroyed when QML is destroyed), while still being able to mock the C++ objects.
I tried a few different approaches so far. Ideally, the result will be that I can use qmlscene on the QML file I want to edit and have a dummydata folder next to that file which contains the mock for the instantiated C++ class.
If I try that by using qmlRegisterType in a plugin class that inherits from QQmlExtensionPlugin (similar to the example in https://qmlbook.github.io/ch17-extensions/extensions.html), and I pass the resulting library to qmlscene, the QML file will not use the mock, but instantiate a C++ object instead. This means that sometimes, I need to start up a fair bit of logic to get some mocked data into my QML file.
It seems like the example in the "QML Book" suggests to completely design the QML component with a mock before introducing any C++ to QML. Is there a way to do that more sustainable? I guess, I could avoid using qmlRegisterType for a C++ class that I want to mock for a while, by commenting out the according line, but I would like to not have to do that.
The other approach I tried was using QQMLContext::setContextProperty from a central C++ controller class. That enables me to pass the C++ object to QML from C++ and also use the dummydata, however the object's lifetime will not be managed by the QML component, but from C++. Also, each class should potentially be instantiated multiple times and connecting signals properly is pretty error-prone. This is what I found so far:
auto proxy = std::make_shared<Proxy>();
//make `proxy` object known in QML realm
_qmlEngine.rootContext()->setContextProperty("proxy", proxy.get());
connect(&_qmlEngine, &QQmlApplicationEngine::objectCreated,
[&proxy](QObject *object, const QUrl &url) {
if (url == QUrl("qrc:/imports/Common/TestWindow.qml")) {
// make sure the proxy is not destroyed when leaving scope of this function
connect(qobject_cast<QQuickWindow *>(object),
&QWindow::visibilityChanged, // as a dirty workaround for missing QWindow::closing signal
[proxy]() mutable { proxy.reset(); }); // delete proxy when closing TestWindow
}
});
_qmlEngine.load(QUrl("qrc:/imports/Common/TestWindow.qml"));
Is there a "comfortable" way to mock data instantiated in QML and originally coming from C++, or is there at least a good way to attach the life time of such a C++ object to the life time of the QML object?
The way I solved this issue is as follows:
The actual production application will use a C++ plugin, containing only C++ files and no QML.
For mocking, there is a QML module with the same name as the C++ plugin, containing QML files which provide the same interface as the equivalent C++ classes. This module is passed to qmlscene in addition to the general QML includes.
If the C++ class header looks like this:
class Proxy : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(int foo)
Q_INVOKABLE void start();
signals:
void started();
}
And this class is made available to QML like this:
qmlRegisterType<Proxy>("Logic", 1, 0, "Proxy");
The QML mock (in file Proxy.qml) can look like this:
import QtQml 2.12
QtObject {
signal started()
property var foo: 42
function start() { console.log("start") }
}
And be importable in QML with a qmldir file that looks like this:
module Logic
Proxy 1.0 Proxy.qml
The final call to qmlscene would be
qmlscene [path/to/prototype/qml] -I [path/to/folder/containing/proxy/mock/]

How to create a property binding in Qt/C++?

In QML, it's easy write create a property binding, such as:
Rectangle {
width: parent.width
}
Is it possible to do this in C++ too?
In Qt, some QObjects have certain properties that can be "bound" using signals and slots:
auto *someWidget = QPushButton(/* ... */);
auto *otherRelatedWidget = QLabel( /* ... */ );
// windowTitle is a property for both QWidgets
QObject::connect(someWidget, &QWidget::windowTitleChanged,
otherRelatedWidget, &QWidget::setWindowTitle);
Apart from this, you can still connect other signals and slots, even if they're not associated to properties.
I've got to point out that there is no syntax sugar for doing this. See the properties documentation for more info.
In Qt6 you can use QProperty to achieve c++ property bindings.
Check out this blog post:
https://www.qt.io/blog/property-bindings-in-qt-6
They're slightly different than QML binding since they execute lazily. QML binding execute eagerly on every signal. Under the hood QProperty uses thread local storage (TLS) to keep track of dependancies while executing value(). It's definitely an interesting piece of technology and adds a declarative programming paradigm to c++.

C++ cocos2d-x pointer

I've just used cocos2d-x for creating some games. When I read the HelloWorld.cpp, I saw this line
Scene* HelloWorld::createScene()
That's strange for me. How does it work? A method named creatScene that takes no parameters and returns a pointer to a Scene ?
In different libraries, there are different methods to initialize library or some part of that. So, in this case, it maybe create a new context inside library and return it without any argument. It maybe needs no arguments (use defaults) it this step of get them from somewhere else like configuration file. And note that this is convenient to use this type of initializing. Like this:
rc = redis.Redis() #uses default values for server address
It is really an easy question even it cannot be called as question when you check the source code.
In cocos2d-x, CCScene always create this way.
1. create a Layer, which coded by yourself with a lot of other widgets.
2. create a Scene
3. add the layer to the scene
4. return the scene you create.

Simple acces of C++ object data from QML

I'm making a board game in Qt/C++ using qml. All the important game data is represented in a single class. My intention is to have the qml sheets access this one object and draw the game board depending on the data.
What is the simplest approach to exposing the C++ data members to QML?
Now I know the question has been asked, I've seen the answers and the Qt Documentation. I am, however, not satisfied. What I've seen, the way to do this seems to be to make Q_PROPERTY out of every single variable I want to access from QML. This looks to me tedious and unnecessary, not to mention it will stretch the code to 3x it's original length, making it significantly worse to read. Also, in most cases I won't need write function to the data members, for example.
And why bother with Q_PROPERTY overhead when I could just write Q_INVOKABLE getters for just the situations I need?
Here's an example of how simple I hoped it would be when I read in the Qt Project documentation: "This enables C++ data and functions to be accessible directly from QML, often with little or no modification."
class game : public QObject
{
Q_OBJECT
public:
explicit game(QObject *parent = 0);
colors NPC[3]; // colors being an enum declared elsewhere
player players[4]; // player is a non-QObject class containing player stats
}
...
game gMain;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("Game",&gMain);
QML in my ideal world:
Image {
id : Image1
source: { if (Game.NPC[0] === 0) {
if (Game.players[1].LifeCount > 0) {
return "pics/FigG.png"
}
else {
return "pics/StoneG.png"
}
}
Now how close to that can I actually get with QML and how do I go about it?
I'm especially interested in handling simple C++ style arrays and enums (have a lot of those in the game) - would I need to write helper functions, e.g. int Game.GetNPCAt(int i) instead of using just Game.NPC[i] ?
I realize that the way I DON'T want to do it is the tried and trusted, and for good reason... however in my situation (small one-man project) it seems like using a cannon to kill a fly (although the GUI building part in qml is amazingly simple and quite a joy to use) - also having to wrap around every data member including the simplest like an int seems... ridiculously excessive.
Maybe I have missed something somewhere, In which case I humbly apologize. Thank you for any thoughts on the matter.
In order:
Q_PROPERTY: When you look at the page that you quoted, they discuss using the Q_PROPERTY method to expose properties to QML. If you don't use Q_PROPERTY, it is my understanding that your variables won't be registered by QMLViewer (or what have you). The Q_PROPERTY needs a Q_INVOKABLE to get/set your variables. If you don't use Q_PROPERTY, though, your class properties will not appear in QML.
Setting the image source: If you may remember, QML is a forge between CSS and JavaScript. If you're just looking to make the image's source change depending on a condition outside of your Image element, you can just create a JavaScript function to achieve what you have quoted:
Image {
id: Image1
function getImage()
{
if (Game.NPC[0] === 0)
{
if (Game.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
However, the function won't run by itself: you'll have to associate it with a signal, which I would create in your C++ class (put the function under a label named signals: (NOT within public -- see here on how to write signals)). Based on your example, I'm guessing that your C++ object is called Game.
Game {
id: gameKeeper //or whatever you want to name it
onUpdate: Image1.getImage() //replace onUpdate with your signal
}
Image {
id: Image1
function getImage()
{
if (gameKeeper.NPC[0] === 0)
{
if (gameKeeper.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
In theory, you should be able to reference arrays this way with JavaScript (I'm not all that familiar with JS myself).
Array handling: On the C++ side, it looks like the best way to do it is through a QList. Fortunately, a QList iterates very similar to a normal array. I found this, which should help -- just ignore the second dimension.
Hope this helps.

Showing two windows in Qt4

My friend and I have each created parts of a GUI using Qt 4. They both work independently and I am trying to integrate his form with the my main window. As of now this is the code I am using to try and load his form:
//connect buttons and such
connect(exitbtn, SIGNAL(triggered()),this,SLOT(terminated()));
connect(add, SIGNAL(triggered()),this,SLOT(add_rec()));
void MainWindowImpl::add_rec()
{
//form quits as soon as it loads...?
DialogImpl dia;//name of his form
dia.show();
}
I have included his header file. The program compiles but when I hit the trigger his form loads up for maybe half a second and then closes. Does anyone know what I am doing wrong?
You have almost get it right. This is because the RAII of C++. If you allocate the Dialog on stack, it would be destructed as soon as the function return.
Assuming MainWindowImpl inherits publically from QWidget, you're looking for this:
void MainWindowImpl::add_rec()
{
// passing "this" to the constructor makes sure dialog will be cleaned up.
// Note that DialogImpl will need a constructor that takes a
// QObject* parent parameter.
DialogImpl* dialog = new DialogImpl(this);
dialog->show();
}
Look at the Qt documentation for examples of how the constructors should look.
Apparently QT4 only allows one instance of an object at a time, however pointers are another matter. Change both the main.cpp and what ever your main window to look something like this:
DialogImpl *dia=new DialogImpl;
dia->show();