0

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);

}

enter image description here

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...

the name of the button


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?

2 Answers2

1

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

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:

  1. 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).

  2. 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

  3. 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.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • I sincerely glad to have your detail answer and fix for this, but still I am confusing on this matter: ``` 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); } ```In order to make this possible, Form1 should know have an instance of socket_session which was instantiated from the mainwindow, that's what I don't know now. How can I pass the socket_session instance to this function? – Sinwoo Yoo Jan 14 '20 at 07:51
  • Even after changing `socket_session` as a member variable, still I am having this error ``` form1.cpp: In member function 'void Form1::on_qpushButton__set_white_level_0_clicked()': form1.cpp:18:5: error: 'socket_session' was not declared in this scope socket_session->setThreadFlag(true); ^ make: *** [form1.obj] Error 1 ``` without including socket_session in the form1.cpp. I am looping on this matter now... – Sinwoo Yoo Jan 14 '20 at 07:51
  • Got it: I oversaw that `Form1` but was thinking the button is a member of the `MainWindow` (which it could be as well). In this case, you have to pass the pointer `socket_session` to the instance of `Form1`. To answer this more precisely, I would've to read the ful code. (That's why, it's strictly recommended to expose a [mcve] always.) However, you may find yourself how to do it. – Scheff's Cat Jan 14 '20 at 07:54
  • I really appreciate your answers, I will do my best to resolve my issue with your comments and answers, Thank you...! – Sinwoo Yoo Jan 14 '20 at 08:53
  • @SinwooYoo I had a look into your upload. The `qpushButton__set_white_level_0` to connect I found in `class Ui_Form1` as `public` member. (This was expected.) Unfortunately, the pointer to instance of `Ui_Form1` is stored as `private` member in `class Form1`. So, the `class MainWindow` hasn't access to it. Unfortunately, I don't know how to overcome this due to my lack of experience with QtDesigner. (In C++, this would be easy to change adding something here or there but this may be overridden when changes are done in QtDesigner.) – Scheff's Cat Jan 14 '20 at 11:25
  • Dear, I glad to have your attention and valuable time for advice. I am working on it to change the logics... – Sinwoo Yoo Jan 16 '20 at 07:01