Russian Qt Forum

Qt => Общие вопросы => Тема начата: alex12 от Январь 31, 2007, 12:59



Название: GUI и 2 потока
Отправлено: alex12 от Январь 31, 2007, 12:59
Всем привет!

[Qt 4.2.2 MinGW]

Есть программа с 2 потоками. Как написано в документации -- все обращения к GUI только из основного потока.

Но мне очень надо из дополнительного потока вызвать тривиальное окошко QInputDialog::getInteger().

Когда я его вызов нахально ставлю из дополнительного потока -- то все в основном работает, но иногда (примерно 20%) программа падает.

Может можно как-то приостановить/заблокировать основной поток и вызвать окошко ввода откуда мне удобно, а не городить дикие конструкции с флажками, таймерами или сигналами/слотами?


Название: GUI и 2 потока
Отправлено: Dendy от Январь 31, 2007, 13:57
Сказано нельзя - значит нельзя. И неважно в каком процентном соотношении валится прога, хоть в 0.01% случаев.

Делать примерно так:

Код:
void MyThread::run()
{
  ...
  // need to stop thread and show dialog
  QWaitCondition condition;
  QMutex mutex;
  qApp->postEvent( object_from_main_thread, MyEvent( &condition ) );
  {
    QMutexLocker locker( &mutex );
    condition.wait( &mutex );
  }
  // now dialog closed
}

bool ObjectFromMainThread::event( QEvent * e )
{
  if ( e->type() == MY_EVENT )
  {
    MyEvent * me = static_cast<MyEvent*>( e );
    show_my_dialog();
    me->condition->wakeAll();
    return true;
  }
  // ...
}


Название: GUI и 2 потока
Отправлено: alex12 от Январь 31, 2007, 14:08
Просто в Qt 3 я что-то слышал о блокировке главного потока. Еще интересно, как по правильному сделать передачу обратно данных из диалога?


Название: GUI и 2 потока
Отправлено: Tonal от Январь 31, 2007, 14:11
Это надо в фак. Однозначно! ;-)


Название: GUI и 2 потока
Отправлено: Racheengel от Февраль 01, 2007, 00:45
+1

а из диалога - точно так же. Эвентом. Хотя 4.х вроде поддерживает межпотоковые сигналы...


Название: GUI и 2 потока
Отправлено: Dendy от Февраль 01, 2007, 02:04
Передавать сигналами будет правильней. Только вот пример тогда будет немного поболее. Ибо испускать сигнал нужно не из потока (QThread), а из обьекта, что принадлежит етому потоку, собьІтия которого обрабатьІваются в теле QThread::run(). Или принудительно вьІставлять QueuedConnection.


Название: GUI и 2 потока
Отправлено: Tonal от Февраль 01, 2007, 08:22
Цитата: "alex12"
Еще интересно, как по правильному сделать передачу обратно данных из диалога?

Вроде достаточно чуть подправить код Dendy:
Цитата: "Dendy"
Код:
void MyThread::run()
{
  ...
  // need to stop thread and show dialog
  QWaitCondition condition;
  QMutex mutex;
  MyEvent ev( &condition )
  qApp->postEvent( object_from_main_thread, ev);
  {
    QMutexLocker locker( &mutex );
    condition.wait( &mutex );
  }
  //Получаем код возврата:
  ... = ev->dialogResult();
}

bool ObjectFromMainThread::event( QEvent * e )
{
  if ( e->type() == MY_EVENT )
  {
    MyEvent * me = static_cast<MyEvent*>( e );
    int dlg_res = show_my_dialog();
    //Запоминаем код в MyEvent
    me->setDialogResult(dlg_res);
    me->condition->wakeAll();
    return true;
  }
  // ...
}

Или я чего-то не учёл?


Название: GUI и 2 потока
Отправлено: Sergeich от Февраль 01, 2007, 14:03
Цитата: "Tonal"
Или я чего-то не учёл?
Не учел :) Читаем в ассистанте:
QApplication::postEvent
... The event must be allocated on the heap since the post event queue will take ownership of the event and delete it once it has been posted. ...
Во-первых, нельзя заводить экземляр MyEvent в стеке, т.к. он будет удален через deletе, и если после этого прога просто грохнется тебе сильно повезет :) , т.к. если она выживет, то будет вытворять всякие гадости в различных местах памяти :)
Во-вторых, удуалением экземляра MyEvent управляет кутевая событийная очередь, и скорей всего он будет удален сразу после выхода из ObjectFromMainThread::event( QEvent * e ), так что не факт, что при обращении к нему в MyThread::run() он будет еще жив.


Название: GUI и 2 потока
Отправлено: Dendy от Февраль 01, 2007, 15:46
В стеке создавать MyEvent для postEvent() нельзя! Навернёте всю программу. Указатель на MyEvent должен теряться сразу же после вьІзова postEvent(). Ибо его удаляет из кучи сам QCoreApplication.

Как передать данньІе диалога обратно в поток? Очень просто. В MyEvent сохраняете указатель на некую структуру с данньІми. Так как поток замрёт до отработки диалога - заботиться о потере етих данньІх по указателю и одновременному их используется не нужно.

Код:
struct ReturnData
{
  int code;
  QString message;
};

ReturnData data;
qApp->postEvent( object_from_main_thread, new MyEvent( &data, &condition ) );
...
if ( data.code == 123 )
  qDebug() << "Dialog returned message: " << data.message;


Название: GUI и 2 потока
Отправлено: Tonal от Февраль 01, 2007, 19:17
Цитата: "Dendy"
В стеке создавать MyEvent для postEvent() нельзя! Навернёте всю программу.

Писал основываясь исключительно на вашем коде.
Код:
qApp->postEvent( object_from_main_thread, MyEvent( &condition ) );

Не сверился с ассистентом - вот и нарвался. ;-)
Цитата: "Dendy"
Как передать данньІе диалога обратно в поток? Очень просто. В MyEvent сохраняете указатель на некую структуру с данньІми. Так как поток замрёт до отработки диалога - заботиться о потере етих данньІх по указателю и одновременному их используется не нужно.

Это примерно и имелось в виду.
С сигналами стоит заморачиваться только если нужна более сложная коммуникация с GUI-я и потока.[/code]


Название: GUI и 2 потока
Отправлено: alex12 от Февраль 02, 2007, 11:37
Всем спасибо. Все же сколько может быть мороки от тривиального QInputDialog::getInteger()!  :D