Russian Qt Forum

Qt => Общие вопросы => Тема начата: Nimbus от Октябрь 08, 2014, 13:15



Название: QAction добавить сигналы ДО triggeted
Отправлено: Nimbus от Октябрь 08, 2014, 13:15
Доброго времени суток.

В приложении на С++ где-то внутри сохранён указатель на экземпляр QAction и добавлен на какие-то меню/тулбары. На сигнал triggered() этого экземпляра законнекчено неизвестное количество каких-то QObject-ов (пусть набор этих соединений со слотами будет называться "хэндлером по-умолчанию").

Теперь, получив извне доступ к этому экземпляру QAction (в данном случае запускается питоновый интерпретатор, в котором запускается какой-то кастомный питоновый код, в контексте этого самого процесса с QAction'ом, имеющий у себя в арсенале биндинги PySide, но это сейчас не важно), очень нужно обернуть хэндлер по-умолчанию своим кодом. То есть, чтобы сигнал triggered() вызвал сначала какой-то наш кастомный слот ДО вызова хэндлера по-умолчанию, затем сам хэндлер по-умолчанию, и затем ещё какой-то наш кастомный слот.

Типичный сценарий такой -- имеется QMenu в С++ приложении. Добавить вызов какого-то кода ДО клика на определённом пункте меню и ПОСЛЕ.
 
С вызовом слота после проблем нет -- добавить новый коннект. А вот ДО...
Можно попытаться добавить Proxy-объект, хранящий оригинальный QAction, который заменял бы его когда нужно на свой кастомный QAction, в слоте на on_triggered вызывался бы код ДО, затем оригинальный QAction.trigger() и код ПОСЛЕ. Однако, такой механизм имеет ряд недостатков, один из которых -- приложение по прежнему оперирует оригинальным экземпляром QAction'а и не знает ничего, что его подменили в меню, соответственно попытки сделать недоступным для клика (или изменить shortcut) приведут лишь к изменению оригинального, а proxy-объект не узнает об этом.

Переконнектить его тоже не получится -- мы не знает ничего про хэндлер по-умолчанию в этом контексте. Знаем только, что он есть.

Другой вариант -- изменить само С++ приложение, чтобы  QAction имел в своём составе Proxy-объект, к которому он бы коннектился и к которому уже коннектился бы хэндлер-по умолчанию и имелась бы возможность получить этот объект извне, например через вызов QMetaObject::invokeMethod. К сожалению это серьёзное архитектурное изменение, требующее значительных затрат и изменений кода и тестов.

Хорошо бы иметь какую-то очередь коннектов, в которую уже можно будет вставлять элементы с начала или конца. Но это лишь пожелание.
И да, я лишь описал единичный случай. Хотелось бы такое проделывать для любых QAction'ов, которые мы возьмём из приложения.

Кто-нибудь знает другие способы?


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Bepec от Октябрь 08, 2014, 13:25
На мой взгляд очень ненужная и опасная штука у вас выходит. А если там цикл бесконечный или ошибка?

К тому же не видно смысла. Зачем вам это?

Вкратце - вам нужно чтобы вызывался слот triggered после обработки питоновского скрипта.
Как делать - создать своего потомка от QAction в котором перед посылкой сигнала вызывать питоновский скрипт.


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Nimbus от Октябрь 08, 2014, 13:32
На мой взгляд очень ненужная и опасная штука у вас выходит. А если там цикл бесконечный или ошибка?

К тому же не видно смысла. Зачем вам это?

Вкратце - вам нужно чтобы вызывался слот triggered после обработки питоновского скрипта.
Как делать - создать своего потомка от QAction в котором перед посылкой сигнала вызывать питоновский скрипт.

Это уже пользователям решать, они будут писать скрипт.
К тому же, С++ приложение НЕ знает ничего что внутри питонового скрипта.
Оно просто поднимает интерпретатор и запускает скрипт.
Скрипт вызывает единожды, а уже в нём навешиваются какие-то хэндлеры и кастомизируется приложение.


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Nimbus от Октябрь 08, 2014, 13:40
Я имею в виду, что у пользователей ДОЛЖНА быть возможно завернуть обработку сигнала triggered в свой код, имея в контексте возможность вызвать хэндлер по-умолчанию.


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Bepec от Октябрь 08, 2014, 17:49
Ну блин поставьте там запуск файла myscript.py.
Если файла нет, то стандартный вызов сигнала.

Вот и вся логика.


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Nimbus от Октябрь 08, 2014, 18:08
Ну блин поставьте там запуск файла myscript.py.
Если файла нет, то стандартный вызов сигнала.

Вот и вся логика.

Видимо я неясно выразился. Это было бы нарушением абстракции. У нас нет цели сделать запуск скрипта по какому-то событию. У нас есть цель обеспечить возможность пользователям-программистам вклинить(/заменить наши на) свои хэндлеры на наши QAction'ы.


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Bepec от Октябрь 08, 2014, 23:13
Так и не понял вашей логики. Можете привести цепочку вызовов без словесной шелухи?

Нажатие экшена - вызов *** - вызов Qt слотов?


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Igors от Октябрь 09, 2014, 07:42
А не дописать ли нужный код в moc файл? (ну конечно запретив его пере-генерацию)


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Old от Октябрь 09, 2014, 08:37
2JC Если есть возможность заменить в c++ программе все QAction на, какие нибудь, MyAction, то я придумал неплохое решение.
Да, MyAction это прямой наследник QAction, т.е. в программе ничего кроме названия класса переделывать не придётся.

P.S. Пишу с телефона, поэтому сразу решение расписывать лениво. :)


Название: Re: QAction добавить сигналы ДО triggeted
Отправлено: Igors от Октябрь 09, 2014, 08:51
Есть такой ходик (qt_signal_spy_callback_set)

Код
C++ (Qt)
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
   int signal_index = signalOffset + local_signal_index;
 
   if (!sender->d_func()->isSignalConnected(signal_index)
       && !qt_signal_spy_callback_set.signal_begin_callback
       && !qt_signal_spy_callback_set.signal_end_callback) {
       return; // nothing connected to these signals, and no spy
   }
 
   if (sender->d_func()->blockSig)
       return;
 
   if (sender->d_func()->declarativeData && QAbstractDeclarativeData::signalEmitted)
       QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender,
                                               signal_index, argv);
 
   void *empty_argv[] = { 0 };
   if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
       qt_signal_spy_callback_set.signal_begin_callback(sender, signal_index,
                                                        argv ? argv : empty_argv);
 
Блокировать не видно как, но "до" и "после" есть