簡體   English   中英

如何從 _clicked() 事件發出另一個實例的信號?

[英]How can I emit a signal of another instance from _clicked() event?

可運行的項目在這里:在此處輸入鏈接描述


我真誠地很高興有您的詳細答案來解決這個問題,但我仍然對這個問題感到困惑:

案例一:把socket_session的成員變量

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實例沒有從mainwindow實例化的socket_thread實例。 我認為有一個解決方案是創建另一個類,其中包含我想從主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);

}

在此處輸入圖片說明

我知道我對此缺乏了解,但是,我想做一些沒有人做的事情嗎......? 我認為每個人都希望清楚地分離所有對象及其方法,並通過信號或從交付的對象實例調用函數進行通信......

案例 2:...讓我先試試你建議如何實現...


我可以閱讀 C++ 代碼和整體結構,但我不知道為什么我必須為此而掙扎,所以請幫助我,親愛的 Guru。

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

我有一個帶有按鈕的表單,我像這樣放置了一個 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??
}

現在,主窗口是這樣的:


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

...

我只是想從 QPushbutton_clicked() 插槽內部發出 socketthread 的信號 setThreadFlag 。 一旦socket_session->run()啟動,我需要通過從正在運行的線程中發出一個人的setThreadFlag()單擊按鈕來更改threadFlag 而我只是卡在這里。

甚至有可能嗎? 還是我從一開始就做錯了?

正如這篇文章中提到

“發出信號”==“調用函數”

所以你真正需要做的就是調用信號函數,所有連接的插槽都應該被調用。

這當然意味着Form1對象需要一個指向線程對象的指針,即它需要socket_session的副本。 然后你可以簡單地調用對象上的信號

socket_session->setThreadFlag(your_flag);

當然,如果Form1socket_session指針的副本,它也可以直接調用setFlag ,如果它是public

我只是想從 QPushbutton_clicked() 插槽內部發出 socketthread 的信號 setThreadFlag 。

不需要信號——只需調用函數。

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 UI builder QtDesigner,而是專門通過 C++ 代碼構建我所有的 UI。)


但是,現在,另一個修復是必要的:

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原子化,因此可以從任何線程訪問它而不會導致數據競爭。


更新:

OP更新問題后:

我只是想從 QPushbutton_clicked() 插槽內部發出 socketthread 的信號 setThreadFlag 。

按鈕(從 UI Form1創建)也可以在MainWindow連接(不使用Form1任何方法):

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

筆記:

  1. 關於form1->button1 ,我不太確定。 我注意到 UI 生成的表單中的小部件可以通過這種方式訪問​​,但我不知道確切的細節(因為我自己從未使用過 Qt UI 構建器)。

  2. 我使用了QObject::connect()的 Qt5 風格。 這是我無論如何都會推薦的。
    Qt5 風格在編譯時驗證。 – C++ 類型檢查檢測到錯誤連接。
    此外,可以使用任何具有匹配簽名的函數——不再需要顯式暴露插槽。
    通過使用同樣受支持的 C++ lambda,甚至可以轉換不匹配的簽名或添加其他參數。
    Qt:基於字符串和基於函子的連接之間的差異

  3. 可以連接不同線程的信號和槽。 我使用Qt::QueuedConnection標記為線程間通信。
    (但是,我大致記得 Qt 可能能夠自己檢測到它。請參閱文檔Qt::AutoConnection這是默認設置。

進一步閱讀: Qt:信號和插槽

順便提一句。 使用 Qt 信號進行線程間通信將排除使SocketThread::threadFlag()原子化的SocketThread::threadFlag() 它可以變成一個簡單的普通bool threadFlag; 反而。 在這種情況下,插槽SocketThread::setThreadFlag()QThread的 Qt 事件循環中被QThread

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM