I have two classes, MyClass and Widget. Below is the MyClass class and from my Widget class i want to get the str variable. How is that done?
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
void fetch();
public slots:
void replyFinished(QNetworkReply*);
private:
QNetworkAccessManager* m_manager;
};
MyClass::MyClass()
{
m_manager = new QNetworkAccessManager(this);
connect( m_manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
}
void MyClass::fetch()
{
m_manager->get(QNetworkRequest(QUrl("http://stackoverflow.com")));
}
void MyClass::replyFinished(QNetworkReply* pReply)
{
QByteArray data=pReply->readAll();
QString str(data);
//this str should be available in my widget class
}
EDIT: Here is a the important part of the widget
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//here str should be accessed
}
If you want the str variable from your function available to classes or other functions, here are two choices:
Return it from the function.
Declare a variable in MyClass to hold the string and set the
variable to the value.
Case 1: Returning from a function
QString MyClass::replyFinished(...)
{
QString str(data);
return data;
}
Case 2: Declare a class member variable
class MyClass
{
public:
QString m_replyStr;
};
//...
void MyClass::replyFinished(...)
{
QByteArray data = pReply->readAll();
m_replyStr = data;
}
Modifying your question with an example of what you want to do would be very helpful.
You can emit a signal with str as argument and connect it to a slot in your widget. Then you can do what you want with it.
This way you will preserve the event oriented design and you have not need to control if str exists. Simply when it's ready the slot will handle it.
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
void fetch();
public slots:
void replyFinished(QNetworkReply*);
signals:
void strReplyReady(QString str);
private:
QNetworkAccessManager* m_manager;
};
...
void MyClass::replyFinished(QNetworkReply* pReply)
{
QByteArray data=pReply->readAll();
QString str(data);
emit strReplyRead(str);
}
your Widget
class MyWidget : public QWidget
{
//public members
...
public slots:
void readReply(QString str);
}
void MyWidget::readReply(QString str){
//do what you want with str
}
in the main.cpp you do the connect with the static member of QObject
QObject::connect(myClassPointer,SIGNAL(strReplyReay(QString)) ,
myWidgetPointer,SLOT(readReply(QString)));
Related
I'm using the MVC pattern and I'm trying to connect a signal from my view class with my controller class that inherits QObject
class View : public QWidget
{
Q_OBJECT
private:
Controller* controller;
QPushButton* startButton;
void addControls(QVBoxLayout* mainLayout);
public:
explicit View(QWidget *parent = nullptr);
void setController(Controller* c);
};
#endif // VIEW_H
This are the methods
void View::addControls(QVBoxLayout *mainLayout)
{
//I'm adding the button
}
View::View(QWidget *parent) : QWidget(parent)
{
//Layout
}
void View::setController(Controller *c){
controller = c;
connect(startButton, SIGNAL(clicked()), controller, SLOT(begin()));
//ERROR controller is a Controller* and it can't be converted it to const QObject*
}
And this is the Controller class
class Controller : public QObject
{
Q_OBJECT
private:
QTimer* timer;
View* view;
Model* model;
public:
explicit Controller(QObject *parent = nullptr);
~Controller();
void setModel(Model* m);
void setView(View* v);
public slots:
void begin() const;
};
#endif // CONTROLLER_H
And the methods
Controller::Controller(QObject *parent):
QObject(parent), timer(new QTimer)
{
connect(timer, SIGNAL(timeout()), this, SLOT(next()));
}
Controller::~Controller() { delete timer; }
void Controller::setModel(Model* m) { model = m; }
void Controller::setView(View* v) { view = v; }
void Controller::begin() const {
timer->start(200);
}
For good measure, here is the main where I set each component
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
View w;
Controller c;
Model m;
c.setModel(&m);
c.setView(&w);
w.setController(&c);
w.show();
return a.exec();
}
I've tried everything I could think of, can't make it work..
Stop using the terrible text-based slot connection interface.
Use the modern one that gives nice compile errors:
QObject::connect(startButton, &QPushButton::clicked, controller, &Controller::begin);
I need some advice to access the field(QString name) variable in QWizardPage from a QThread. I'm building some kind of an installer and I want to do the installing work in a separate Thread.
My purpose:
When reached the commit/install page, I want to execute code to do the "installing" and update the QWizardPage with my progress, until its finished.
The install function is dependent on many field() variables from other QWizardPages. Therefore I tried to execute this install function from a QThread, which is defined in an inner class from my QWizardPage. The problem is, the field()-function i a non-static member and so it's not working. And so I'm out of ideas to run my install-function parallel to my WizardPage.
I tried something like this:
InstallPage.h
class InstallPage : public QWizardPage
{
Q_OBJECT
class WorkerThread : public QThread
{
Q_OBJECT
void run() override;
};
public:
InstallPage(QWidget *parent = 0);
private:
QLabel *lProgress;
WorkerThread *installer;
void install();
};
InstallPage.c
InstallPage::InstallPage(QWidget *parent)
: QWizardPage(parent)
{
...
installer = new WorkerThread(this);
installer->start();
}
void InstallPage::WorkerThread::run()
{
if(field("checkBox1").ToBool())
{
doStuff();
}
}
//QT-Creator says at field("checkBox1"):
//error: call to non-static member function without an object argument
I'm also open for any other idea to make my installer work. Maybe someone knows something I haven't thought of.
Another approach is to create a worker (QObject) that lives in another thread that performs the heavy task and notifies the status of that task through signals:
#include <QtWidgets>
class InitialPage: public QWizardPage
{
public:
InitialPage(QWidget *parent = nullptr): QWizardPage(parent)
{
QSpinBox *spinbox = new QSpinBox;
QLineEdit *lineedit = new QLineEdit;
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(spinbox);
lay->addWidget(lineedit);
registerField("value1", spinbox);
registerField("value2", lineedit);
}
};
class InstallWorker: public QObject
{
Q_OBJECT
public:
InstallWorker(QObject *parent=nullptr): QObject(parent)
{
}
public Q_SLOTS:
void install(int param1, const QString & param2)
{
Q_EMIT started();
for(int i=0; i < 100; i++){
qDebug() << __PRETTY_FUNCTION__ << i << param1 << param2;
QThread::msleep(100);
Q_EMIT progressChanged(i);
}
qDebug()<< __PRETTY_FUNCTION__ << "finished";
Q_EMIT finished();
}
Q_SIGNALS:
void started();
void progressChanged(int value);
void finished();
};
class InstallPage: public QWizardPage
{
Q_OBJECT
public:
InstallPage(QWidget *parent = nullptr): QWizardPage(parent),
label(new QLabel), progressbar(new QProgressBar)
{
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(label);
lay->addWidget(progressbar);
progressbar->setMinimum(0);
progressbar->setMaximum(100);
thread = new QThread(this);
worker.moveToThread(thread);
connect(&worker, &InstallWorker::started, this, &InstallPage::onStarted);
connect(&worker, &InstallWorker::finished, this, &InstallPage::onFinished);
connect(&worker, &InstallWorker::progressChanged, this, &InstallPage::onProgressChanged);
thread->start();
}
~InstallPage(){
thread->quit();
thread->wait();
}
void initializePage(){
start_install();
}
private Q_SLOTS:
void start_install(){
int param1 = field("value1").toInt();;
QString param2 = field("value2").toString();
QMetaObject::invokeMethod(&worker, "install", Qt::QueuedConnection, Q_ARG(int, param1), Q_ARG(QString, param2));
}
void onStarted(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(false);
}
void onFinished(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(true);
wizard()->next();
}
void onProgressChanged(int value){
progressbar->setValue(value);
label->setNum(value);
}
private:
InstallWorker worker;
QThread *thread;
QLabel *label;
QProgressBar *progressbar;
};
class FinalPage: public QWizardPage
{
public:
FinalPage(QWidget *parent = nullptr): QWizardPage(parent)
{
}
};
#include "main.moc"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWizard wizard;
wizard.addPage(new InitialPage);
wizard.addPage(new InstallPage);
wizard.addPage(new FinalPage);
wizard.show();
return app.exec();
}
This is my Wavefrontrenderer class.
Class WavefrontRenderer : public QMainWindow , private
Ui::WavefrontRendererClass
{
Q_OBJECT
//Q_PROPERTY( QColor m_color READ m_color NOTIFY colorChanged)
public:
WavefrontRenderer(TreeModel* model , QWidget *parent = Q_NULLPTR);
void iterate(const QModelIndex & index, const QAbstractItemModel * model);
void render();
void RenderTreeElement(QModelIndex index);
private:
//Ui::WavefrontRendererClass ui;
TextureManager textureManager;
TextureManagerCubeMap textureManagerCubeMap;
QColor m_color;
void FillComboBox();
private slots:
void PositionXYZ();
};
/////////////////////////////////////////////////////////////////////////////////
WavefrontRenderer::WavefrontRenderer(TreeModel* model , QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
treeView->setModel(model);
treeView->setDragEnabled(true);
treeView->setAcceptDrops(true);
treeView->installEventFilter(this);
connect(doubleSpinBoxPositionX, SIGNAL(valueChanged(double)), this ,
SLOT(PositionXYZ()));
}
/////////////////////////////////////////////////////////////////////////////////
I am creating controls at runtime from another class to which i pass the Wavefrontrenderer class as a pointer.
void Container::CreateUI(QHBoxLayout* layout)
{
wavefrontrenderer // Pointer defined as a private member
QDoubleSpinBox *PositionXSpinBox = new QDoubleSpinBox;
PositionXSpinBox->setRange(-10000, 10000);
PositionXSpinBox->setSingleStep(.1);
PositionXSpinBox->setValue(x);
layout->addWidget(PositionXSpinBox);
bool ok = QObject::connect(PositionXSpinBox,
SIGNAL(valueChanged(double)), wavefrontrenderer , SLOT(print()));
qDebug() << "The slot is connected =" << ok;
}
I have defined print as a public slot member in Container class.
The issue is that the connect tries to locate print() function in WavefrontRenderer class rather than Container class.
How can i make connect call the print() funtion of the Container class.
I have a slot that receives a QString, and I want to assign the value for this parameter in another variable, or use it directly in another method of the same class.
The signal is emitted from some other class.
MyWidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include"myclass.h"
#include <QWidget>
#include <QtWidgets>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
private:
Ui::MyWidget *ui;
signals:
void redirectData(QString data);
public slots:
void sendData();
private:
MyClass *myClass; // the object to receive and output the data
};
#endif // MYWIDGET_H
MyWidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myClass = new MyClass();
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(sendData()));
connect(this, SIGNAL(redirectData(QString)), myClass, SLOT(outputData(QString)));
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::sendData()
{
emit redirectData(ui->lineEdit->text());
myClass->show();
}
MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QWidget>
namespace Ui {
class MyClass;
}
class MyClass : public QWidget
{
Q_OBJECT
public:
explicit MyClass(QWidget *parent = 0);
~MyClass();
QString x;
private:
Ui::MyClass *ui;
signals:
void send2(QString);
public slots:
void outputData(QString data);
};
#endif // MYCLASS_H
MyClass.cpp
#include "myclass.h"
#include "ui_myclass.h"
MyClass::MyClass(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyClass)
{
ui->setupUi(this);
ui->plainTextEdit->insertPlainText(x); // show x in plainTextEdit
}
MyClass::~MyClass()
{
delete ui;
}
void MyClass::outputData(QString data){
x=data; // affect data a x
}
I want to display the value of x in the QPlainTextEdit.
This method is correct but does not fit with the rest of my code. I would like to preserve the value in x.
MyClass.cpp
#include "myclass.h"
#include "ui_myclass.h"
MyClass::MyClass(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyClass)
{
ui->setupUi(this);
}
MyClass::~MyClass()
{
delete ui;
}
void MyClass::outputData(QString data){
ui->plainTextEdit->insertPlainText(data);
}
This now an OK question, if a tad wordy and basic. It is clearly mostly complete (the .ui files are missing - but don't include them!) and reproducible.
Thus, there are two things that you can do:
Simply set the value of the plain text edit in the slot:
void MyClass::outputData(QString data){
x = data;
ui->plainTextEdit->insertPlainText(x);
}
You don't need to store the value in x at all, if you don't need it anywhere else in MyClass.
If MyClass should expose the x property to interact with other classes, you should make it so - and then use the notification signal to modify the widget:
class MyClass : public QWidget {
Q_OBJECT
Q_PROPERTY(QString x READ x WRITE setX NOTIFY xChanged)
QString m_x;
Ui::MyClass *ui;
public:
explicit MyClass(QWidget *parent = {}) {
ui->setupUi(this);
connect(this, &MyClass::xChanged, ui->plainTextEdit, &QPlainTextEdit::insertPlainText);
~MyClass {}
Q_SIGNAL void xChanged(const QString &);
Q_SLOT void setX(const QString &x) {
if (m_x != x) {
m_x = x;
emit xChanged(m_x);
}
}
QString x() const { return m_x; }
};
Other notes
The MyClass::x member should not be public.
The containers passed as method arguments should be almost always of a const reference type - to avoid the premature pessimization of making copies of objects passed. Thus you should modify the signatures as follows - in both declarations (header files) and definitions (implementation - .cpp files):
void MyClass::send2(const QString&);
void MyClass::outputData(const QString &data)
void MyWidget::redirectData(const QString &data)
The old-style connect syntax you use demands normalized signatures and doesn't change - the const and reference must be dropped.
There's no need to store the ui by pointer - simply store it by value. The C++ language then automatically takes care of memory management for you, and the default compiler-generated destructor does the job.
In self-contained examples, there's no need to use the .ui files. Creating the widgets manually and laying them out is quite trivial, as you can see below.
A reworked example
Taking all this into account, this would be a self-contained demo - with no Ui files needed. Your question should have had something like this included - it's self contained, single file. The use of the properties is not undesirable to indicate intent: those are the changeable properties of the classes.
#include <QtWidgets>
class Source : public QWidget {
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
QHBoxLayout m_layout{this};
QLineEdit m_edit;
QPushButton m_send{tr("Send")};
QString m_text;
public:
Source(QWidget *parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_edit);
m_layout.addWidget(&m_send);
connect(&m_send, &QPushButton::clicked, [this]{
setText(m_edit.text());
});
}
Q_SLOT void setText(const QString &text) {
if (text == m_text) return;
m_text = text;
emit textChanged(m_text);
}
QString text() const { return m_text; }
Q_SIGNAL void textChanged(const QString &);
};
class Destination : public QWidget {
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
QHBoxLayout m_layout{this};
QPlainTextEdit m_edit;
QString m_text;
public:
Destination(QWidget *parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_edit);
connect(this, &Destination::textChanged, &m_edit, &QPlainTextEdit::setPlainText);
}
Q_SLOT void setText(const QString &text) {
if (text == m_text) return;
m_text = text;
emit textChanged(m_text);
}
QString text() const { return m_text; }
Q_SIGNAL void textChanged(const QString &);
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
Source src;
Destination dst;
QObject::connect(&src, &Source::textChanged, &dst, [&](const QString &text){
if (!dst.isVisible()) {
dst.move(src.frameGeometry().topRight()); // align the windows
dst.show();
}
dst.setText(text);
});
src.show();
return app.exec();
}
#include "main.moc"
I have 2 files Gui.cpp, ReadData.cpp (and headers). I need to access the QComboBox member in the Gui.cpp from ReadData.cpp. When I do this, the code compiles fine but when I run my application, it crashes, saying Access Violation . What am I doing wrong ?
How can I access the QComboBox in Gui.cpp from readdata ?
Gui.h
class Gui : public QObject
{
Q_OBJECT
public:
Gui() {}
~Gui() {}
QComboBox* comboBox;
ReadData* r;
private:
void run();
}
Gui.cpp
void Gui::run()
{
QWidget *w = new QWidget();
comboBox = new QComboBox();
//code...
r = new ReadData();
THROW_IF_FALSE(QObject::connect(loadButton, SIGNAL(clicked()), r, SLOT(read())));
}
readdata.h
class ReadData : public QObject
{
Q_OBJECT
public:
ReadData() {}
~ReadData() {}
public slots :
void read();
private:
void displayResult(QString arg);
};
readdata.cpp
void ReadData::read()
{
Gui cb;
QString str1 = cb.comboBox->currentText();
}