Pergunta

I'll try to be explicit. I create a Qt application, that has some buttons and a QTextEdit. Next i create a pthread. And offer the pointer to the MainWindow as the parameter. Something like this:

MainWindow w;
pthread_create(&rThread,NULL,treat,&w);

treat is the function that is executed when the thread is created. Now if i have a pushButton called myButton, and i do somthing like this inside the treat function:

 void *treat(void *arg)
 {
  MainWindow *win = (MainWindow*)arg;
  win->ui->myButton->setEnabled(false);
  close(pthread_self());
 }

It will work fine , and the myButton in my application will disable. However if i do something like this:

 void *treat(void *arg)
 {
  MainWindow *win = (MainWindow*arg;
  win->ui->editText->setText("random string");
  close(pthread_self());
 }

My application will crash with the following error:

QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x23af2e0), parent's thread is QThread(0x209a290), current thread is QThread(0x7f7eec000af0) The program has unexpectedly finished.

As i understand the Ui is living in the main thread, and probably is not accesible in the thread that i created , despite the fact that i offered the pointer of the main window to this thread. But why does disabling of the button work? I am very confused. The reason why i dint use the QThread is because our teacher told me not do it. I have to use pthreads. How could i apply such a change from the pthread to the editText ? How could i send a signal from a pthread to the other thread in wich the Ui is "living". Thanks in advance guys.

Foi útil?

Solução

Generally speaking, it's an error to call any of QObject (or derived classes') methods from a thread other than object->thread() - unless they are designed and documented to be thread-safe. There are a few methods in Qt proper that are explicitly documented as being thread safe, for example QCoreApplication::postEvent.

The behavior you're facing is due to accessing QWidget methods from the non-gui thread. It's undefined behavior, so some methods may crash, some won't, but even if they don't it's still undefined behavior that you can't count on. It may depend on the phase of the Moon, for all we know.

The only safe thing to do from another thread is to post an event to the object. When you use QMetaMethod::invoke or QMetaObject::invokeMethod on an object in another thread, Qt will internally post a QMetaCallEvent to the object. Since posting events is thread-safe (can be done from other threads), it's OK to do use either of those invoke methods from other threads. QObject::event() reacts to such events by executing the proper method call.

So, the only thing you can do from the other thread is:

QMetaObject::invokeMethod(win->ui->editText, "setText", Q_ARG(QString, "random string"));

Alas, this is bad design, since you're exposing MainWindow's internal details (like the ui pointer) to outside. What you should do instead is have a setEditText slot on the window:

MainWindow : public QWidget {
  ...
public:
  Q_SLOT void setEditText(const QString & str) {
    ui->editText->setText(str);
  }
  ...
};

Then, from the other thread, you do:

QMetaObject::invokeMethod(win, "setEditText", Q_ARG(QString, "random string"));

I fully agree with Marek R's recommendation not to use pthreads when you have QThread available.

Outras dicas

First of all mixing libraries when it is not necessary is bad habit. Qt provides QThread and very handy QtConcurrent.

Secondly this is bad design. Create some QObject which will handle yours calculations in thread and will emit signal when it should pass result to UI (main thread). Then create connection and Qt will handle rest of the stuff make it thread safe (by default it will queue connection if signal is passed between threads).

Your code with Qt concurrent:

void *treat(SomeClass *arg) {
    arg->doStuff();
}

QtConcurrent::run(treat, someObject);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top