Reputation: 1
the runnable project is here: enter link description here
I sincerely glad to have your detail answers to solve this, but I am still confusing on this issue:
case 1: changing socket_session
as a member variable of mainwindow
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
SocketThread* socket_session;
private:
...
But this is not the solution to access setFlag
, even after I change the `Form1::on_qpushButton__set_white_level_0_clicked()' function like this:
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
socket_session->setThreadFlag(true);
}
Still it doesn't make sense because form1
instance doesn't have "the" instance of socket_thread
which has been instantiated from mainwindow
.
There's a solution I think is making another class that includes all instances that I want to use from inside of mainwindow
but I don't think that is a good one because I am using thread and accessing a global big instance class that includes all of them to be "shared" is not a good idea for someone like me.
#include <form1.h>
#include <ui_form1.h>
#include "socketthread.h"
Form1::Form1(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form1) {
ui->setupUi(this);
}
Form1::~Form1() {
delete ui;
}
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
socket_session->setThreadFlag(true);
}
I know I am lack of understanding about this but, do I wanna make something nobody does...? I think everyone wants to separate all objects and their methods clearly and communicate via signals or calling functions from delivered object instances...
case 2: ... let me try how you suggested make possible first...
I can read C++ code and overall structure, but I don't know why I have to struggle with this, so please help me, dear Guru.
On socketthread.h :
class SocketThread : public QThread {
Q_OBJECT
public:
QTcpSocket *socket_session;
SocketThread();
~SocketThread(){}
bool connectToServer(QString, int);
void sendData(const char*, int, int);
void run(void);
private:
QString message;
volatile bool threadFlag;
signals:
void changedThreadFlag(void);
void changedMessageStr(void);
void setThreadFlag(bool);
void setMessageStr(QString);
private slots:
void setStr(QString);
void setFlag(bool);
void socketError(QAbstractSocket::SocketError);
};
And its implementation is...
SocketThread::SocketThread() {
socket_session = NULL;
threadFlag = false;
message = "NULL";
connect(this, SIGNAL(setThreadFlag(bool)), this, SLOT(setFlag(bool)));
}
...
void SocketThread::setStr(QString str) {
message = str;
}
void SocketThread::setFlag(bool flag) {
threadFlag = flag;
}
void SocketThread::run() {
while(true) {
if(threadFlag) {
QThread::msleep(100);
qDebug() << message;
} else
break;
}
qDebug() << "loop ended";
}
And I have one form which has a button, and I put a clicked() slot of it like this...
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
--how can I emit the signal of the one of socketthread from here??
}
Now, the mainwindow is like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
QString addr_server = "223.194.32.106";
int port = 11000;
SocketThread* socket_session = new SocketThread();
socket_session->connectToServer(addr_server, port);
ui->setupUi(this);
Form1* form1;
form1 = new Form1();
ui->stackedWidget_mainwindow->addWidget(form1);
ui->stackedWidget_mainwindow->setCurrentWidget(form1);
socket_session->run();
...
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
Once the socket_session->run()
started, I need to change the threadFlag
by clicking the button by emitting setThreadFlag()
of one's from the running thread. And I just stuck in here.
Does it possible even? Or am I doing this all wrong from the beginning?
Upvotes: 0
Views: 638
Reputation: 20141
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
No signal is needed – just call the function.
void Form1::on_qpushButton__set_white_level_0_clicked() {
qDebug() <<"clicked()";
// --how can I emit the signal of the one of socketthread from here??
// E.g. this way:
socket_session->setThreadFlag(true);
}
To make this possible, another fix is needed:
socket_session
is a local variable in OP's exposed code.
To make it "persistent", it has to become e.g. a member variable.
So, the constructor MainWindow::MainWindow()
has to be changed:
// Nope: SocketThread* socket_session = new SocketThread();
// Instead:
socket_session = new SocketThread();
and SocketThread* socket_session;
has to be added to member variables of class MainWindow
.
To make it accessible in Form1
, it has to be passed to Form1
as well.
This could be done e.g. by making it a member variable in Form1
also which is initialized with a constructor argument (or set from MainWindow
afterwards).
(I must admit that I never have used the Qt UI builder QtDesigner but build all my UIs by C++ code exclusively.)
But, now, another fix is necessary:
volatile
doesn't make a variable suitable for interthread communication.
(This was used in ancient times before multi-threading started to be supported by C++11.)
However, this is wrong: Is volatile useful with threads?
An appropriate fix would be to use std::atomic
instead:
// Wrong for interthread-com.
//volatile bool threadFlag;
// Correct:
std::atomic<bool> threadFlag; // #include <atomic> needed
FYI: SO: Multithreading program stuck in optimized mode but runs normally in -O0
And, finally, in SocketThread::SocketThread()
:
connect(this, SIGNAL(setThreadFlag(bool)), this, SLOT(setFlag(bool)));
is not necessary in this case.
SocketThread::setThreadFlag()
could call SocketThread::setFlag()
directly, or even write threadFlag
itself:
void setThreadFlag(bool flag) { threadFlag = flag; }
As I (recommended to) make threadFlag
atomic, it can be accessed from any thread without causing a data race.
Update:
After OP has updated the question:
I just simply want to emit the signal setThreadFlag of the socketthread from inside of QPushbutton_clicked() slot.
The button (created from UI Form1
) can be connected in the MainWindow
as well (without using any method of Form1
):
QObject::connect(form1->button1, &QPushButton::clicked,
socket_session, &SocketThread::setThreadFlag,
Qt::QueuedConnection);
Notes:
About form1->button1
, I'm not quite sure.
I noticed that widgets in UI generated forms can be accessed this way but I don't know the exact details (as I never used the Qt UI builder on my own).
I used the Qt5 style of QObject::connect()
.
This is what I would recommend in any case.
The Qt5 style is verified at compile time. –
Wrong connections are detected by the C++ type checking.
Additionally, any function with matching signature can be used – no explicit exposure of slots is anymore necessary.
Even conversion of non-matching signature or adding additional parameters becomes possible by using C++ lambdas which are supported as well.
Qt: Differences between String-Based and Functor-Based Connections
It is possible to connect signals and slots of distinct threads.
I used Qt::QueuedConnection
to remark this as interthread communication.
(However, I roughly remember that Qt might be able to detect it itself.
See the doc. for Qt::AutoConnection
which is the default.
Further reading: Qt: Signals & Slots
Btw. using the Qt signals for inter-thread communication would exclude the necissity to make SocketThread::threadFlag()
atomic. It could become a simple plain bool threadFlag;
instead. The slot SocketThread::setThreadFlag()
is called in the Qt event loop of QThread
, in this case.
Upvotes: 0
Reputation: 409404
As mentioned in this post:
"Emitting a signal" == "calling a function"
So all you really have to do is call the signal function, and all connected slots should be called.
This of course means that the Form1
object needs a pointer to the thread object, i.e. it needs a copy of socket_session
. Then you can simply call the signal on the object
socket_session->setThreadFlag(your_flag);
Of course, if the Form1
have a copy of the socket_session
pointer, it might as well call setFlag
directly, if it was public
.
Upvotes: 1