Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: neosapient от Январь 09, 2009, 15:40



Название: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: neosapient от Январь 09, 2009, 15:40
Есть окно MyMainWindow, производное от QMainWindow.
Код
C++ (Qt)
class MyMainWindow: public QMainWindow
{
   Q_OBJECT
public slots:
   void sltGet(QString str);
};
 

У окна есть слот, вида
Код
C++ (Qt)
void MyMainWindow::sltGet(QString str){
       QMessageBox::information(window(), tr("title"), str);
}
 

Есть второй поток, в который передается указатель на существующий экземпляр окна. Как в этом потоке выполнить вызов слота.

Код
C++ (Qt)
void func(MyMainWindow* pWindow)
{
   pWindow->sltGet("Hello World");
}
 
Такой вариант не подходит, так как отрисовка окон происходит в GUI потоке, о чем свидетельствует екцепшин с красным крестиком в верхнем левом угле.  :) "Widgets must be created in GUI thread"
---------------------------------------------
Сейчас пошел по второму варианту: объявил дополнительный сигнал и метод приема

Код
C++ (Qt)
class MyMainWindow: public QMainWindow
{
   Q_OBJECT
public:
   void get(QString str);
public slots:
   void sltGet(QString str);
signals:
   void signGet(QString str);
};
 
MyMainWindow::MyMainWindow()
{
...
   connect(this,  SIGNAL(signGet(QString)),  this,  SLOT(sltGet(QString)));
...
}
 
void MyMainWindow::get(QString str){
       emit sltGet(str);
}
 
void MyMainWindow::sltGet(QString str){
       QMessageBox::information(window(), tr("title"), str);
}
 
void func(MyMainWindow* pWindow)
{
   pWindow->get("Hello World");
}
 

Собственно, хочется более элегантного решения чем я описал в последнем случае, а то много вторичного кода получается. Хочется, зная указатель на QT-объект, передать в GUI-поток, о том что был emit (вызов).


Название: Re: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: Rcus от Январь 09, 2009, 16:05
bool QMetaObject::invokeMethod ( QObject * obj, const char * member, Qt::ConnectionType type, ...)


Название: Re: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: neosapient от Январь 09, 2009, 16:39
Так оно конечно лучше  ;)
Но всё ещё "не айс".
Строчка вызова выглядит громоздко,
QMetaObject::invokeMethod ( pWindow, "sltGet", Qt::QueuedConnection, Q_ARG(QString, "Hello World"));
по сравнению с
pWindow->get("Hello World");

Можно её облегчить?

----------
Приложение. Текущий вариант
Код
C++ (Qt)
class MyMainWindow: public QMainWindow
{
   Q_OBJECT
public slots:
   void sltGet(QString str);
};
 
void MyMainWindow::sltGet(QString str){
       QMessageBox::information(window(), tr("title"), str);
}
 
void func(MyMainWindow* pWindow)
{
   // pWindow->get("Hello World");
   QMetaObject::invokeMethod ( pWindow, "sltGet", Qt::QueuedConnection, Q_ARG(QString, "Hello World"));
}
 

 


Название: Re: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: Rcus от Январь 09, 2009, 16:54
Тут уж либо вы пишете обвязку, либо миритесь с громоздкими выражениями при вызове. Я бы объявил в одном из классов, используемых в дополнительном потоке сигнал типа
Код
C++ (Qt)
void msg(const QString& text)
и связал его со слотом в основном окне через Qt::QueuedConnection или Qt::BlockingQueuedConnection в зависимости от ситуации.
В таком случае вызов сводится к строчке
Код
C++ (Qt)
emit msg(tr("Hello world"));


Название: Re: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: neosapient от Январь 09, 2009, 17:12
Второй поток, который не GUI, был создан виндозной функцией _beginthreadex, а не классом  QThread.
Далее, для требуется связать методом connect(источник,сигнал,приемник,слот) указатель pWindow и указатель на вспомогательный объект, наследующий QObject. Но они глобальными не являются (во всяком случае у меня). По этому, потребуется синхронизированная передача указателей. Тут, собственно, возвращаемся к тому от чего хотелось уйти - дополнительный код на синхронизацию, который перегружает читабельность листингов.

Может есть какой-нибудь макрос ?


Название: Re: Вызов слота из стороннего потока, используя указатель на владельца слота
Отправлено: Dendy от Январь 09, 2009, 18:01
А вам в любом случае прийдётся обвязать код синхронизацией, если пользуетесь голыми указателями, чтобы убедиться в том что окно всё ещё живое.

Далее. Элегантного решения в одну строчку вроде:

Код:
pWindow->get("Hello World");

быть не может, так как тело должно выполниться в другом потоке. Так или иначе вызов прийдётся разорвать делая функцию потокобезопастной (разрыв внутри MyMainWindow::get()) или нет (разрыв во втором потоке). Разрыв будет в виде вспомагательного вызова, вроде invokeMethod() или postEvent(), то-есть неочевидным.

Кстати, всё зависит от задачи. Должен ли второй поток ждать, когда пользователь закроет MessageBox, чтобы обработать результат или нет?