I've just begun to learn Qml.Altought i read too many qt tutorial, still struggling with some problems.
I want to make multipages desktop application using OpenGL.
First of all, in main function of the program, i am transmitting class instance so that i could access them in qml by using below code snippet.
QQmlApplicationEngine engine;
Foo foo;
engine.rooContext()->setContextProperty(QStringLiteral("foo"),&foo);
But if i must instance all of classes that i want to use in qml, it means there will be a hundred of instances in main function. I think there must be more proper way to do it.
Secondly, if we register object by using qmlRegisterType and import in qml file, can i reach property of bar class after active qml changed ? Because as far as i know, object of bar class is created when corresponding qml is loaded.
Project.cpp
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
qmlRegisterType<bar>("MyLib", 1, 0, "Comp");
qmlRegisterType<bar2>("MyLib2", 1, 0, "Comp2");
QQmlApplicationEngine engine;
Foo foo;
engine.rooContext()->setContextProperty(QStringLiteral("foo"),&foo);
.
.
.
}
GlWindow.qml
import QtQuick 2.0
import MyLib 1.0
Comp
{
id:sample
}
GlWindow2.qml
import QtQuick 2.0
import MyLib2 1.0
Comp2
{
id:sample2
}
bar.h
class bar: public QObject
{
Q_OBJECT
public:
Product* product;
void initialize();//Initialize Gl
void render(); //paint product's context
}
bar2.h
class bar2: public QObject
{
Q_OBJECT
public:
Product* product2;
void initialize();//Initialize Gl
void render(); //paint product's context
}
I painted content of product on GlWindow.qml after that closed this qml and showed GlWindow2.qml. My problem starts here, how to transmit content of product to product2?
For your first concern you can create a "main model" with properties for each of your models you would otherwise have added to the rootContext:
Class MainModel : public QObject
{
Q_OBJECT
Q_PROPERTY(Foo1 *foo1 READ foo1 CONSTANT)
Q_PROPERTY(Foo2 *foo2 READ foo2 CONSTANT)
....
Q_PROPERTY(FooN *fooN READ fooN CONSTANT)
}
If you fancy some design patterns, you can also go the kinda dependency injection rout:
Class Injector : public QObject
{
public:
Q_INVOKABLE QObject* getFoo(const QString& fooName);
}
This has the downside of losing the strong typed return value, but on the plus side you can use caching etc.
About your second question: you are correct, programming it like that it's hard to gain access from the C++ side (assuming that's what you mean) unless you make it a singleton:
class bar : public QObject
{
public:
bar *instance()
{
static bar theInstance;
return &theInstance;
}
};
//main.cpp
qmlRegisterSingleton<bar>("MyLib", 1, 0, "Comp", [](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return bar::instance();
});
But you probably tried that route because you didn't see the "main model" idea.
Also your last question about passing information between models can likely be easily solved by having a nice "main model" where classes know about each other, or can signal and the "main model" connects the signals and slots.
Related
I have subclassed the QAbstractTableModel. Now I would like to pass to to the QML side. All examples I found expose the override methods of the class using Q_INVOKABLE, ie data or setData. Can the whole QAbstractTableModel object be passed as Q_INVOKABLE? If yes, how to do that exactly?
Q_INVOKABLE is meant for exposing methods of QObject derived types to QML. You can use Qt property system for exposing your QAbstractTableModel from your "global object" which you have made available to QML through QML context (as you commented under your question).
You can read from the documentation more about exposing Attributes of C++ Types to QML.
MyTableModel deriving from QAbstractTableModel:
class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
};
MyGlobalObject exposing MyTableModel member variable through the property system:
class MyGlobalObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractTableModel* myTableModel READ myTableModel CONSTANT)
public:
MyGlobalObject(QObject *parent = nullptr) : QObject(parent), m_myTableModel(new MyTableModel) { }
MyTableModel *myTableModel() { return m_myTableModel.data(); }
private:
QScopedPointer<MyTableModel> m_myTableModel;
};
MyGlobalObject instance set as a context property in main:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyGlobalObject model;
engine.rootContext()->setContextProperty("myGlobalObject", &model);
}
MyTableModel used as model for QML TableView:
import QtQuick 2.12
TableView {
model: myGlobalObject.myTableModel
}
It is not safe to pass pointers from C++ to Qml via Q_INVOKABLE as the Javascript garbage collecter will try to delete those objects from QML even though they are supposed to be owned by C++. Instead, you should pass your objects (pointers) as properties using Q_PROPERTY macro.
I am writing a program in QT, which currently has a GameEngine (data handling) class and a MainWindow (GUI) class.
The single instances of both GameEngineand MainWindow classes are owned by the int main function.
The MainWindow instance has a User Action-button, which will open an instance of a QDialog class called Dialog_UserAction. The instance of this QDialog is owned by the MainWindow, which is also the parent of the QDialog (to disable the MainWindow GUI while the Dialog_UserAction instance is open).
My issue is that many events (signals) need to be connected between the QDialog and the GameEngine instance.
Is there any simple way that I can achieve this?
I have already tried by forwarding the signals from Dialog_UserAction to GameEngine via the MainBoard and vice versa. This works, but it is quite a messy solution for this task.
I have also tried letting the Dialog_UserAction be owned by Main, but I don't know how to react on the User Action Button clicked-event in main context.
Finally, I have also tried letting the Dialog_UserAction be owned by the GameEngine instance, which would the easy solution (except that the MainBoard GUI will not be disabled, while Dialog_UserAction is opened). But, I would really prefer that all GUI related instances were kept out of the GameEngine context.
GameEngine.h:
class GameEngine : public QObject
{
Q_OBJECT
signals:
void someSignalToDialog(void);
public slots:
void on_someSignalFromDialog();
}
Dialog_UserAction.h:
class Dialog_UserAction: public QObject
{
Q_OBJECT
signals:
void someSignalToGameEngine(void);
public slots:
void on_someSignalFromGameEngine();
}
Main.cpp:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QApplication::setWindowIcon(QIcon(":/images/MageKnightLogo.jpg"));
GameEngine gameEngine;
Window_MainBoard mainBoard;
mainBoard.showFullScreen();
return a.exec();
}
MainBoard.cpp:
#include "Dialog_UserAction.h"
...
void Window_MainBoard::on_pushButton_enterUserAction_clicked() {
Dialog_UserAction actionDialog(this);
// connect signals and slots here?
if (actionDialog.exec() == QDialog::Accepted)
{
// Send signal with data to GameEngine
}
}
...
So, what I'm really asking is:
Is there any simple way I can setup the signal-slot connections in this setup where I can connect Dialog_UserAction with GameEngine without forwarding the signals in the MainBoard context?
If not, do you have any suggestions on how I could approach this in a better way in general? Thanks in advance.
Since the GameEngine object is a singleton (only one instance exists), you can have the Dialog_UserAction object connect its signals directly to the GameEngine object. You can do that in the Dialog_UserAction constructor.
To get easy access to the GameEngine object, simply add a static member function to it that returns a static GameEngine* member.
GameEngine.h
class GameEngine
{
public:
GameEngine()
{
Q_ASSERT(instance_ == nullptr); // Only one instance allowed.
instance_ = this;
}
static GameEngine* instance() noexcept
{ return instance_; }
private:
static GameEngine* instance_;
};
GameEngine.cpp
GameEngine* GameEngine::instance_ = nullptr;
You can now connect the Dialog_UserAction signals to GameEngine::instance().
So, just for clarification, I ended using the Singleton design pattern as suggested by Nikos C.
But I implemented the class a little differently, and therefore wanted to share this as a standalone answer.
I found that I needed to trigger the constructor of the the object somehow, and figured that this could be done using the so-called (I believe) Lazy Initialization method. Also, the constructor of the class should be private, such that only the instance itself will be able to call this, and thus making sure that the constructor is only called once.
Furthermore, I made the GameEngine::Instance()method as a const static GameEngine*-type in order to let the access to the object be read-only.
GameEngine.h
class GameEngine
{
public:
const static GameEngine* instance() { // Singleton instance reference getter
if (instance_ == nullptr)
instance_ = new GameEngine(); // This triggers the constructor of the object
return instance_;
}
private:
GameEngine(); // The constructor is made private!
};
GameEngine.cpp
// Place this declaration in the top of the file to create the global class instance pointer
// The initial value of the pointer must be nullptr to let the constructor be correctly triggered at the first instance()-call
GameEngine* GameEngine::instance_ = nullptr;
Then in Main and Dialog_UserAction (the clients) the access to the Singleton GameEngine instance is used by creating a const class instance pointer in the each context.
Main.cpp
#include "GameEngine.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Window_MainBoard mainBoard;
const GameEngine* gameEngine = GameEngine::instance(); // Const pointer to the Singleton instance of the GameEngine class (global object)
QObject::connect(gameEngine, SIGNAL(someSignalToDialog), &mainBoard, SLOT(on_someSignalFromGameEngine));
}
Dialog_UserAction.cpp
#include "GameEngine.h"
// Constructor
Dialog_UserAction::Dialog_UserAction(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog_UserAction) {
ui->setupUi(this);
// Const pointer to the Singleton instance of the GameEngine class (global object)
const GameEngine* gameEngine = GameEngine::instance();
connect(this, SIGNAL(someSignalToGameEngine), gameEngine, SLOT(on_someSignalFromDialog) );
}
I have difficulty connecting to SLOTs defined in a different class. I have 2 classes - Computations and MainWindow. MainWindow is supposed to handle the GUI part of the program only and Computations handles the calculations. I am creating a calculator.
Basically, I want to understand how I can connect to SLOTs in the Computations Class from the MainWindow Class.
I guess you already checked the Qt Signals & Slots page. To implement what you want you need to have an instance of your class in the other one.
So for example in your MainWindow class, you can include the Computations class and create a member variable of it:
#include "computations.h"
class MainWindow : public QMainWindow
{
Q_ObBJECT
public:
//..
private:
Computations *_computation;
};
and then in the constructor of MainWindow after initializing the _computation object (_computation = new Computations();) you do the connections like this (works for Qt5):
QObject::connect(_computation, &Computations::somethingHappened, this, &MainWindow::doSomeGuiStuff);
QObject::connect(this, &MainWindow::guiThingHappened, _computation, &Computations::doSomeComputation);
depending on which way it should go.
I hope this helps.
This is another version how to use, I think can be easier to understand for beginners
You need define signal and slots in your classes.
Add to header of your class, for example signals to MainWindow, slots to Computations
public slots:
void something();
signals:
void something_happend();
Then, in anywhere, where you want use it, in your example in mainwindow.cpp, you need to connect signal and slot. Do this by QObject::connect :
QObject::connect(who_emit,SIGNAL(what_signal),who_receive,SLOT(what_slot))
Example:
mainwindow.h
signals:
void something_happend();
computations.h
public slots:
void something_happend();
mainwindow.cpp
Computations *c = new Computations(this);
QObject::connect(this,SIGNAL(something_happend()),c,SLOT(something()));
If you want to pass some arguments, SIGNAL and SLOT that you want to connect need have same types of arguments:
public slots:
void something(int c);
signals:
void something_happend(int c);
QObject::connect(this,SIGNAL(something_happend(int)),c,SLOT(something(int)));
Such connections belong at a level where both the UI and the controller (computation object) are available. Thus, either in the body of main, or in a class that composes that various elements of the application at a high level (such a class usually shouldn't derive from QApplication, though).
It is almost always too tight of a coupling if the UI class knows of the existence of the computation object, or is somehow tied to its details. I usually design the UI to have an interface composed of signals and slots of as generic a nature as practicable, and then tie it to one or more controller objects via signal/slot connections. I also leverage the property system to expose UI-relevant properties in a generic manner, often using viewmodel objects to interface a UI-agnostic controller to a concrete kind of a UI.
In your case, I'd suggest that neither MainWindow know of Computations, nor vice versa:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Computations comp;
MainWindow ui;
QObject::connect(&comp, ..., &ui, ...);
/* more connections here */
ui.show();
return app.exec();
}
For more concrete examples, see answer #1 or answer #2.
you need slots and signals because those work together, like this:
your file.h
public slots:
void start();
signals:
void levelChanged(int level);
implementing:
void MainBoard::start()
{
isStarted = true;
clearBoard();
emit levelChanged(1);
}
now you need to link a button
startButton = new QPushButton(tr("&Start"));
startButton->setFocusPolicy(Qt::NoFocus);
connect(startButton, &QPushButton::clicked, board, &MainBoard::start);
In my main QML file, I have defined a MediaPlayer. To have a low level access to the media buffer (through QAudioProbe), I need to obtain a reference to its mediaObject. My C++ backend interfaces with UI through a class registered by qmlRegisterSingletonType.
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<BackendInterface>("_", 0, 1, "Backend", backendInterfaceProvider);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
And here's the callback:
static QObject *backendInterfaceProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new BackendInterface(/* need a QMediaPlayer* here*/);
}
Question
How to access the QML heirarchy when I am creating my back-end interface (i.e. BackendInterface)?
Since you have a singleton type object, it wil be created on first usage, at which time your MediaPlayer object might not exist yet.
Instead of trying to retrieve the MediaPlayer from QML, make QML "register" the object with C++, i.e. my passing the object to the singleton.
Something like
class BackgroundInterface : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void registerMediaPlayer(QObject *player);
};
and
MediaPlayer {
id: mediaPlayer
Component.onCompleted: Backend.registerMediaPlayer(mediaPlayer)
}
I'm trying to collect an often used subset of GUI-Elements together into one Subclass, which can be "included" into the real GUIs later without rewriting the given functionality (don't ask why, I wanna learn it for later use). The Subclass should use it's own *.ui-File and should be put into an QWidget resding in the real GUI. After this, it would be nice to access some methods of the Subclass from the real GUI -- like the state of a button or so.
But how do I do this right?
In the moment, my Subclass works and is instantiated in main, but cannot be accessed from the real GUI because its only declared in main.
My Subclass Header-File:
class logger : public QWidget, private Ui::loggerWidget {
Q_OBJECT
public:
logger(QWidget *parent = 0);
virtual ~logger();
// some more stuff...
}
The corresponding constructor. I had to run setupUI with "parent" instead of "this", but I'm not sure that this is correct -- anyways, it works... otherwise, the subelements from the subclass are not shown in the main-window of the real GUI.
logger::logger(QWidget *parent) : QWidget(parent){
setupUi(parent);
//ctor
}
Inside the main.cpp the main-window is constructed, which uses it's own *.ui-File (containing one widget "widget_loggerArea") aswell. Doing so, I can not access methods of "logger" from within "loggerTest":
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
loggerTest window;
logger myLog(window.widget_loggerArea);
window.show();
return app.exec();
}
I can't put the constructor of "logger" into the constructor of the main-window "loggerTest", since it will be destroyed immidiately and never enters the event-loop.
I'm sure I'm missing some concept of object-oriented programming, or the way qt handles its stuff... I would be gratefull if someone could put my nose to this ;-)
I was so stupid... using a pointer with new and delete does the job... this is so silly, I can't believe it! I'm more used to VHDL recently, this weakens my C++-karma...
So, the answer is in the real GUI class. The Constructor:
testLogger::testLogger(QMainWindow *parent) : QMainWindow(parent){
setupUi(this);
myLog = new logger(widget_loggerArea);
}
In main.cpp:
QApplication app(argc, argv);
testLogger window;
window.show();
And in constructor of logger, setupUi works with "this":
dfkiLogger::dfkiLogger(QWidget *parent) : QWidget(parent){
setupUi(this);
}
Yes, thats it... Just for completebility, maybe someone needs a similar "push in the right direction"...
EDIT: In the header of the SubClass the scope of the ui-Elements has to be updated to "public", too:
class logger : public QWidget, public Ui::loggerWidget {
Q_OBJECT
public:
logger(QWidget *parent = 0);
virtual ~logger();
}