I have a class that performs a request to the network and parses the data. How do I properly implement the request abort for it?
Imagine that I have such a class:
class MyClass
{
public:
...
void doRequest()
{
m_reply = m_manager.get(...);
QEventLoop waitForResponse;
connect(m_reply, &QNetworkReply::finished, &waitForResponse, &QEventLoop::quit);
waitForResponse.exec();
// Check if request was aborted (otherwise, application will crash)
if (m_reply == nullptr)
return;
// Check for network errors, write result to m_data and delete m_reply;
...
}
void abort()
{
if (m_reply != nullptr)
m_reply->abort();
}
QString data()
{
return m_data;
}
...
private:
QNetworkAccessManager *m_manager;
QPiinter<QNetworkReply> m_reply;
QString m_data;
}
Here is an example of its use by pressing the button:
class MainWindow : public QMainWindow
{
...
private slots:
MainWindow::on_myButton_pressed()
{
m_myClass->abort();
m_myClass->doRequest();
ui->myTextEdit->setText(m_myClass->data());
}
private:
MyClass m_myClass;
}
When you press the button, if the previous request is not completed, then it is canceled. This works. But under the hood in this case a new request writing data into the QTextEdit and exit the function, then old request returning from it's own loop and writes the same m_data to QTextEdit again.
Is it corrent? Maybe there is a more correct way to implement this?
Nested event loops are the root of all evil. It is much simpler to write a function like doRequest without pretending to the user that it is a synchronous function. It seems that you have already traced the convoluted control-flow that happens when you call abort() and you understand how subsequent calls to doRequest() end up being nested calls due to re-entering the event loop. If you restart your request multiple times, your stack would look something like (the stack grows downwards):
1. main function
2. main event loop
3. [...] (Qt functions)
4. MainWindow::on_myButton_pressed()
5. MyClass::doRequest()
6. QEventLoop::exec()
7. [...] (Qt functions)
8. MainWindow::on_myButton_pressed()
9. MyClass::doRequest()
10. QEventLoop::exec()
11. [...] (Qt functions)
12. MainWindow::on_myButton_pressed() and so on...
Each one of the calls to MainWindow::on_myButton_pressed() need to call ui->myTextEdit->setText() when its nested event loop exits.
An alternative would be make your functions fully asynchronous and rely on signals/slots if you need things to be executed when a particular operation finishes. Here is a minimal implementation for what you are trying to achieve:
#include <QtNetwork>
#include <QtWidgets>
/// A class responsible for communication with the web backend
class Backend : public QObject {
Q_OBJECT
public:
explicit Backend(QObject *parent = nullptr)
: QObject(parent), m_reply(nullptr) {}
public slots:
void doRequest() {
// abort and delete ongoing request (if any)
if (m_reply) {
m_reply->abort();
delete m_reply;
m_reply = nullptr;
}
emit textUpdated(QString());
// send request
QUrl url("http://wtfismyip.com/text");
QNetworkRequest request(url);
m_reply = m_manager.get(request);
// when the request is finished,
QObject::connect(m_reply, &QNetworkReply::finished, [this] {
// if the request ended successfully, read the received ip into m_lastData
if (m_reply->error() == QNetworkReply::NoError)
m_lastData = QString::fromUtf8(m_reply->readAll());
// otherwise, emit errorOccured() signal (if the request has not been
// actively canceled)
else if (m_reply->error() != QNetworkReply::OperationCanceledError)
emit errorOccured(m_reply->errorString());
// in all cases, emit updateText and do cleanup
emit textUpdated(m_lastData);
m_reply->deleteLater();
m_reply = nullptr;
});
}
void abort() {
if (m_reply != nullptr)
m_reply->abort();
}
signals:
void textUpdated(const QString &);
void errorOccured(const QString &);
private:
QNetworkAccessManager m_manager;
QNetworkReply *m_reply;
QString m_lastData;
};
/// A minimal widget that contains a QPushButton and a QLabel
class Widget : public QWidget {
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr) : QWidget(parent) {
m_layout.addWidget(&m_pushButton);
m_layout.addWidget(&m_label);
connect(&m_pushButton, &QPushButton::clicked, this, &Widget::buttonClicked);
}
signals:
void buttonClicked();
public slots:
void updateText(const QString &text) { m_label.setText(text); }
void showError(const QString &error) {
QMessageBox::warning(this, tr("Error"), error);
}
private:
QVBoxLayout m_layout{this};
QPushButton m_pushButton{"Retrieve Name"};
QLabel m_label;
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Backend backend;
Widget widget;
// connect components
QObject::connect(&backend, &Backend::textUpdated, &widget,
&Widget::updateText);
QObject::connect(&backend, &Backend::errorOccured, &widget,
&Widget::showError);
QObject::connect(&widget, &Widget::buttonClicked, &backend,
&Backend::doRequest);
widget.show();
return a.exec();
}
#include "main.moc"
Related
how can I "link" a memory data ptr to a qt input field ?
data view is dynamically refreshed, if memory has changed.
memory data is overwritten if input has been done.
I had this function in fox gui toolkit (FXDataTarget class).
I would need the same/similar thing in qt.
I'm not saying it is the best solution, but a possible approach would be as follows:
Inherit a Qobject as a wrapper around your pointer.
Implement the valueChanged and setValue signal and slots and let them accept Qstrings.
Implement a member detectChange() that detects changes (if the array isn't too big store a full copy of the array to check againts). If a change is detected, let it raise the ValueChanged signal)
Set QTimer to call detectChange() periodically.
Connect The valueChanged signal of your object to the setValue slot of your text box and vice-versa.
finally, the code looks like this example, here coded only for an int * ptr.
class Updater : public QObject
{
Q_OBJECT
public:
Updater(int *);
QTimer *timer;
int myData_mem;
int *mydata;
public slots:
void setText(const QString &value);
void detectChange();
signals:
void hasChanged(const QString &value);
};
Updater::Updater(int *mydata_) : QObject()
{
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(detectChange()));
timer->start(500);
mydata = mydata_;
myData_mem = mydata_;
}
void Updater::detectChange()
{
if (*mydata != myData_mem)
{
myData_mem = *mydata;
emit hasChanged(QString::number(myData_mem));
}
}
void Updater::setText(const QString &value)
{
myData_mem = value.toInt();
*mydata = myData_mem;
}
int shared_data;
int main(int argc, char *argv[]) {
// your main qt code goes here
// ....
// and updater setup ,now :
QLineEdit *editor = new QLineEdit("0");
Updater updater(&shared_data);
QObject::connect(editor, SIGNAL(textChanged(const QString &)),
&updater, SLOT(setText(const QString &)));
QObject::connect(&updater, SIGNAL(hasChanged(const QString &)),
editor, SLOT(setText(const QString &)));
// ....
}
The core of my project is independent of GUI framework that's why I prefer std::thread. But Qt gives me an error when thread is using.
The inferior stopped because it received a signal from the operating system.
Signal name: SIGSEGV
Signal meaning: Segmentation fault
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <thread>
#include <mutex>
#include <QMainWindow>
namespace Ui { class MainWindow; }
struct Observer
{
virtual void notify() = 0;
};
class Core
{
public:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
void setObserver(Observer *observer) { _observer = observer; }
int ii() const { return _ii; }
void nextIi() { _ii++; }
void lock() { _mutex.lock(); }
bool tryLock() { return _mutex.try_lock(); }
void unlock() { _mutex.unlock(); }
private:
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
lock();
nextIi();
unlock();
notify();
}
}
}
void notify() { _observer->notify(); } //!!!
Observer *_observer;
int _ii;
std::mutex _mutex;
};
struct MwObserver : public Observer
{
explicit MwObserver(struct MainWindow *mainWindow) { _mainWindow = mainWindow; }
virtual void notify();
MainWindow *_mainWindow;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow() { delete _ui; }
void upd();
public slots:
void run() { _core.run(); }
private:
Ui::MainWindow *_ui;
MwObserver _observer;
Core _core;
};
inline void MwObserver::notify() { _mainWindow->upd(); }
#endif
-
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
}
void MainWindow::upd()
{
_core.lock();
setWindowTitle(QString::number(_core.ii()));
_core.unlock();
}
There are multiple problems here, first and most obvious was already noted by perencia. You are returning a pointer to stack variable. In c++ terms it's unacceptable.
Secondly. The crash comes from not using std::thread, but from race condition. The Qt event loop does not know about you mutex, so your setWindowTitle call is introducing a race, that leads to crash.
You need to use QMetaObject::invokeMethod to post function to the Qts event loop.
Example:
change
inline void MwObserver::notify() { _mainWindow->upd(); }
to
inline void MwObserver::notify() {
if(!QMetaObject::invokeMethod(_mainWindow, "upd", Qt::QueuedConnection))
std::cerr << " Failed to invoke method" << std::endl;
}
additional includes may apply
This updates the GUI from a thread different then the GUI thread! Which is not allowed.
Why not to use QThread and a signal/slot mechanism to update your window title. The Qt framework does the thread switching automatically.
class Core : public QObject
{
Q_OBJECT
public:
explicit Core(QObject * parent = 0) : QObject(parent) {}
signals:
void notify();
public slots:
void nextIi() { _ii++; }
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
nextIi();
notify();
}
}
}
private:
Q_DISABLE_COPY(Core);
int _ii;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void run() {_th.start();}
void upd(int ii) {setWindowTitle(QString::number(ii));}
private:
Ui::MainWindow *_ui;
Core _core;
QThread _th;
};
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
connect(&_core, SIGNAL(notify(int)), this, SLOT(upd(int)));
_core.moveToThread(&_th);
}
MainWindow::~MainWindow()
{
delete _ui;
_th.quit();
_th.wait(1000);
}
You are creating thread on the stack and returning a pointer to that. After run() that pointer is no longer valid.
Aside from returning pointer to stack variable and updating GUI from thread object that is not known for QT. I don't see from your code, where you set up _observer member of Core class. There is no setObserver call for _core member of MainWindow class.
So consructor of MainWindow class calls consructor of _core member, but after that _core._observer contains garbage. I think this is the cause of your Segmentaion Fault in call of notify method of Core class.
The answers to all the problems have already been given, let me summarize.
The program crash has nothing to do with the threading, The problem is that the _observer in the _core member of MainWindowis not set. A call to setObserver must be added.
explicit MainWindow( QWidget *parent = nullptr ) :
QMainWindow( parent ),
_observer( this )
{
_core.setObserver( &_observer );
}
This will lead to the next problem, that the observer actually calls the udp message from another thread, causing a UI update in a different thread context. To solve this, it is easiest to use Qt's Qt::QueuedConnection. To enable this we must make upt() a slot.
public slots:
void run();
void upd();
Then we can either call it using QMetaObject::invokeMethod in
inline void MwObserver::notify()
{
QMetaObject::invokeMethod( _mainWindow, "upd", Qt::QueuedConnection );
}
or use a signal / slot connection by deriving MwObserver from QObject, giving it a signal, and connect that signal to the upd slot and raising the signal in notify.
struct MwObserver
: public QObject
, public Observer
{
Q_OBJECT;
signals:
void sigUpd();
public:
explicit MwObserver( MainWindow *mainWindow );
virtual void notify()
MainWindow *_mainWindow;
};
void MwObserver::notify()
{
sigUpd();
}
MwObserver::MwObserver( MainWindow *mainWindow )
{
_mainWindow = mainWindow;
connect( this, SIGNAL(sigUpd()), _mainWindow, SLOT(upd()) )
}
Disclaimer: I haven't used Qt in some time but with X/XMotif on Linux/UNIX the GUI MUST run in the 'main-thread', not spawned threads. Maybe this applies to your situation. Just a thought, have your GUI code run in the main-thread.
The best approach is to wrap pure C++ code with QObejct instance and fire signals when this objects receive some notification from pure C++ code.
SO in your case:
class MwObserver : public QObject, public Observer
{
Q_OBJECT
public:
explicit MwObserver(QObject *parent)
: QObject(parent)
{}
signals:
void SomeEvent();
protected:
// Observer
void notify() {
emit SomeEvent();
}
};
Now MainWindow should connect some slot to signal provided this way and everything should work out of the box (Qt will do thread jumping behind the scenes).
In your code form comment the crash is caused by invalid use of temporary object. This is INVALID C++ code no mater what kind of object is returned:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
You cant return a pointer to local object of the function method, since this object becomes invalid immediately when you return a function. This is basic C++ knowledge.
I sticked to the tutorial about threaded qt-networking (which is here: http://doc.qt.io/qt-5/qtnetwork-threadedfortuneserver-example.html), I made some minor changes and integrated it into my main program. However incomingConnection() never gets executed, on the other hand the client is able to connect. Since I'd like to work with incomingConnection() it became obsolete to work with the SIGNAL(newConnection()) but even this isn't working.
Somebody knows what's going wrong?
Here my .h
#include <QtNetwork>
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
class WirelessNetThread: public Thread
{
Q_OBJECT
public:
WirelessNetThread(int socketDescriptor, QObject * parent);
void run() Q_DECL_OVERRIDE;
signals:
void error(QTcpSocket::SocketError socketError);
private:
int socketDescriptor;
QString text;
};
class WirelessNet : public QTcpServer
{
Q_OBJECT
public:
WirelessNet(QObject *parent = 0);
protected:
void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
};
And the .cpp
WirelessNetThread::WirelessNetThread(int socketDescriptor, QObject *parent):QThread(parent), socketDescriptor(socketDescriptor)
{
}
void WirelessNetThread::run()
{
QTcpSocket tcpSocket;
if ( !tcpSocket.setSocketDescriptor(socketDescriptor))
{
emit error(tcpSocket.error());
return;
}
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
}
WirelessNet::WirelessNet(QObject *parent): QTcpServer(0)
{
listen(QHostAddress::Any, 5220);
printf("is listening %d\n", this->isListening());
}
void WirelessNet::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "incomming \n";
printf("incomming \n");
WirelessNetThread *thread = new WirelessNetThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
here the excerpt out of my main program, where it is initiated (by the way it doesn't matter if I leave out moveToThread():
WirelessNet *wifi = new WirelessNet(this->parent());
wifi->moveToThread(this->thread());
Even this has no influence if I add these lines after the initalization of wifi:
wifi = new WirelessNet(this->parent());
QEventLoop testLoop;
testLoop.exec();
In other words "incomming" is never printed out, and so I'm not able to work on. Has anyone an idea, this is pretty much 1:1 the code from the tutorial that's what confuses me.
In your main code:
WirelessNet *wifi = new WirelessNet(0); // 0 = assign no parent
QThread *wifiThread = new QThread;
wifi->moveToThread(wifiThread);
QObject::connect(wifiThread, SIGNAL(started()), wifi, SLOT(startWifi()));
// start() will start its own event loop, it will emit started(), therefore startWifi() slot will be called.
wifiThread->start();
Then your WirelessNet class header:
class WirelessNet : public QTcpServer
{
Q_OBJECT
public:
WirelessNet(QObject *parent = 0);
protected:
void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
public slots:
void startWifi();
};
Then your WirelessNet class body:
WirelessNet::WirelessNet(QObject *parent) :
QTcpServer(parent)
{
// Do nothing much here because we want to initialise new stuff in our thread.
// When this function runs we have not moved this to the new thread - or even started it.
}
void WirelessNet::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "incomming \n";
printf("incomming \n");
WirelessNetThread *thread = new WirelessNetThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
// Called when the thread has started
void WirelessNet::startWifi()
{
// Anything done here is now safely within out new thread.
listen(QHostAddress::Any, 5220);
printf("is listening %d\n", this->isListening());
}
note this is example code, I wrote it directly into stack overflow it has not been compiled, so there are probably some errors :) There are some key points, that I have commented, where you may have gone wrong in your original attempt.
My problem is the following: I need to create class, which contains QStateMachine instance. This class should have slots through which you could "ask" state machine to make transition to another state. And if transition was successful, my class should emit signal about it. How would I implement this? Class should have ability to emit certain signals according to certain slot invoke.
Here is a small example of class:
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0)
{
mStateMachine = new QStateMachine(this);
QState *s1 = new QState(mStateMachine);
QState *s2 = new QState(mStateMachine);
QState *s3 = new QState(mStateMachine);
s1->addTransition(); // Transition to s2
s2->addTransition(); // Transition to s3
s3->addTransition(); // Transition to s1
mStateMachine->setInitialState(s1);
mStateMachine->start();
}
signals:
toS1();
toS2();
toS3();
public slots:
slotToS1()
{
/* post event to state machine about
transition to state s1,
if transition was successful,
then emit toS1() signal. */
};
slotToS2(){ /* Similar to slotToS1 */};
slotToS3(){ /* Similar to slotToS1 */};
private:
QStateMachine *mStateMachine;
}
I would be very grateful for your help!
UPD:
The slots are representing defferent kinds of transitions, so that outer class (that will be using MyClass) could 'ask' for some transition. So, the slot send event or signal to state machine, it looks on event or signal and (if in right state) makes this transition. And I want to notify outer class with certain signal, that asked before slot (transition) was made successfuly.
To transition on a slot call, you need to somehow bind the slot to a QAbstractTransition. There are two ways of doing it:
Use a QEventTransition and send a relevant event to trigger it.
Use a QSignalTransition and use an internal signal to trigger it.
To emit signals on state transitions, you can connect the QAbstractTransition::triggered or QState::entered or QState::exited signals to other signals. Remember, in Qt a connection target can be either a slot or a signal.
Thus, using signal transitions:
class MyClass : public QObject
{
Q_OBJECT
QStateMachine machine;
QState s1, s2;
Q_SIGNAL void s_go_s1_s2();
Q_SIGNAL void s_go_s2_s1();
public:
Q_SIGNAL void transitioned_s1_s2();
Q_SIGNAL void transitioned_s2_s1();
Q_SLOT void go_s2_s1() { emit s_go_s2_s1(); }
Q_SLOT void go_s1_s2() { emit s_go_s1_s2(); }
explicit MyClass(QObject *parent = 0) : QObject(parent),
s1(&machine), s2(&machine) {
auto s1_s2 = s1.addTransition(this, SIGNAL(s_go_s1_s2()), &s2);
auto s2_s1 = s2.addTransition(this, SIGNAL(s_go_s2_s1()), &s1);
machine.setInitialState(&s1);
machine.start();
connect(s1_s2, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s1_s2);
connect(s2_s1, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s2_s1);
}
}
Using event transitions is a bit harder, since the events you're using must be cloneable by the state machine. The core module's state machine only knows how to clone the None and Timer events - see its cloneEvent implementation.
The widgets module adds support for various GUI/Widgets events - see the cloneEvent implementation there. You could, in a pinch, use such GUI events for your own purposes - after all, they are sent to a plain QObject that doesn't interpret them in a special way.
You can provide your own cloneEvent implementation that links with the others.
#include <private/qstatemachine_p.h>
class MyClass : public QObject
{
Q_OBJECT
QStateMachine machine;
QState s1, s2;
QEvent e_s1_s2, e_s2_s1;
QEventTransition s1_s2, s2_s1;
public:
Q_SIGNAL void transitioned_s1_s2();
Q_SIGNAL void transitioned_s2_s1();
Q_SLOT void go_s2_s1() { QCoreApplication::sendEvent(this, &e_s2_s1); }
Q_SLOT void go_s1_s2() { QCoreApplication::sendEvent(this, &e_s1_s2); }
explicit MyClass(QObject *parent = 0) : QObject(parent),
s1(&machine), s2(&machine),
e_s1_s2((QEvent::Type)(QEvent::User + 1)),
e_s2_s1((QEvent::Type)(QEvent::User + 2)),
s1_s2(this, e_s1_s2.type()),
s2_s1(this, e_s2_s1.type()) {
s1_s2.setTargetState(&s2);
s2_s1.setTargetState(&s1);
s1.addTransition(&s1_s2);
s2.addTransition(&s2_s1);
machine.setInitialState(&s1);
machine.start();
connect(&s1_s2, &QAbstractTransition::triggered, this, &MyClass::transitioned_s1_s2);
connect(&s2_s1, &QAbstractTransition::triggered, this, &MyClass::transitioned_s2_s1);
}
}
static const QStateMachinePrivate::Handler * last_handler = 0;
static QEvent * cloneEvent(QEvent * e) {
if (e->type() >= QEvent::User && e->type() < QEvent::User+100) {
return new QEvent(e->type());
return last_handler->cloneEvent(e);
}
const QStateMachinePrivate::Handler our_handler = {
cloneEvent
};
void registerHandler() {
last_handler = QStateMachinePrivate::handler;
QStateMachinePrivate::handler = &our_handler;
}
Q_CONSTRUCTOR_FUNCTION(registerHandler())
void unregisterHandler() {
QStateMachinePrivate::handler = last_handler;
}
Q_DESTRUCTOR_FUNCTION(unregisterHandler())
I have had the same problem in the past and I have found the easiest way was to inherit from QState with your own QState class and implement 2 methods called QState::onEntry(QEvent * event) and QState::onExit(QEvent * event).
This way you are able to emit any signal you like when you exit and when you enter a new state.
Here is and example:
file mystate.h
#include <QState>
class MyState : public QState
{
Q_OBJECT
public:
explicit MyState(qint32 stateId, QState * parent = 0);
protected:
void onEntry(QEvent * event);
void onExit(QEvent * event);
signals:
void exit(qint32 stateId);
void enter(qint32 stateId);
private:
qint32 stateId;
};
And file mystate.cpp
#include "mystate.h"
MyState::MyState(qint32 stateId, QState *parent)
{
this->stateId = stateId;
}
void MyState::onEntry(QEvent *event)
{
emit enter(stateId);
}
void MyState::onExit(QEvent *event)
{
emit (exit(stateId));
}
I'm new to Qt, C++ and signals and slots. I'm trying to load in a webpage. Then set a label_3's text to the title of the webpage. To do this I figured I had to connect the loadFinished signal to my custom function. But I'm having trouble doing just that.
I've read up on the manual, different examples and other questions, but I'm stuck.
This is a excerpt from code I have so far.
How do I properly connect the signal loadFinished() to my function labelSetText()?
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
QString webAdress = ui->lineEdit->text();
QWebView *view = ui->webView;
view->load(QUrl(webAdress));
QString taxt = view->title();
connect(&view, SIGNAL(loadFinished(bool)),
this, SLOT(labelSetText(taxt)));
QWebPage * webPage = view->page();
}
void MainWindow::labelSetText(QString titleStr)
{
ui->label_3->setText(titleStr);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
namespace Ui {
class MainWindow;
}
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void labelSetText(QString titleStr);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
EDIT:
This is the error I get
E:\_Programming\C++\playAround\mainwindow.cpp:37: error: no matching function for call to 'MainWindow::connect(QWebView**, const char*, MainWindow* const, const char*)'
this, SLOT(labelSetText(taxt)));
^
That's not how connections work. A signal-slot connection can only pass data from the signal into the slot. It can't pass arbitrary variables like you do. The only way you could write your connect statement is as follows (the this argument is unnecessary):
connect(view, SIGNAL(loadFinished(bool)), SLOT(labelSetText(QString)));
This of course doesn't work, because the signal and slot are incompatible. You of course don't need the intermediate slot, since a label already has the slot you want, but it doesn't help:
connect(view, SIGNAL(loadFinished(bool)), ui->label_3, SLOT(setText(QString)));
Note that you should not have connect(&view, ... since view is already a pointer-to-QObject.
To do it, you need to leverage C++11:
connect(view, &QWebView::loadFinished, [=,this](){
this->ui->label_3->setText(taxt);
});
The lambda syntax translates into a functor class instance with copies of taxt and this as members. The compiler essentially creates the following, on the fly:
class Functor_1 {
MainWindow * _this;
QString taxt;
public:
MyFunctor_1(MainWindow * a1, const QString & a2) : _this(a1), taxt(a2) {}
void operator() {
_this->ui->label_3->setText(taxt);
}
}
...
connect(view, &QWebView::loadFinished, Functor_1(this, taxt));
Of course this means that if you want to use Qt 4 signals and slots, you need to add the taxt member to your MainWindow class, and create a slot to do what the functor does. So, for Qt 4:
class MainWindow : public QMainWindow {
Q_OBJECT
QString m_taxt;
Q_SLOT void loadFinished() {
ui->label_3->setText(m_taxt);
}
...
Q_SLOT void on_pushButton_clicked() {
QString webAdress = ui->lineEdit->text();
QWebView *view = ui->webView;
view->load(QUrl(webAdress));
m_taxt = view->title();
connect(view, SIGNAL(loadFinished(bool)), SLOT(loadFinished());
...
}
};
Note that you shouldn't be connecting repeatedly. For Qt 4 style connection, move the connect to MainWindow's constructor. For Qt 5 style connection, you need to break the connection once it fires.