I am researching possibilities of QtScript. I understand that it is possible to create QObject in C++ and then pass it into QScriptEngine:
QObject *someObject = new WindowWithText;
QScriptValue objectValue = engine.newQObject(someObject);
engine.globalObject().setProperty("window", objectValue);
This works - I was able to call my methods defined in C++:
WindowWithText declaration:
#include <QWidget>
namespace Ui {
class WindowWithText;
}
class WindowWithText : public QWidget
{
Q_OBJECT
public:
explicit WindowWithText(QWidget *parent = 0);
~WindowWithText();
public slots:
void setHeading(const QString&);
void setContents(const QString&);
QString getHeading() const;
private:
Ui::WindowWithText *ui;
};
But I would like to instantiate windows from qtscript itself, like this:
var window = new WindowWithText();
I understand I will probably have to write some proxy between constructor and QtCcript, but how to do it?
So far, I just created static method newInstance that creates the object, but that's no new:
QScriptValue WindowWithText::newInstance(QScriptContext *context, QScriptEngine *engine)
{
QObject *someObject = new WindowWithText;
QScriptValue objectValue = engine->newQObject(someObject);
return objectValue;
}
I exported it to the engine as follows:
engine.globalObject().setProperty("WindowWithText", engine.newFunction(WindowWithText::newInstance));
This does not use new though and isn't true javascript pseudoclass:
Following code will fail:
function Subclass() {
this.setHeading("bla bla");
}
Subclass.prototype = Object.create(WindowWithText.prototype);
var window = new dd();
window.show();
Error caused by the fact that WindowWithText.prototype doesn't have anything to do with WindowWithText:
TypeError: Result of expression 'this.setHeading' [undefined] is not a function.
Is there more reliable and less tedious way of exporting C++ classes to my engine?
Related
i have 2 class : Class MaFentre and Code
code.h :
class Code : public QObject {
public :
explicit Code(Q3DScatter *scatter);
public slots:
std::vector<point> readingData(std::string inputFileName);
}
MaFenetre.h :
class MaFenetre : public QWidget
{ Q_OBJECT
public:
MaFenetre();
private:
QLineEdit *entry1;
}
Code.cpp :
std::vector<point> Code::readingData(std::string inputFileName){
// i read a file here
}
i created the Code class object in the constructor of the class MaFenetre
Code *modifier = new Code(graph);
for making connection between slot and signal
QObject::connect(entry1, SIGNAL(textChanged(QString)),modifier, SLOT(readingDara(std::string inputFileName)))
i know the parameters must be of the same type , for that i try to code :
QObject::connect(entry, SIGNAL(textChanged(QString.toStdString)),modifier, SLOT(readingDara(std::string inputFileName)))
but it doesnt work
Your signal and slot arguments are not compatible.
You can do this workaround with the lambda function
Code *modifier = new Code();
MaFenetre * poMaFenetre = new MaFenetre();
connect(poMaFenetre->Entry(), &QLineEdit::textChanged,
[modifier](const QString & oText)
{
std::vector<int> data = modifier->readingData(oText.toStdString());
// Handle data here...
});
In the MaFenetre
class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre() {entry1.reset(new QLineEdit());}
QLineEdit *Entry() {return entry1.data();}
private:
QScopedPointer<QLineEdit> entry1;
};
Using signals and slots it's not the same as calling function and pass parameters.
At first signal and slot must have same parameters type, means they must be defined with same parameters. In your case you have to change your slot to fit possible signals. Also note that returned value is useless in case of slot invoking, so better way is to keep you reading function as is, move it to private area, and create wrapper slot:
void Code::readingDataSlot(QString inputFileName)
{
std::vector<point> result = readingData( inputFileName.toStdString() );
// Do what ever you need with result vector
}
and connect it to signal.
connect(entry1, SIGNAL(textChanged(QString)),modifier, SLOT(readingDataSlot(QString)));
I'm new to programming. I can not understand how to make a reference to the methods of another class.
I have several files and classes:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtCore/QtGlobal>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class Valve;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
class A {
...
private:
void start();
}
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowFlags(Qt::CustomizeWindowHint);
this->setFixedSize(this->geometry().width(),this->geometry().height());
//класс для 7 клапанов
valve = new Valve(7);
}
MainWindow::~MainWindow()
{
delete settings;
delete ui;
}
void MainWindow::valveSwitch(int id)
{
if (valve->getState(id))
closeValve(id);
else
openValve(id);
}
void MainWindow::openValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->open(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(QPalette(Qt::green));
//valveButton[id-1]->setStyleSheet(VALVE_OPEN_COLOR);
QString style = QString(DEFAULT_STYLE_BUTTON) + QString(DEFAULT_BACKGROUND_BUTTON);
valveButton[id-1]->setStyleSheet(style);
ui->mainLabel->setText(str + " open! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant open " + str);
remoteDisconnect();
}
}
void MainWindow::closeValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->close(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(style()->standardPalette());
valveButton[id-1]->setStyleSheet("");
ui->mainLabel->setText(str + " close! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant close " + str);
remoteDisconnect();
}
}
A::A
{
}
A::~A
{
}
void A::start()
{
//MainWindow::openValve(2);
//valve.open(3);
}
How do I access MainWindow class methods openValve/closeValve from class A?
Or how can I access an instance valve of a class Valve of MainWindow's constructor from class A?
//MainWindow::openValve(2);
//valve.open(3);
At very first:
openValve is not static, so you need an instance of MainWindow to be able to call it:
MainWindow* mw_ex0;
// alternatively, if more appropriate:
MainWindow& mw_ex1;
mw_ex0->openValve(2);
mw_ex1.openValve(2);
The MainWindow instance could be a parameter of your function start or a member variable of class A – depending on your concrete needs.
Same applies if you want to access the valve member (valve is a pointer, so you need operator->): mw_ex0->valve->open(3); or mw_ex1.valve->open(3);).
However, you need to grant class A access to those currently private members; three options:
Make A a friend class of MainWindow - this allows A to access MainWindow's private members (might apply for Valve class, too, if open is not public).
Make the appropriate functions public (MainWindow::openValve and Valve::open); to access the valve member of MainWindow, too, you could make it public, too, but it is in general not recommendable to make the internals of a class publicly available to the outside world - someone might simply change your valve member to something else - and your program is broken... So rather provide a simple getter for.
Make A an inner class of MainWindow. Then it gets access to its outer class members implicitly (depending on the requirements for class A, this might not be suitable – up to you to decide...).
Sidenotes:
In your constructor, you do not initialise the settings member.
You do not clean up the valve member in your destructor (potential memory leak).
To avoid having to clean up, you could to incorporate valve directly in your class - this is not always suitable, but might be a good option here (up to you to decide, just showing the alternative):
class MainWindow
{
Valve valve;
};
MainWindow::MainWindow()
: valve(7) // calls constructor directly
{ }
Be aware that you now do use operator. to access the valve's members (mw_ex0->valve.open(3);). Advantage is that Valve will no be automatically cleaned up together with MainWindow. Alternatively, a std::unique_ptr could be used to hold the pointer to your Valve instance, then you get automatic cleanup, too.
You should pass a MainWindow object into the A::start method:
class A {
...
private:
void start(MainWindow & w);
}
void A::start(MainWindow & w) {
w._MainWindow_method_name_here_();
}
Or you should declare a static method in MainWindow class:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static void aStaticMethod();
};
void MainWindow::aStaticMethod() {
...
}
void A::start() {
MainWindow::aStaticMethod();
}
To access protected/private methods of MainWindow you should declare the A class as a friend of MainWindow:
class MainWindow : public QMainWindow
{
friend class A;
...
};
Update
I create a new class for it to work in a separate thread, and call its methods from the main class (by clicking on the button). Accordingly, I need class A to open / close valves, etc.
The "true Qt way" is to use signals & slots mechanism.
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// Transform open/closeValve methods into slots
// (a method that can be assigned as an event handler)
//
public slots:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
// This class should be a descendant of QObject
//
class A : public QObject
{
Q_OBJECT
// Transform this method to a slot, so it can be called
// as regular method, or can be assigned as an event handler,
// for instance, as QPushButton::click handler.
//
public slots:
void start();
// Add signals
//
signals:
void openValveSignal(int id);
void closeValveSignal(int id);
}
void A::start()
{
// do something and then emit the signal to open valve,
// MainWindow::openValve(2) will be called
emit openValveSignal(2);
...
// do something and then emit the signal to close valve,
// MainWindow::closeValve(3) will be called
emit closeValveSignal(3);
}
// connects A signals with MainWindow slots,
// so when you `emit A::***Signal()` then corresponding
// `MainWindow::***` method will be called
//
void initialize(MainWindow * pWnd, A * pA)
{
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
You can call a->start() method from MainWindow methods as usual. Or you can connect button clicked signal with A::start method, for instance:
void initialize(MainWindow * pWnd, QAbstractButton * pBtn, A * pA)
{
// C++11 lambda function is used here because A::start has no arguments
QObject::connect(pBtn, &QAbstractButton::clicked, [pA](){ pA->start(); });
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
so when you click a button then A::start method will be called automatically. And then MainWindow::open/closeValve methods will be called from A::start method.
Declare openValve as public method and valve as public object (open must be public too)
Then use as:
MainWindow mainWindow;
mainWindow.openValve(2);
mainWindow.valve.open(3);
i have a class like this:
class MyClass : public QObject
{
Q_OBJECT
public:
CircularList<unsigned char> buffer_[2];
explicit MyClass(QObject *parent = 0);
signals:
void dataReady(short *buff,int len);
};
and the other one is:
class WaveItem:public QQuickItem
{
Q_OBJECT
public:
WaveItem(QQuickItem *parent = 0);
public slots:
void setSamples(short *buff,int len);
protected:
QSGNode * updatePaintNode(QSGNode *node, UpdatePaintNodeData *data);
};
i need to connect this class in qml with signal(dataReady)/slot(setSamples). how is it possible?
If you check qt docs about exposing signals it describes very well.
First, you need to register your QObject derived class to QML engine.
qmlRegisterType<Myclass>("MyclassLib", 1, 0, "Myclass");
This way, you can create Myclass objects in QML.
But if you like to create objects in C++ and use that particular object in QML then you need to use QQmlContext::setContextProperty
QQuickView view;
Myclass myClass;
view.engine()->rootContext()->setContextProperty("myclass", &myClass);
After you registered type or set your object to QML, you can now use them.
Myclass {
onDataReady: waveItem.setSamples(buff, len);
}
Alternatively, you can also use connect(),
Myclass {
id: myClass
Component.onCompleted: myClass.dataReady.connect(waveItem.setSamples);
}
Note: You might also look to Connections.
Is it possible to make MyObject be always equal (one same instance) in all it's qml definitions?
C++:
class MyObject : public QObject {
Q_OBJECT
Q_DISABLE_COPY(MyObject)
Q_PROPERTY(QString test READ test NOTIFY testChanged)
public:
explicit MyObject(QObject *parent = 0);
signals:
void testChanged();
private:
QString test() const {
return _test;
}
QString _test;
};
QML:
Item {
MyObject { id: myObject1 }
MyObject { id: myObject2 }
}
I want myObject1 to be equal myObject2. Some kind of singleton (but no qmlRegisterSingletonType)
I can interpret your question as if you want more than one entry of MyObject in QML code referring to the same C++ object. You also know what singleton is. How about the wrapper over the singleton that you can use with QML like:
class MyObject : public QObject {
Q_OBJECT
Q_DISABLE_COPY(MyObject)
Q_PROPERTY(QString test READ test NOTIFY testChanged)
public:
explicit MyObject(QObject *parent = 0);
signals:
void testChanged();
private:
QString test() const {
return MySingleton::instance().test();
}
// QString _test; // this supposed to be implemented in MySingleton
};
Or I in my application for many different types of communication between C++ and QML use some kind of MessageBoard from the article Exposing Attributes of C++ Types to QML. That one is even more convenient considering many uses.
I have a problem with accessing ui elements from another class(with instance). I have a second QMainWindow in my application, I can access in secondWindow.cxx class all ui elements but not in read.cxx class. My code looks like following. Where is my mistake? Thank you for your help.
-------------------------------secondWindow.h------------------------------------
#ifndef __secondWindow_h
#define __secondWindow_h
#include "ui_secondwindow.h"
class secondWindow : public QMainWindow
{
friend class read;
igstkStandardClassBasicTraitsMacro(secondWindow, QMainWindow);
Q_OBJECT
public:
igstkStateMachineMacro();
secondWindow();
virtual ~secondWindow();
void createSignalAndSlots();
public slots:
void secondWindowTest();
protected:
private:
Ui::secondMainWindow m_secondWindowUI;
};
#endif
-------------------------------secondWindow.cxx------------------------------------
#include "secondWindow.moc"
#include "secondWindow.h"
#include "read.h"
secondWindow::secondWindow() :m_StateMachine(this)
{
m_secondWindowUI.setupUi(this);
createSignalAndSlots();
}
void secondWindow::createSignalAndSlots()
{
connect(m_secondWindowUI.pushButton1, SIGNAL(clicked()),this, SLOT(secondWindowTest()));
connect(m_secondWindowUI.pushButton2, SIGNAL(clicked()), read::instance(), SLOT(readTest()));
}
void secondWindow::secondWindowTest()
{
m_secondWindowUI.pushButton1->setEnabled(true); //OK
}
secondWindow::~secondWindow(){}
---------------------------------read.h--------------------------------------
#pragma once
#include "secondWindow.h"
class read : public QObject
{
Q_OBJECT
public:
static read *instance();
read();
virtual ~read() {}
public slots:
void readTest();
protected:
secondWindow *m_readUI;
static read *m_read;
private:
};
---------------------------------read.cxx--------------------------------------
#include <read.moc>
#include "secondWindow.h"
#include "read.h"
read *read::m_read= NULL;
read::read()
{
m_readUI = dynamic_cast<secondWindow*>( QApplication::instance() );
}
read *read::instance()
{
if(m_read == NULL)
m_read = new read();
return m_read;
}
void read::readTest()
{
m_readUI->m_secondWindowUI.qlabelTest->setText("test"); //segmentation fault
}
You are casting a QApplication::instance(), which is a QApplication * deriving from QCoreApplication * deriving from QObject *. That won't work, it's not a secondWindow *, not even a QMainWindow *, not even a QWidget *.
Apart from that, your coding style is rather strange -- in Qt, it's customary to use CamelCase for classes, not thisStuff which usually applies to functions and methods. Including <read.moc> is just wrong. Why is read::m_read static? Finally, the coupling between the two window classes is set up in a strange way (accessing global stuff like QApplication just to get a reference to another window smells ugly code). A much better and more obvious approach is to either wrap all of your windows in a parent object or setting up the dependencies explicitly, perhaps like this:
MainWindow *mainWindow = new MainWindow();
SecondWindow *second = new SecondWindow(mainWindow);
UtilityWindow *utilityWin = new UtilityWindow(second);