Russian Qt Forum

Qt => Общие вопросы => Тема начата: kuzulis от Февраль 05, 2010, 16:29



Название: pimpl. Почему работают сигналы из приватного класса? [РЕШЕНО]
Отправлено: kuzulis от Февраль 05, 2010, 16:29
Доброго времени!

Я сделал небольшой примерчик в котором в приватном классе не унаследованном ни от кого в приватной секции имеется указатель на QTimer.
Код:
#ifndef MYCLASS_P_H
#define MYCLASS_P_H

#include "myclass.h"

class QTimer;
class MyClassPrivate
{
    Q_DECLARE_PUBLIC(MyClass);
public:
    MyClassPrivate();
    virtual ~MyClassPrivate();

    void start();
    void _q_print();

    MyClass * q_ptr;
private:
    int cnt;
    QTimer *timer;
};

#endif // MYCLASS_P_H

И если сделать код, который у меня в примерчике - то можно подключаться к сигналам из приватного класса! О_О

Или я что-то недопонял в концепции.. или я вообще недопонял :)

1. Почему работают сигналы, ведь приватный клас не унаследован от QObject и в нем нет макроса Q_OBJECT? (про слоты с префиксом _q_ в приватных классах я в курсе)
2. Можно ли кодить применяя этот подход?

Проект в аттаче


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 16:40
что-то я не вижу сигналов ни в одном из классов


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 05, 2010, 16:42
Цитировать
что-то я не вижу сигналов ни в одном из классов
ну от QTaimer-a сигналы :)
timer же находится в приватном классе -> по идее сигналы от него не должны обрабатываться


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: BRE от Февраль 05, 2010, 16:43
1. Почему работают сигналы, ведь приватный клас не унаследован от QObject и в нем нет макроса Q_OBJECT? (про слоты с префиксом _q_ в приватных классах я в курсе)
2. Можно ли кодить применяя этот подход?
Ну так ты же используешь:
Q_PRIVATE_SLOT
и moc этого пропустить не может.  :)


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 05, 2010, 16:47
Цитировать
Ну так ты же используешь:
Q_PRIVATE_SLOT
и moc этого пропустить не может.
Ну я так понял, что эта фича делает из _q_print() слот... Но каким образом это относится к сигналам от таймера?
Если я сделаю слот в паблик классе MyClass и сделаю коннект - то тоже будет работать!

Прицепил аттач новый


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 16:50
да собственно сигналы от таймера не вызывают удивления.
мне например непонятно как соединение осуществляется для класса не наследника QObject.


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 16:53
так стоп.
полная запись вот такая:
q->connect(timer, SIGNAL(timeout()), q, SLOT(_q_print()));
где q - наследник QObject и в нём указан макрос Q_OBJECT


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 05, 2010, 16:54
Цитировать
да собственно сигналы от таймера не вызывают удивления.
И? Поделитесь секретом! :) В чем "фишка" ? В том что parent у таймера это q ?

Цитировать
мне например непонятно как соединение осуществляется для класса не наследника QObject.
в смысле?


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 05, 2010, 16:58
Цитировать
так стоп.
полная запись вот такая:
q->connect(timer, SIGNAL(timeout()), q, SLOT(_q_print()));
где q - наследник QObject и в нём указан макрос Q_OBJECT

ааа.. начинает доходить!

Ну так значит такой код (идея) имеют место на существование? т.е. в том смысле, что указатели на объекты которые должны емиттить сигналы расположены в приватном классе?
т.е.
Код:
class MyClassPrivate
{
....
....
....
private:
    int cnt;
    QTimer *timer; // вот про это я имею ввиду!!
};

просто я нигде в примерах и исходниках Qt4 не встречал такого (или просто не попалось на глаза) и мне нужно именно так реализовать решение, вот я и спрашиваю :)


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 17:09
>>В чем "фишка" ? В том что parent у таймера это q ?
родитель не причём, у QTimer'а есть сигнал и он посылается при внутреннем событии таймера, независимо ни отчего (если их явно не блокировать).
Сигналы и слоты реализуют идею компонентного программирования, т.е. компонент (QTimer) максимально автономен.


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 17:11
В целом такой подход (без наследования от QObject) я считаю плохим, т.к. макрос Q_PRIVATE_SLOT отсутствует в открытом описании API, т.е. его нет в Assistant'е


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: BRE от Февраль 05, 2010, 17:12
Цитировать
так стоп.
полная запись вот такая:
q->connect(timer, SIGNAL(timeout()), q, SLOT(_q_print()));
где q - наследник QObject и в нём указан макрос Q_OBJECT

ааа.. начинает доходить!
На самом деле не обязательно использовать q->connect.
connect это статический метод QObject, т.е. можно просто
QObject::connect( ... );


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 05, 2010, 18:12
Цитировать
В целом такой подход (без наследования от QObject) я считаю плохим, т.к. макрос Q_PRIVATE_SLOT отсутствует в открытом описании API, т.е. его нет в Assistant'е

Почему?

хм, а вот в блоге: http://habrahabr.ru/blogs/qt_software/76506/

автор говорит что:
Цитировать
Приватные слоты — это механизм дополняющий функционал d-указателей. Он позволяет реализовать слоты для приватного класса, даже если он не является наследником от QObject (обычно он им и не является), но для этого публичный класс должен быть наследником от QObject. Тоесть по факту создается некий приватный слот в публичном классе и он непосредственно дергает нужный метод приватного класса.
Зачем это нужно? Ну рассмотрим на примере. Есть класс QAbstractScrollArea. Он просто отображает некий виджет (viewport) и обеспечивает прокрутку. Прокрутка обеспечивается с помощью двух экземпляров класса QScrollBar. Сами эти скролбары он хранит в приватном классе. Теперь проблемма: как подключить сигнал от скроллбара об изменение его позиции с классом QAbstractScrollAreaPrivate, ведь он не является QObject'ом? Сделать его наследником от QObject — лучше не делайте это :-) . Можно сделать слот в публичном классе и повесить на него, то в таком случае это не очень красиво — так как наружу выходят слоты от внутренней реализации. Вот ту Qt-шниками был придуман достаточно разумный и элегантный подход — приватные слоты.

да и в исходниках Qt4 приватные классы НЕ наследуются от QObject!

Цитировать
На самом деле не обязательно использовать q->connect.
connect это статический метод QObject, т.е. можно просто
QObject::connect( ... );
в исходниках Qt я только что заметил, что делают именно q->connect()!

О_О в общем пока непонятно.. неразбериха


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: lit-uriy от Февраль 05, 2010, 19:46
>>Почему?
Дык, яж написал:
"...т.к. макрос Q_PRIVATE_SLOT отсутствует в открытом описании API, т.е. его нет в Assistant'е"
Завтра троли переделают потраха, и ты будешь плакать


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: Авварон от Февраль 09, 2010, 12:08
там 90% макросов нет. и например они сами его юзают в креаторе


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: ритт от Февраль 18, 2010, 16:59
не переделают завтра...и в 5.0 не переделают, скорее всего...
сам часто использую данную группу макросов и пока что не собираюсь плакать

про сигналы от таймера - я даже не сразу понял суть "открытия"...но это - полная чушь :)
то, что таймер задекларирован в приватной секции не-куобжэкта, не играет абсолютно никакой роли. ровно с таким же успехом сигналы будут испускаться и из мэйна (если создать кореапп и запустить очередь)


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: kuzulis от Февраль 18, 2010, 18:20
Ок, спс за консультэйшен :)


Название: Re: pimpl. Почему работают сигналы из приватного класса?
Отправлено: SABROG от Февраль 21, 2010, 01:44
там 90% макросов нет. и например они сами его юзают в креаторе

На самом деле код QtCreator'a оставил удручающее впечатление. Тролли вовсю используют приватные/недокументированные вещи из исходников Qt вместо того, чтобы исправить саму библиотеку и документацию сделав их публичными. Это лишь говорит о том, что сами тролли при создании QtCreator'a столкнулись с задачами, которые либо сложно либо невозможно решить публичным API Qt. Кстати запросто можно испустить сигнал любого объекта как буд-то он сам его сгенерил:

Код
C++ (Qt)
#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>
 
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <QtCore/QMetaMethod>
 
class Object : public QObject
{
   Q_OBJECT;
 
public:
 
   Object (QObject* parent = 0) : QObject(parent) {}
 
   virtual ~Object() {}
 
public slots:
 
   void onTimeout()
   {
       qDebug() << "Time is out. Sender: " << sender();
       qApp->quit();
   }
};
 
/* Another, more complex method
void emitSignal(QObject* obj, const char* name)
{
   QMetaObject const* mo = obj->metaObject();
   int ind = mo->indexOfMethod(QMetaObject::normalizedSignature(name));
   QMetaObject::activate(obj, ind, 0);
}
*/

 
int main(int argc, char* argv[])
{
   QCoreApplication a(argc, argv);
   Object object;
   QTimer timer;
   QObject::connect(&timer, SIGNAL(timeout()), &object, SLOT(onTimeout()), Qt::QueuedConnection);
   QMetaObject::invokeMethod(&timer, "timeout");
   //emitSignal(&timer, "timeout()"); // more complex method, doesn't support arguments for now
   return a.exec();
}
 
#include "main.moc"
 

Обратите внимание, я нигде не запускаю таймер, а он приходит как QTimer::singleShot(0, &object, SLOT(timeout()), так как был искусственно сгенерирован. На деле, если попытаться написать такое:

Код
C++ (Qt)
emit timer.timeout();
 

То Qt выдаст в консоль ошибку о том, что сигнал является protected. Но конструкция на самом деле рабочая. Задача усложняется лишь тем, что макрос "slots" помещает сигналы всегда в protected секцию и единственный способ сделать сигнал публичным это использовать Q_SIGNAL:

Код:
public:
    Q_SIGNAL void test();

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


Название: Re: pimpl. Почему работают сигналы из приватного класса? [РЕШЕНО]
Отправлено: ритт от Февраль 21, 2010, 08:59
> На самом деле код QtCreator'a оставил удручающее впечатление. Тролли вовсю используют приватные/недокументированные вещи из исходников Qt вместо того, чтобы исправить саму библиотеку и документацию сделав их публичными. Это лишь говорит о том, что сами тролли при создании QtCreator'a столкнулись с задачами, которые либо сложно либо невозможно решить публичным API Qt.

будь добр, приведи несколько примеров


Название: Re: pimpl. Почему работают сигналы из приватного класса? [РЕШЕНО]
Отправлено: SABROG от Февраль 21, 2010, 16:52
/src/libs/utils/consoleprocess_win.cpp

Код
C++ (Qt)
#include <QtCore/private/qwineventnotifier_p.h>
   ...
   processFinishedNotifier = new QWinEventNotifier(m_pid->hProcess, this);
   connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(stubExited()));
 

/src/libs/qtconcurrent/multitask.h

Код
C++ (Qt)
(Class::*fn)(QFutureInterface<T> &)
 

/src/plugins/qt4projectmanager/qtmodulesinfo.cpp

Код
C++ (Qt)
Q_GLOBAL_STATIC_WITH_INITIALIZER(itemVectorType, staticItemVector, {
   *x = itemVector();
});