Измените Qt GUI из фонового рабочего потока

Я работаю в Qt, и когда я нажимаю кнопку GO, мне нужно постоянно отправлять пакеты в сеть и изменять интерфейс с информацией, которую я получаю.

Проблема в том, что у меня есть кнопка while(1) в кнопке, поэтому кнопка никогда не заканчивается, поэтому интерфейс никогда не обновляется. Я думал создать поток в кнопке и поставить там код while(){}.

Мой вопрос в том, как я могу изменить интерфейс из потока? (Например, как я могу изменить textBox из потока?

Ответы

Ответ 1

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

Вот почему правильный способ сделать это - уведомить основной поток от рабочего, а код в основном потоке будет фактически обновлять текстовое поле, индикатор выполнения или что-то еще.

Лучший способ сделать это, я думаю, - использовать QThread вместо posix thread и использовать сигналы Qt для связи между потоками. Это будет ваш рабочий, заменитель thread_func:

class WorkerThread : public QThread {
    void run() {
        while(1) {
             // ... hard work
             // Now want to notify main thread:
             emit progressChanged("Some info");
        }
    }
    // Define signal:
    signals:
    void progressChanged(QString info);
};

В вашем виджете определите слот с тем же прототипом, что и сигнал в .h:

class MyWidget : public QWidget {
    // Your gui code

    // Define slot:
    public slots:
    void onProgressChanged(QString info);
};

В .cpp выполните эту функцию:

void MyWidget::onProgressChanged(QString info) {
    // Processing code
    textBox->setText("Latest info: " + info);
}

Теперь в том месте, где вы хотите создать поток (при нажатии кнопки):

void MyWidget::startWorkInAThread() {
    // Create an instance of your woker
    WorkerThread *workerThread = new WorkerThread;
    // Connect our signal and slot
    connect(workerThread, SIGNAL(progressChanged(QString)),
                          SLOT(onProgressChanged(QString)));
    // Setup callback for cleanup when it finishes
    connect(workerThread, SIGNAL(finished()),
            workerThread, SLOT(deleteLater()));
    // Run, Forest, run!
    workerThread->start(); // This invokes WorkerThread::run in a new thread
}

После того, как вы подключите сигнал и слот, слот с emit progressChanged(...) в рабочем потоке отправит сообщение в основной поток, и основной поток вызовет слот, который подключен к этому сигналу, onProgressChanged здесь.

<ы > P.s. Я еще не тестировал код, поэтому не стесняйтесь предлагать редактирование, если я ошибаюсь где-то

Ответ 2

вы можете использовать механизм invokeMethod() или Signals and slots. В основном есть много примеров, например, как испустить сигнал и как получить это в SLOT. Но InvokeMethod кажется интересным.

Ниже приведен пример, где показано, как изменить текст метки из потока:

//file1.cpp

QObject *obj = NULL; //global 
QLabel *label = new QLabel("test");
obj = label;   //Keep this as global and assign this once in constructor.

Далее в вашем WorkerThread вы можете сделать следующее:

//file2.cpp(то есть, поток)

extern QObject *obj;
void workerThread::run()
{
     for(int i = 0; i<10 ;i++
     {
         QMetaObject::invokeMethod(obj, "setText",
                                Q_ARG(QString,QString::number(i)));
     }
     emit finished();
}

Ответ 3

вы запускаете поток, передающий некоторый указатель на функцию потока (в posix функция потока имеет подпись void * (thread_func) (void *), что-то равное под окнами тоже) - и вы совершенно свободны отправить указатель на свои собственные данные (структуру или что-то еще) и использовать это из функции потока (указатель на правильный тип). хорошо, управление памятью должно быть, хотя вне (так что вы не утечка памяти и не используете уже освобожденную память из потока), но это другая проблема.