Как я могу отправить сигнал другого экземпляра из события _clicked ()? - PullRequest
0 голосов
/ 14 января 2020

запускаемый проект находится здесь: введите описание ссылки здесь


Я искренне рад получить ваши подробные ответы, чтобы решить эту проблему, но я все еще путаюсь по этому вопросу:

case 1: изменение socket_session в качестве переменной-члена mainwindow

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    SocketThread* socket_session;

private:
...

Но это не решение для доступа к setFlag, даже после того, как я изменил `Form1 :: on_qpushButton__set_white_level_0_clicked ( ) 'функция выглядит следующим образом:

void Form1::on_qpushButton__set_white_level_0_clicked() {
    qDebug() <<"clicked()";
    socket_session->setThreadFlag(true);

}

Тем не менее, это не имеет смысла, поскольку экземпляр form1 не имеет "экземпляра" socket_thread, который был создан из mainwindow. Я думаю, что есть решение создать другой класс, который включает все экземпляры, которые я хочу использовать из mainwindow, но я не думаю, что это хороший вариант, потому что я использую поток и обращаюсь к глобальному большому классу экземпляров, который включает в себя все для них «делиться» не очень хорошая идея для таких, как я.

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

}

введите описание изображения здесь

Я знаю, что не понимаю это, но я хочу сделать что-то, что никто не делает ...? Я думаю, что каждый хочет четко отделить все объекты и их методы и общаться через сигналы или вызывающие функции от экземпляров доставленных объектов ...

case 2: ... позвольте мне попробовать, как вы предложили, сделать возможным сначала ...


Я могу читать код C ++ и общую структуру, но я не знаю, почему мне нужно с этим бороться, поэтому, пожалуйста, помогите мне, дорогой Гуру.

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

};

И его реализация ...

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";
}

И у меня есть одна форма, в которой есть кнопка, и я вставил в нее щелчок (), как это .. .

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??
}

Теперь главное окно выглядит так:


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

...

Я просто хочу испустить сигнал setThreadFlag сокета thread изнутри слота QPushbutton_clicked (). Как только socket_session->run() запустился, мне нужно изменить threadFlag, нажав кнопку, выбрасывая setThreadFlag() одного из запущенного потока. И я просто застрял здесь.

Возможно ли даже? Или я делаю все это неправильно с самого начала?

Ответы [ 2 ]

1 голос
/ 14 января 2020

Как уже упоминалось в этом посте :

"Излучение сигнала" == "Вызов функции"

Так что все, что у вас есть на самом деле Для этого нужно вызвать функцию сигнала, и должны быть вызваны все подключенные слоты.

Это, конечно, означает, что объект Form1 нуждается в указателе на объект потока, т.е. ему нужна копия socket_session. Затем вы можете просто вызвать сигнал объекта

socket_session->setThreadFlag(your_flag);

Конечно, если у Form1 есть копия указателя socket_session, он может также вызвать setFlag напрямую, если бы это было public.

0 голосов
/ 14 января 2020

Я просто хочу послать сигнал setThreadFlag socketthread изнутри слота QPushbutton_clicked ().

Сигнал не требуется - просто вызовите функцию.

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

Чтобы сделать это возможным, необходимо другое исправление:

socket_session - локальная переменная в открытом коде OP.

Чтобы сделать его "постоянным", оно имеет например, стать переменной-членом.

Итак, конструктор MainWindow::MainWindow() должен быть изменен:

// Nope: SocketThread* socket_session = new SocketThread();
// Instead:
  socket_session = new SocketThread();

и SocketThread* socket_session; необходимо добавить к переменным-членам class MainWindow.

Чтобы сделать его доступным в Form1, его необходимо также передать в Form1. Это может быть сделано, например, сделав его переменной-членом в Form1, которая также инициализируется аргументом конструктора (или устанавливается из MainWindow впоследствии).

(Я должен признать, что я никогда не использовал Qt Построитель пользовательского интерфейса QtDesigner, но создаю все мои пользовательские интерфейсы исключительно с помощью кода C ++.)


Но теперь необходимо другое исправление:

volatile не делает переменную, подходящую для межпоточности коммуникации. (Это использовалось в древние времена до того, как многопоточность начала поддерживаться C ++ 11.)

Однако это неправильно: Полезно ли volatile с потоками?

Подходящим решением было бы использовать std::atomic вместо:

    // Wrong for interthread-com.
    //volatile bool threadFlag;
    // Correct:
    std::atomic<bool> threadFlag; // #include <atomic> needed

К вашему сведению: SO: Многопоточная программа зависла в оптимизированном режиме, но работает нормально в - O0


И, наконец, в SocketThread::SocketThread():

    connect(this, SIGNAL(setThreadFlag(bool)), this, SLOT(setFlag(bool)));

в этом случае нет необходимости. SocketThread::setThreadFlag() может вызвать SocketThread::setFlag() напрямую или даже написать threadFlag сам:

    void setThreadFlag(bool flag) { threadFlag = flag; }

Поскольку я (рекомендую) сделать threadFlag atomi c, к нему можно получить доступ из любого потока без вызывая гонку данных.


Обновление:

После того, как OP обновил вопрос:

Я просто хочу отправить сигнал setThreadFlag socketthread изнутри слота QPushbutton_clicked ().

Кнопка (созданная из пользовательского интерфейса Form1) также может быть подключена в MainWindow (без использования какого-либо метода Form1):

QObject::connect(form1->button1, &QPushButton::clicked,
  socket_session, &SocketThread::setThreadFlag,
  Qt::QueuedConnection);

Примечания:

  1. О form1->button1, я не совсем уверен. Я заметил, что к виджетам в формах, сгенерированных пользовательским интерфейсом, можно получить доступ таким образом, но я не знаю точных деталей (поскольку я никогда не использовал конструктор Qt UI самостоятельно).

  2. Я использовал стиль Qt5 QObject::connect(). Это то, что я бы порекомендовал в любом случае. Стиль Qt5 проверяется во время компиляции. - Неправильные соединения обнаруживаются проверкой типа C ++. Кроме того, может быть использована любая функция с совпадающей сигнатурой - явного раскрытия слотов больше не требуется. Даже преобразование несоответствующей подписи или добавление дополнительных параметров становится возможным с помощью лямбда-символов C ++, которые также поддерживаются. Qt: Различия между строковыми и функторными соединениями

  3. Можно подключать сигналы и слоты разных потоков. Я использовал Qt::QueuedConnection, чтобы отметить это как межпотоковое общение. (Однако я грубо помню, что Qt мог бы обнаружить его сам. См. Do c. Для Qt::AutoConnection, который используется по умолчанию.

Дальнейшее чтение: Qt: Сигналы и слоты

Кстати, использование сигналов Qt для связи между потоками исключит необходимость создания SocketThread::threadFlag() atomi c Вместо этого он может стать простой равниной bool threadFlag;. Слот SocketThread::setThreadFlag() вызывается в событии Qt l oop из QThread, в данном случае

.
...