Sending QPixmap pointer via signal-slots, empty pixmap data - c++

I have problem with sharing pointer to Pixmap via signal-slot mechanism.
Inside slot function I have correct QPixmap with fulled data.
In reciever I have unaccessible QPixmap with empty data.
Any ideas?
class A
{
public:
A:A():pixmap(0){};
void fillPixmap()
{
// correct Pixmap and data isn't null
}
public signals:
void sendQPixmapToB(QPixmap*);
private:
QPixmap *pixmap;
}
class B
{
public:
B:B(){};
public slots:
void recievePixmap(QPixmap* pixmap)
{
// here in debugger pixmap is unaccesible and data is 0
}
}
void onButtonClicked()
{
a.fillPixmap();
}
int main()
{
A a;
B b;
.....
connect(a, SIGNAL(sendQPixmapToB(QPixmap*)),b,SLOT(recievePixmap(QPixmap*)));
return 0;
}

I can not tell you what is wrong with your code because as you indicate you are showing a pseudo-code, so I will show you the correct way to do it.
#include <QGuiApplication>
#include <QPixmap>
#include <QTimer>
#include <QDebug>
class A: public QObject{
Q_OBJECT
public:
using QObject::QObject;
void sendPixmap(){
fillPixmap();
emit sendQPixmapToB(pixmap);
}
signals:
void sendQPixmapToB(const QPixmap & pixmap);
private:
void fillPixmap(){
pixmap = QPixmap(64, 64);
pixmap.fill(Qt::red);
}
QPixmap pixmap;
};
class B: public QObject{
Q_OBJECT
public:
using QObject::QObject;
public slots:
void recievePixmap(const QPixmap & pixmap){
qDebug()<<pixmap;
}
};
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
A obja;
B objb;
QObject::connect(&obja, &A::sendQPixmapToB, &objb, &B::recievePixmap);
QTimer::singleShot(1000, &obja, &A::sendPixmap);
return a.exec();
}
#include "main.moc"ยท

Related

How to access QMainWindow from another class

// mainwindow.h
#include "ui_MainWindow.h"
#include "parseTextFile.h"
class MainWindow:public QMainWindow
{
Q_OBJECT
public:
MainWindow(void);
private:
Ui_mainWindow ui;
parseFile *fileParse;
public slots:
int onOkButtonClick();
};
// mainwindow.cpp
MainWindow::MainWindow(void)
{
ui.setupUi(this);
connect(ui.OkButton,SIGNAL(clicked()),this,SLOT(onOkButtonClick()));
}
int MainWindow::onOkButtonClick()
{
fileParse = new parseFile(this);
fileParse->parseTextFile();
return 0;
}
int main(int argc,char* argv[])
{
QApplication app(argc,argv);
MainWindow *mainWindow = new MainWindow();
mainWindow->show();
return app.exec();
}
// parseTextFile.h
class parseFile
{
public:
parseFile(QWidget *parent =0);
~parseFile();
int parseTextFile( );
};
// parseTextFile.cpp
#include "parseTextFile.h"
#include <QMessageBox>
parseFile::parseFile(QWidget *parent)
{
}
parseFile::~parseFile()
{
}
int parseFile::parseTextFile( )
{
QMessageBox::information(this,"a","b");
return 0;
}
I can able to access parseTextFile method but i am getting error in QMessageBox. Is it the right way?
How to print QMessageBox in parseTextFile class?
anything needs to add in parseTextFile constructor?
Pass this to QMessageBox::information as a parent is not a valid argument because parseFile is not a QWidget derived class.
For simple, just pass nullptr instead of this :
QMessageBox::information(nullptr ,"a","b");
In this case, the message box belongs to no one. It's will be closed by the user or when the application exits.
PS: you should release the memory after using fileParse to avoid memory leaks.
There are several ways to like with the main window (static pointer, singleton, top widgets enumeration,etc.) but the simplest one, that I thinks you intend to do, is to make parseFile class as QObject derived class and QMainWindow its parent.
class parseFile: public QObject
{
Q_OBJECT
public:
parseFile(QWidget *parent =0);
~parseFile();
int parseTextFile();
};
parseFile::parseFile(QWidget *parent)
:QObject(parent)
{
}
int parseFile::parseTextFile()
{
QWidget * parentWidget = qobject_cast<QWidget *>( parent() ); //<-- QMainWindow instance
QMessageBox::information( parentWidget, "a", "b");
return 0;
}
int MainWindow::onOkButtonClick()
{
fileParse = new parseFile(this);
fileParse->parseTextFile( );
//fileParse should be released by:
//fileParse->deleteLater();
return 0;
}

Share data between two QWidget instances

I would like share a string between two instances of QWidget.
In main.cpp, two objects are instantiated and shown like this:
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w1,w2; //Derived from QWidget
w1.show();
w2.show();
return a.exec();
}
I would introduce SharedState class:
// shared_state.h
#ifndef SHARED_STATE_HPP
#define SHARED_STATE_HPP
#include <QObject>
class SharedState : public QObject
{
Q_OBJECT
public:
SharedState(QString initialValue = "")
: currentValue(initialValue)
{}
QString getCurrentValue()
{
return currentValue;
}
public slots:
void setValue(QString newValue)
{
if(currentValue != newValue)
{
currentValue = newValue;
emit valueChanged(currentValue);
}
}
signals:
void valueChanged(QString);
private:
QString currentValue;
};
#endif // SHARED_STATE_HPP
Now I would provide reference to SharedState in Dialog's constructor,
// dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QWidget>
#include "shared_state.h"
namespace Ui {
class Dialog;
}
class Dialog : public QWidget
{
Q_OBJECT
public:
explicit Dialog(SharedState& state, QWidget *parent = 0);
~Dialog();
private slots:
void handleTextEdited(const QString&);
public slots:
void handleInternalStateChanged(QString);
private:
Ui::Dialog *ui;
SharedState& state;
};
#endif // DIALOG_H
You may have noticed that I have added two slots, one to handle the case when the text is manually edited and one when shared state will inform us that we are out of date.
Now in Dialog's constructor I had to set initial value to textEdit, and connect signals to slots.
// dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(SharedState& state, QWidget *parent) :
QWidget(parent),
ui(new Ui::Dialog),
state(state)
{
ui->setupUi(this);
ui->textEdit->setText(state.getCurrentValue());
QObject::connect(ui->textEdit, SIGNAL(textEdited(QString)),
this, SLOT(handleTextEdited(QString)));
QObject::connect(&state, SIGNAL(valueChanged(QString)),
this, SLOT(handleInternalStateChanged(QString)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::handleTextEdited(const QString& newText)
{
state.setValue(newText);
}
void Dialog::handleInternalStateChanged(QString newState)
{
ui->textEdit->setText(newState);
}
Now the change in the main function:
// main.cpp
#include "dialog.h"
#include "shared_state.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SharedState state("Initial Value");
Dialog w1(state), w2(state);
w1.show();
w2.show();
return a.exec();
}

Pass custom C++ object to Qml error (no properties)

I am trying to write code that will pass some data from C++ engine to the Qml scripts via signal, but it looks like that I doing some thing wrong, because when I receive signal in Qml my object don't any method or properties! Look at that code:
Signaller - class that invoke signal:
signaller.h:
class Signaller : public QObject
{
Q_OBJECT
public:
explicit Signaller(QObject *parent = 0);
Q_INVOKABLE void invokeSignal();
signals:
void mysignal(TestClass test);
public slots:
};
signaller.cpp:
Signaller::Signaller(QObject *parent) :
QObject(parent)
{
}
void Signaller::invokeSignal()
{
TestClass s;
emit mysignal(s);
}
TestClass - class that will be passed to Qml engine, and which must have test method in Qml script:
Test.h:
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QObject *parent = 0);
TestClass(const TestClass& obj);
~TestClass();
Q_INVOKABLE void test();
signals:
public slots:
};
Q_DECLARE_METATYPE(TestClass)
Test.cpp:
TestClass::TestClass(QObject *parent) :
QObject(parent)
{
qDebug()<<"TestClass::TestClass()";
}
TestClass::TestClass(const TestClass &obj) :
QObject(obj.parent())
{
qDebug()<<"TestClass::TestClass(TestClass &obj)";
}
TestClass::~TestClass()
{
qDebug()<<"TestClass::~TestClass()";
}
void TestClass::test()
{
qDebug()<<"TestClass::test";
}
Those 2 classes also registered in main function:
int main(int argc, char *argv[])
{
// SailfishApp::main() will display "qml/template.qml", if you need more
// control over initialization, you can use:
//
// - SailfishApp::application(int, char *[]) to get the QGuiApplication *
// - SailfishApp::createView() to get a new QQuickView * instance
// - SailfishApp::pathTo(QString) to get a QUrl to a resource file
//
// To display the view, call "show()" (will show fullscreen on device).
qmlRegisterType<TestClass>("Test", 1, 0, "Test");
qmlRegisterType<Signaller>("Signaller", 1, 0, "Signaller");
return SailfishApp::main(argc, argv);
}
That is my Qml file with test code:
import Signaller 1.0
Page {
Signaller {
id: sig
Component.onCompleted: sig.invokeSignal()
onMysignal: {
console.log("signal",typeof test);
console.log(typeof test.test);
}
}
}
And the log:
[D] TestClass::TestClass:6 - TestClass::TestClass()
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj)
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj)
[D] onMysignal:41 - signal object
[D] onMysignal:42 - undefined
[D] TestClass::~TestClass:18 - TestClass::~TestClass()
[D] TestClass::~TestClass:18 - TestClass::~TestClass()
As you can see from log, TestClass.test field is empty after passing to the Qml.
What I am doing wrong?
You are passing a QObject derived object by value through the signal/slot system - you should never do this (docs). Create it on the heap and send it's pointer through.
In this case you'll probably want it's lifetime controlled via QML, you can set that by calling QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership).
QObject should not be used by value in signals and slots. Also, It is a bad idea to implement a copy constructor for a QObject subclass when QObject itself hides its copy constructor.
Thus change your signals to pass pointers to QObject and It will be fine. There is a short and good reference on how to communicate between C++ and Qml
ps: You don't need to register classes which declare the Q_OBJECT macro.
This is an example I made for myself for testing C++ <--> QML interaction:
//File: animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#include <QObject>
class Animal : public QObject
{
Q_OBJECT
Q_PROPERTY(QString animal_name READ get_animal_name WRITE set_animal_name)
public:
explicit Animal(QObject *parent = 0);
QString get_animal_name();
void set_animal_name(QString p_name);
private:
QString animal_name;
};
#endif // ANIMAL_H
//File: animal.cpp
#include "animal.h"
Animal::Animal(QObject *parent) : QObject(parent)
{
}
void Animal::set_animal_name(QString p_name) {
animal_name=p_name;
}
QString Animal::get_animal_name() {
return animal_name;
}
//File: zoo.h
#ifndef ZOO_H
#define ZOO_H
#include <QObject>
class Animal;
class Zoo : public QObject
{
Q_OBJECT
public:
explicit Zoo(QObject *parent = 0);
Q_INVOKABLE Animal* get_animal_by_index(int index);
Q_INVOKABLE void add_animal(Animal *a);
Q_INVOKABLE void dump_animal_info(Animal *a);
Q_INVOKABLE void emit_signal();
signals:
void some_signal(Animal *animal_object);
public slots:
private:
QList<Animal*> animals;
};
#endif // ZOO_H
//File: zoo.cpp
#include <QDebug>
#include "zoo.h"
#include "animal.h"
Zoo::Zoo(QObject *parent) : QObject(parent)
{
Animal *a;
a=new Animal();
a->set_animal_name("Black Bear");
add_animal(a);
a=new Animal();
a->set_animal_name("Gray Wolf");
add_animal(a);
}
Animal* Zoo::get_animal_by_index(int index) {
return animals.at(index);
}
void Zoo::add_animal(Animal *a) {
animals.append(a);
}
void Zoo::dump_animal_info(Animal *a) {
qWarning() << "animal_name=" << a->get_animal_name();
}
void Zoo::emit_signal() {
Animal *a;
a=animals.at(0);
emit some_signal(a);
}
//File: main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "animal.h"
#include "zoo.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Zoo>("zoo",1,0,"Zoo");
qmlRegisterType<Animal>("zoo.animal",1,0,"Animal");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
//File: main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import zoo 1.0
import zoo.animal 1.0
ApplicationWindow {
visible: true
width: 640; height: 480; title: qsTr("Zoo")
Zoo {
id: zoopark
}
Column {
Button {
text: "Test C++ <--> QML data exchange by Method"
onClicked: {
var animal=zoopark.get_animal_by_index(1);
zoopark.dump_animal_info(animal);
}
}
Button {
text: "Text C++ <--> QML data exchage by Signal"
onClicked: {
zoopark.emit_signal();
}
}
}
Connections {
target: zoopark
onSome_signal: {
console.log('signal received');
console.log('from qml: animal name=' + animal_object.animal_name)
console.log('dumping animal info from c++:')
zoopark.dump_animal_info(animal_object)
}
}
function process_signal() {
console.log('from qml animal name=' + animal_object.animal_name)
zoopark.dump_animal_info(animal_object)
}
}

Correct way of threading in Qt

I have time consuming image loading (image is big), also some operations on it are done when loading. I do not want to block application GUI.
My idea is to load image in another thread, emit signal that image is loaded and then redraw view with this image.
My approach:
void Window::loadImage()
{
ImageLoader* loaderThread = new ImageLoader();
connect(loaderThread,SIGNAL(imageLoaded()),this,SLOT(imageLoadingFinished());
loaderThread->loadImage(m_image, m_imagesContainer, m_path);
}
void Window::imageLoadingFinished()
{
m_imagesContainer->addImage(m_image);
redrawView();
}
class ImageLoader : public QThread
{
Q_OBJECT
public:
ImageLoader(QObject *parent = 0) : m_image(NULL), m_container(NULL)
void loadImage(Image* img, Container* cont, std::string path)
{
m_image = img;
m_container = cont;
...
start();
}
signals:
void imageLoaded();
protected:
void run()
{
//loading image and operations on it
emit imageLoaded();
}
protected:
Image* m_image;
Container* m_container;
}
I was basing on quedcustomtype example from Qt writing this code. When googling and searching in stackoverflow I've also find out that subclassing QThread is not a good idea.
So the question is what is the correct way to do it? As I said I want non blocking GUI, loading and operations done in another thread and signal which says loading is finished. After signal is emited view should be redrawn.
I don't know much about multithreading however think to understand or have sufficient knowledge to understand basic ideas.
Use QtConcurent framework.
#include <QtConcurentRun>
#include <QFutureWatcher>
//....
class Window: public QWidget /*or something*/
{
//....
private:
QFutureWatcher<QImage> _wacther; //this object will signal when loading finished
};
//...
void Window::loadImage()
{
connect(&_watcher, SIGNAL(finished(), SLOT(finishLoading());
_wacther.setFuture(QtConcurent::run<QImage>(this, &Window::doLoadImage));
}
QImage Window::doLoadImage() //this function will be executed in the new thread. SHOULD BE Thread Safe
{
return someImage;
}
void window::finishLoading()
{
QImage result = _watcher.result();
}
I suppose this is the best way to go:
#include <QApplication>
#include <QLabel>
#include <QThread>
class ImageLoader : public QObject
{
Q_OBJECT
public:
ImageLoader() : QObject() {
moveToThread(&t);
t.start();
}
~ImageLoader() {
qDebug("Bye bye!");
t.quit();
t.wait();
}
void requestImage(QString absPath) {
QMetaObject::invokeMethod(this, "loadImage", Q_ARG(QString, absPath));
}
public slots:
void loadImage(QString absPath) {
// Simulate large image.
QImage image(absPath);
sleep(10);
qDebug("Image loaded!");
emit imageReady(image);
}
signals:
void imageReady(QImage image);
private:
QThread t;
};
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel() : QLabel() {}
void mousePressEvent(QMouseEvent* ev) {
Q_UNUSED(ev);
qDebug("I got the event!");
}
public slots:
void setImage(QImage image) {
setPixmap(QPixmap::fromImage(image));
resize(image.width(), image.height());
qDebug("Image shown!");
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyLabel label;
label.show();
ImageLoader imageLoader;
QObject::connect(&imageLoader, SIGNAL(imageReady(QImage)), &label, SLOT(setImage(QImage)));
imageLoader.requestImage(some_abs_path);
return a.exec();
}
#include "main.moc"
I also like QtConcurrent, but consider that its use is somehow discouraged: http://www.mail-archive.com/development#qt-project.org/msg07794.html.

Implementing c++ Callback using Qt Signal and Slot

I have created a library which has following interface and callback.
I wanted to Implent it using QT Signal and Slot.
Is is possible to replace IWifiCameraDiscovery with Signal and slot
class IWifiCameraDiscovery
{
public:
virtual int InitialiseWiFiDiscovery(IWifiCameraEnumerationCallback*) = 0;
virtual void UnInitialiseWiFiDiscovery() = 0;
virtual int EnumerateWiFiDevice() = 0;
};
class IWifiCameraEnumerationCallback
{
public:
virtual void onWiFiDeviceDiscovered( WiFiDeviceInfo* pDeviceInfo,unsigned short nNoOfDevice) = 0;
virtual void onDiscoveryTimeout() = 0;
};
Yes, in a quite straightforward way.
class IWifiCameraDiscovery
{
public:
virtual int InitialiseWiFiDiscovery() = 0;
virtual void UnInitialiseWiFiDiscovery() = 0;
virtual int EnumerateWiFiDevice() = 0;
signals:
void onWiFiDeviceDiscovered(WiFiDeviceInfo* pDeviceInfo, unsigned short nNoOfDevice);
void onDiscoveryTimeout();
};
In your implementation, instead of calling the callbacks directly:
callback.onWiFiDeviceDiscovered(pDevInfo, x);
You would emit a signal:
emit onWiFiDeviceDiscovered(pDevInfo, x);
And you would use QObject::connect to connect these signals to your actual receivers (slots) on the other end. Be aware that emitting a signal is more expensive than calling a virtual function.
try this simple example
#include <QApplication>
#include <QFont>
#include <QPushButton>
#include <QWidget>
class MyWidget : public QWidget
{
public:
MyWidget(QWidget *parent = 0);
};
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(200, 120);
QPushButton *quit = new QPushButton(tr("Quit"), this);
quit->setGeometry(62, 40, 75, 30);
quit->setFont(QFont("Times", 18, QFont::Bold));
connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget widget;
widget.show();
return app.exec();
}
you can use like this
class IWifiCameraDiscovery
{
public slots:
void UnInitialiseWiFiDiscovery();
signals:
void EnumerateWiFiDevice();
}