QT + Как вызывать слот из пользовательского кода на С++, запущенного в другом потоке
Я новичок в QT, и я занимаюсь некоторым обучением.
Я хотел бы вызвать слот, который модифицирует виджет GUI из потока С++ (в настоящее время Qthread).
К сожалению, я получаю: ASSERTION не удалось: Q_ASSERT (qApp & & ap; qApp- > thread() == QThread:: currentThread());
вот какой код:
(MAIN + класс Thread)
class mythread : public QThread
{
public:
mythread(mywindow* win){this->w = win;};
mywindow* w;
void run()
{
w->ui.textEdit->append("Hello"); //<--ASSERT FAIL
//I have also try to call a slots within mywindow which also fail.
};
};
int main(int argc, char *argv[])
{
QApplication* a = new QApplication(argc, argv);
mywindow* w = new mywindow();
w->show();
mythread* thr = new mythread(w);
thr->start();
return a->exec();
}
Window:
class mywindow : public QMainWindow
{
Q_OBJECT
public:
mywindow (QWidget *parent = 0, Qt::WFlags flags = 0);
~mywindow ();
Ui::mywindow ui;
private:
public slots:
void newLog(QString &log);
};
Поэтому мне интересно, как обновить часть gui с помощью кода в другом потоке.
Спасибо за помощь
Ответы
Ответ 1
В дополнение к ответу стрибика, мне часто проще использовать соединение сигнал/слот. Вы можете указать, что это соединение должно быть в очереди при подключении, чтобы избежать проблем с сигналами потока, находящимися в контексте его собственного объекта.
class mythread : public QThread
{
signals:
void appendText( QString );
public:
mythread(mywindow* win){this->w = win;};
mywindow* w;
void run()
{
emit ( appendText( "Hello" ) );
};
};
int main(int argc, char *argv[])
{
QApplication* a = new QApplication(argc, argv);
mywindow* w = new mywindow();
w->show();
mythread* thr = new mythread(w);
(void)connect( thr, SIGNAL( appendText( QString ) ),
w->ui.textEdit, SLOT( append( QString ) ),
Qt::QueuedConnection ); // <-- This option is important!
thr->start();
return a->exec();
}
Ответ 2
stribika получил почти прав:
QMetaObject::invokeMethod( textEdit, "append", Qt::QueuedConnection,
Q_ARG( QString, myString ) );
cjhuitt right: хотя обычно вы хотите объявить сигнал в потоке и подключить его к слоту append()
, чтобы получить бесплатное управление жизненным циклом объекта (ну, по цене незначительного изменения интерфейса). На стороне, дополнительный аргумент:
Qt::QueuedConnection ); // <-- This option is important!
из cjhuitt ответа больше не требуется (это было в Qt <= 4.1), так как connect()
по умолчанию имеет значение Qt::AutoConnection
, которое теперь (Qt >= 4.2) делает правильную вещь и переключается между очереди и прямым режим подключения на основе QThread::currentThread()
и аффинность потока приемника QObject
во время испускания (вместо аффинности отправителя и приемника во время соединения).
Ответ 3
Вам нужно использовать QMetaObject :: invokeMethod. Например:
void MyThread::run() {
QMetaObject::invokeMethod(label, SLOT(setText(const QString &)), Q_ARG(QString, "Hello"));
}
(Код выше приведен здесь: http://www.qtforum.org/article/26801/qt4-threads-and-widgets.html)
Ответ 4
Я не думаю, что вам разрешено напрямую обращаться к вещам, которые приводят к событиям рисования от любого
другие потоки, чем основной поток. Это приведет к сбою.
Я думаю, вы можете использовать цикл событий, чтобы вызывать вещи асинхронно, чтобы основной поток gui поднимался, а затем обновлялся из основного потока, что и предлагает cjhuitt.
Ответ 5
Что, если наша нить сродни говорит GUI, но мы не в потоке GUI, ни в QThread?
Что я имею в виду, поток non-Qt (уведомление) вызывает метод интерфейса QObject, в котором мы испускаем сигнал AutoConnected. Сходство по потоку QObject является основным потоком, но процедура фактически вызывается из другого потока. Что будет делать Qt здесь?