Как передать аргументы между QThread и QGui? - PullRequest
0 голосов
/ 10 июня 2019

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

Я хочу передать аргументы конструктору или пользовательской функции в классе из Gui перед началом работы QThread.

Также мне нужно составить график для данных, полученных из сокета,Таким образом, любой способ решить, что я пытаюсь передать объекты указатели на класс, но получить много ошибок

Здесь код потока и код пользовательского интерфейса

//client code
#include "clientclass.h"
#include <QThread>
#include <QDebug>

clientClass::clientClass(char *serverIp, unsigned int serverPort, char *idname ="SCR")
{
    this->ip=serverIp;
    this->port=serverPort;
    this->id=idname;


    std::cout<<ip<<port<<id<<std::endl;

};

int clientClass::connection()
{

    SOCKET socketDescriptor;
    int numRead;

    char id[20]="SCR";

    unsigned int maxEpisodes;
    unsigned int maxSteps;
    char trackName[1000];
    BaseDriver::tstage stage;

    tDriver drive;
    strcpy(drive.trackName,trackName);
    drive.stage = stage;


    tSockAddrIn serv_addr;
    struct timeval timeVal;
    fd_set readSet;
    char buf[UDP_MSGLEN];

    // Create a socket (UDP on IPv4 protocol)
    socketDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
    if (INVALID(socketDescriptor))
    {
        response = "Error: cannot create socket";
        return (1);
    }

    // Set some fields in the serverAddress structure.

    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons((this->port));
    serv_addr.sin_addr.s_addr = inet_addr(this->ip);
    //std::cout<<serv_addr.sin_port<<std::endl;
    std::cout<<serv_addr.sin_addr.s_addr<<std::endl;

    bool shutdownClient=false;
    unsigned long curEpisode=0;
    do
    {

        do
        {
            // Initialize the angles of rangefinders
            float angles[19];
            drive.init(angles);
            string initString = SimpleParser::stringify(string("init"),angles,19);
            response =QString ("Sending id to server: %1").arg(id);
            initString.insert(0,id);
            response =QString("Sending init string to the server");
            if (sendto(socketDescriptor, initString.c_str(), initString.length(), 0,
                       (struct sockaddr *) &serv_addr,
                       sizeof(serv_addr)) < 0)
            {

                response ="Error: cannot send data ";
                CLOSE(socketDescriptor);
                return (1);
            }

            // wait until answer comes back, for up to UDP_CLIENT_TIMEUOT micro sec
            FD_ZERO(&readSet);
            FD_SET(socketDescriptor, &readSet);
            timeVal.tv_sec = 0;
            timeVal.tv_usec = UDP_CLIENT_TIMEUOT;

            if (select(socketDescriptor+1, &readSet, NULL, NULL, &timeVal))
            {
                // Read data sent by the solorace server
                memset(buf, 0x0, UDP_MSGLEN);  // Zero out the buffer.
                numRead = recv(socketDescriptor, buf, UDP_MSGLEN, 0);
                if (numRead < 0)
                {
                    response ="Error: didn't get response from server";
                }
        else
        {
                    response = "Received";

                    if (strcmp(buf,"***identified***")==0)
                            break;
                }
          }

        }  while(1);

    unsigned long currentStep=0;

        while(1)
        {
            // wait until answer comes back, for up to UDP_CLIENT_TIMEUOT micro sec
            FD_ZERO(&readSet);
            FD_SET(socketDescriptor, &readSet);
            timeVal.tv_sec = 0;
            timeVal.tv_usec = UDP_CLIENT_TIMEUOT;



            cout<<"in connection"<<endl;



            if (select(socketDescriptor+1, &readSet, NULL, NULL, &timeVal))
            {
                // Read data sent by the solorace server
                memset(buf, 0x0, UDP_MSGLEN);  // Zero out the buffer.
                numRead = recv(socketDescriptor, buf, UDP_MSGLEN, 0);

                if (numRead < 0)
                {
                    response = "Error : didn't get response from server";
                    CLOSE(socketDescriptor);
                    return (1);
                }

                if (strcmp(buf,"***shutdown***")==0)
                {
                    drive.onShutdown();
                    shutdownClient = true;
                    response="Client Shutdown";
                    break;
                }

                if (strcmp(buf,"***restart***")==0)
                {
                    drive.onRestart();
                    response ="Client Restart";
                    break;
                }
                /**************************************************
                 * Compute The Action to send to the solorace sever
                 **************************************************/

        if ( (++currentStep) != maxSteps)
        {
                    string action = drive.drive(string(buf));
                    memset(buf, 0x0, UDP_MSGLEN);
                    sprintf(buf,"%s",action.c_str());

        }
        else
            sprintf (buf, "(meta 1)");

                if (sendto(socketDescriptor, buf, strlen(buf)+1, 0,
                           (struct sockaddr *) &serv_addr,
                           sizeof(serv_addr)) < 0)
                {
                    response = "Error : cannot send data ";
                    CLOSE(socketDescriptor);
                    return (1);
                }
            }
            else
            {
                response ="Server did not respond in 1 second ";
            }
        }
    } while(shutdownClient==false && ( (++curEpisode) != maxEpisodes) );

    if (shutdownClient==false)
    drive.onShutdown();
    CLOSE(socketDescriptor);
    return 0;
}

>

// ui code 
<
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QString>
#include <time.h>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    thread =new QThread();
    QString response;

    mine = new clientClass("127",3001,"SCR");

    mine->moveToThread(thread);


    connect(thread, SIGNAL(started()),mine, SLOT(connection()));

}

MainWindow::~MainWindow()
{
    delete ui;
    delete thread;
    delete mine;
}

void MainWindow::on_pushButton_clicked()
{
    thread->start();

    if(thread->isRunning()) std::cout<< "is running"<<endl;


}

1 Ответ

0 голосов
/ 11 июня 2019

Создайте новый класс Worker Подклассы QObject и используйте сигналы / слоты для связи между вашим работником и пользовательским интерфейсом.

По моему мнению, было бы легче создать нового работника каждый раз, когдаВы хотите запустить процесс, чем повторно использовать тот же работник.Таким образом, вы должны удалить работника перед созданием нового.Для этого вы можете использовать слот QObject::deleteLater.

Вам нужен сигнал в классе Worker, срабатывающий, когда вам нужно отправить результаты в пользовательский интерфейс, и слот в вашем MainWindow для обработки результата.

Вам также нужен флаг дляостановите работника и прервите бесконечный цикл.

Пример работника:

class MyWorker: public QObject
{
    Q_OBJECT
public:
    MyWorker(QString const& aAddress, unsigned int aPort, QObject* parent=nullptr): QObject(parent),
        address(aAddress), port(aPort)
    {}
    virtual ~MyWorker()
    {
        qDebug() << "Deleted" << address << port;
    }
public slots:
    void run()
    {
        running = true;
        int count = 0;
        while(running)
        {
            QCoreApplication::processEvents();
            for (int i = 0; i != 100000; ++i) { // Long time processing
            for (int i = 0; i != 100; ++i) {}
            }
            ++count;
            resultAvailable(QString("Loop count %1").arg(count));
        }
        finished(); // Inform that the worker has finished its task
    }

    void stop() // Call it to stop the thread
    {
        qDebug() << QThread::currentThreadId();
        running = false;
    }
signals:
    void resultAvailable(QString const&); // Will send message to the UI
    void finished();
private:
    QString address;
    unsigned int port;
    bool running;
};

Пользовательский интерфейс, который вызывает работника и отображает полученный от него результат.

class MyWindow: public QWidget
{
    Q_OBJECT
public:
    MyWindow(QWidget* parent=nullptr): QWidget(parent),
    address(new QLineEdit),
    port(new QLineEdit),
    ok(new QPushButton("Run")),
    label(new QLabel())
    {

        QVBoxLayout* layout = new QVBoxLayout(this);
        layout->addWidget(address);
        layout->addWidget(port);
        layout->addWidget(ok);
        layout->addWidget(label);
        connect(ok, &QPushButton::clicked, this, &MyWindow::process);
    }
public slots:
    void process()
    {
        // Create a new worker and its thread
        QThread* workerThread = new QThread(this);
        MyWorker* worker = new MyWorker(address->text(), port->text().toUInt());
        worker->moveToThread(workerThread); // Put the worker in a dedicated thread

        connect(ok, &QPushButton::clicked, worker, &MyWorker::stop); // If user clicks on the ok btn again, delete the previous worker
        connect(worker, &MyWorker::resultAvailable, this, &MyWindow::processResult); // Process the data sent by the worker in the UI
        connect(worker, &MyWorker::finished, workerThread, &QThread::quit); // Stop the thread when the worker has finished
        connect(workerThread, &QThread::finished, workerThread, &MyWorker::deleteLater); // Delete the thread when finished
        connect(workerThread, &QThread::finished, worker, &MyWorker::deleteLater); // Delete the thread when finished
        connect(workerThread, &QThread::started, worker, &MyWorker::run); // Starts the worker when the thread is launched
        workerThread->start(); // The worker will be called
    }

    void processResult(QString const& result)
    {
        label->setText("The result: " + result);
    }
private:
    QLineEdit* address;
    QLineEdit* port;
    QPushButton* ok;
    QLabel* label;
};
...