2012-05-24 2 views
3

У меня есть следующая проблема: наше основное приложение использует набор инструментов Qt для отображения окон и взаимодействия с пользователем. Однако большая часть нашего приложения не знает часть GUI. Теперь я создал следующий дизайн:Обработка сигнала усиления в приложении Qt с несколькими потоками

  • Там одноэлементно класс, который может запросить рендеринг для данного объекта (OpenSceneGraph узел, но это не имеет никакого отношения к этому вопросу)
  • запрос рендеринга вызывает одноплодное испускать сигнал
  • Существует слот в главном классе окна (который использует Qt) для обработки визуализации объекта
  • в настоящее время, слот только создает новый текст редактирование виджета и помещает его в QMdiArea главного окна

Тем не менее, приложение неизбежно сбой, когда я пытаюсь создать новый виджет. Сообщения об ошибках площадь:

QObject::setParent: Cannot set parent, new parent is in a different thread 
[xcb] Unknown request in queue while dequeuing 
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called 
[xcb] Aborting, sorry about that. 
myApplication: ../../src/xcb_io.c:178: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed. 
Aborted 

Ознакомившись StackOverflow, я нашел подобные вопросы (которые не были легко применимы к данной ситуации). Очевидно, Qt не нравится, когда я что-то меняю в главном окне из другого потока. Тем не менее, я не сознательно создать новую тему, и я подумал, что синглтон (который создается в главной функции сразу после вызова QApplication()) должен быть в же потоке как Qt. Видимо, я ошибаюсь.

Вот минимальный пример, который показывает то, что я делаю (я извлек соответствующие части кода, так что пример не совсем функциональный):

class Object 
{ 
public: 
}; 

class Singleton 
{ 
public: 
    typedef boost::signals2::signal<void (Object*)> signalShowObject; 
    signalShowObject _showObject; 
}; 

class MainWindow : public QMainWindow 
{ 
public: 
    MainWindow() 
    { 
    Singleton::getInstance()->_showObject.connect(boost::bind(&MainWindow::showObject, this, _1)); 

    // Set up MDI area etc. 
    } 

private: 
    QMdiArea* _mdiArea; 

    void showObject(Object* object) 
    { 
    // Creating a new subwindow here causes the crash. The `object` pointer is 
    // not used and has just been included because it models my real problem 
    // better. 
    _mdiArea->addSubWindow(new QTextEdit())->show(); 
    } 
}; 

Мои попытки решить эту проблему есть был очень неуклюжим:

  • Я создал новый Qt сигнал в MainWindow классе с той же подписью, что и сигнала бустера
  • в сл ВЗ, который обрабатывает сигнал Boost, я испускаю новый Qt сигнал, передавая указатель на
  • теперь я создал новый Qt слота, который получает указатель

Когда я открываю новое окно в новом слоте , все работает. Однако это кажется мне очень неуклюжим. Нужно ли мне cascadeвсе Усиление сигналов так или есть лучший способ?

+0

Просто, чтобы быть ясным. Каждая операция gui ДОЛЖНА выполняться в потоке QApplication. Если вы хотите проверить, вызван ли ваш метод в правильном потоке, используйте qDebug() << thread() << QThread :: currentThread(); Сообщите нам, что является результатом этой отладки в элементе showObject. –

+0

@KamilKlimek Я получил идентификаторы потоков в моем «реальном» приложении, и они действительно разные. – Gnosophilon

+0

Вы можете вызвать слоты, а затем с QMetaObject :: invokeMethod –

ответ

1

Я думаю, что сбивает с толку то, что вызов singleton из запроса рендеринга производится из любого потока, генерирующего запрос. Синглтон будет возвращать уникальный объект, но сигнал он посылает его еще в контексте запрашивающего потока.Что-то нужно сделать, чтобы явно вызвать или разрешить переключение контекста потока в основной поток пользовательского интерфейса, чтобы фактически обработать этот сигнал и создать объекты пользовательского интерфейса в основном потоке.

И вы делаете это неявно в этой последовательности вы описали:

• Я создал новый Qt сигнал в классе MainWindow с той же подписи сигнала бустера

• В слоте который обрабатывает сигнал Boost, я испускаю новый Qt сигнал, передавая указатель на

• Теперь я создал новый слот Qt, который получает указатель

Сигналы Qt и слоты автоматически передают сигналы поперечной нити очереди (примечание 1). Таким образом, слот, который обрабатывает сигнал усиления, все еще находится в запрашивающем потоке. Затем он излучает сигнал Qt. Qt обнаруживает, что приемник сигнала находится в основном потоке (примечание 2), но отправитель находится в потоке запроса и опрашивает сигнал. Когда основной цикл событий Qt в основном потоке вытягивает это событие в очередь из списка событий, он затем автоматически переизлучает сигнал, но теперь он находится в контексте основного потока, и разрешены операции пользовательского интерфейса.

примечание 1 - если это явно не переопределено в вызове connect() - см. Документацию для Qt :: ConnectionType.

примечание 2 - действительно, что объект QObject получателя принадлежит основной теме. Каждый QObject сохраняет идентификатор потока контекста потока, в котором он был создан.

Надеюсь, это поможет объяснить, что происходит с потоками. Ваше решение в порядке, но, поскольку @tmpearce предположил, что было бы удобно обернуть вещи в адаптер.

+0

Спасибо за разъяснение! – Gnosophilon

0

Определить showObject в слоте и добавить немного формулы к телу:

if(QThread::currentThread() != thread()) 
{ 
    bool ok = QMetaObject::invokeMethod(this, "showObject", Qt::QueuedConnection, Q_ARG(QObject *, object)); 

    if(! ok) 
     qDebug() << "Couldn't invoke method"; 

    return; 
} 

сохранить остальную часть вашего тела метода, как есть.