Russian Qt Forum
Ноябрь 24, 2024, 18:32 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Вызов слота из стороннего потока, используя указатель на владельца слота  (Прочитано 6021 раз)
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 (вызов).
« Последнее редактирование: Январь 09, 2009, 15:56 от neosapient » Записан
Rcus
Гость
« Ответ #1 : Январь 09, 2009, 16:05 »

bool QMetaObject::invokeMethod ( QObject * obj, const char * member, Qt::ConnectionType type, ...)
Записан
neosapient
Гость
« Ответ #2 : Январь 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"));
}
 

 
Записан
Rcus
Гость
« Ответ #3 : Январь 09, 2009, 16:54 »

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

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

Может есть какой-нибудь макрос ?
Записан
Dendy
Гость
« Ответ #5 : Январь 09, 2009, 18:01 »

А вам в любом случае прийдётся обвязать код синхронизацией, если пользуетесь голыми указателями, чтобы убедиться в том что окно всё ещё живое.

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

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

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

Кстати, всё зависит от задачи. Должен ли второй поток ждать, когда пользователь закроет MessageBox, чтобы обработать результат или нет?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.159 секунд. Запросов: 20.