Qt: Фоновый поток, обновляющий поток пользовательского интерфейса - PullRequest
9 голосов
/ 04 марта 2010

У меня есть фоновый поток, и этот поток вызывает некоторые методы, которые обновляют пользовательский интерфейс (для отображения индикаторов выполнения и дополнительной информации в текстовых областях).

Если я изменяю некоторые значения виджета пользовательского интерфейса, возникает ошибка «Невозможно отправить события объектам, принадлежащим другому потоку».

Просматривая форумы, я прочитал, что могу использовать метод QMetaObject :: invokeMethod, но он просто работает, если я передаю ему флаг Qt :: DirectConnection, который фактически вызывает ту же ошибку, показанную выше.

Если я использую Qt :: QueuedConnection или Qt :: AutoConnection, invokeMethod возвращает false.

Мой код выглядит примерно так:

.h:

class A : public QMainWindow
{
  Q_OBJECT

  QProgressBar* pb;

  public slots:
    bool m(bool, int);
};

class B
{
  A* a;

  public:
    void handleEvent();
};


.cpp:

bool A::m(bool x, int y)
{
  pb->setValue(y);
  return x;
}

void B::handleEvent()
{
  //a->m(true, 12); //raises an assertion error

  bool r;
  //bool ret = QMetaObject::invokeMethod(a, "m", Qt::DirectConnection, Q_RETURN_ARG(bool, r), Q_ARG(bool, true), Q_ARG(int, 12)); //raises the same assertion error error

  bool ret = QMetaObject::invokeMethod(a, "m", Qt::AutoConnection, Q_RETURN_ARG(bool, r), Q_ARG(bool, true), Q_ARG(int, 12)); //is ignored and ret contains false.
}

Вы знаете, что происходит или что я делаю неправильно? или, может быть, кто-то может предложить мне другой подход к решению моей проблемы новичка?

Заранее спасибо,

Эрнесто

1 Ответ

10 голосов
/ 04 марта 2010

Я не использовал invokeMethod() сам, но для этого я обычно просто использую сигналы и слоты. Например, вы можете создать сигнал в качестве члена class B, который подключен к слоту в class A, который обновляет ход выполнения:

class B : public QObject
{
    Q_OBJECT

    A* a;

signals:
    void update_signal(bool, int);

public:
    void handleEvent();
};

B::B()
{
    //assuming a already points to the correct place...
    connect(this, SIGNAL(update_signal(bool,int), 
            a, SLOT(m(bool,int)), Qt::QueuedConnection);
}

void B::handleEvent()
{
    emit update_signal(true, 12);
}

A::m() придется возвращать void в этом случае, но это не проблема, потому что при использовании соединения в очереди вы не можете получить возвращаемое значение в любом случае, поскольку вызов асинхронный (emit update_signal(true,12) может вернуться до вызвал невозможность иметь готовое возвращаемое значение).

Вы можете фактически выполнить это соединение где угодно, если у вас есть указатели на объект типа A и объект типа B. Это делает сигналы и слоты очень гибкими, поскольку вы можете полностью отделить A от B, но при этом позволить им обмениваться данными через сигналы и слоты. Например:

class B : public QObject
{
    Q_OBJECT

signals:
    void update_signal(bool, int);

public:
    void handleEvent();
};

void B::handleEvent()
{
    emit update_signal(true, 12);
}

class A : public QMainWindow
{
    Q_OBJECT

    QProgressBar* pb;

    public slots:
        void m(bool, int);
};

void A::m(bool x, int y)
{
    pb->setValue(y);
}

int main()
{
    A* a = new A();
    B* b = new B();

    QObject::connect(b, SIGNAL(update_signal(bool, int)),
            a, SLOT(m(bool, int)), Qt::QueuedConnection);

    //...
}

В этом случае b не нужно хранить указатель или знать что-либо о a, но они могут общаться через тонкий четко определенный канал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...