Интересные обсуждения

темы заинтересовавшие velkin

Визуализация графа соединений сигналов и слотов в Qt

nen777w nen777w
Добрый час!
Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.
По сути на уровне Qt это можно было бы сделать, но не сделали.
А я пока смог найти вот такое наколенное подделие которое умеет писать файлы для graphviz .
SaZ
SaZ Граф сигнал-слотов
15.09.2014 08:14
Здравствуйте, nen777w, Вы писали:

N>Добрый час!

N>Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.
N>По сути на уровне Qt это можно было бы сделать, но не сделали.
N>А я пока смог найти вот такое наколенное подделие которое умеет писать файлы для graphviz .

А в чём проблема написать самому? Через метаинформацию от объекта.

Под кутэ 4 в компайл тайме не получится в принципе. Парсинг исходников — дело не благодарное. Это не сильно проще будет, чем MOC на CLANG'e переделать
Под кутэ 5 — те же проблемы, только плюс, можно коннектить сигналы на статические методы / функции.
nen777w
nen777w
16.09.2014 07:55
N>>Добрый час!
N>>Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.

SaZ>А в чём проблема написать самому? Через метаинформацию от объекта.

Да не проблема, наверно. Надо попробовать. Другое дело на это не хватает времени как всегда

SaZ>Под кутэ 4 в компайл тайме не получится в принципе. Парсинг исходников — дело не благодарное.

SaZ>Под кутэ 5 — те же проблемы, только плюс, можно коннектить сигналы на статические методы / функции.
Да, но это IMHO наиболее точный способ увидеть всё сразу.

SaZ>Это не сильно проще будет, чем MOC на CLANG'e переделать

Тут не понял немного.
SaZ
SaZ
17.09.2014 06:41
Здравствуйте, nen777w, Вы писали:

SaZ>>Это не сильно проще будет, чем MOC на CLANG'e переделать

N>Тут не понял немного.

Слышал, что есть проект , где на основе llvm написали moc компилятор.
Возможно, если пошаманить, то можно будет вытащить инфу о коннекшенах в удобоваримом виде. Но это так, мысли в слух (мнелишьбы314здануть).
Honduras
Honduras Граф сигнал-слотов
19.03.2015 05:30
Здравствуйте, nen777w, Вы писали:

N>Добрый час!


А где вообще хранится связь сигнал-слот, в проекте

Я поискал по всем файлам on_pushButton_XYZ_clicked в хедере, сишнике присутствуют и в генерируемом МОС_СРР и нет в UI! остается только по имени on_..._clicked
nen777w
nen777w
19.03.2015 09:24
H>А где вообще хранится связь сигнал-слот, в проекте
Нигде, только в генерённых moc файлах, которые надо парсить (вместе с исходными) разбирать и строить.

H>Я поискал по всем файлам on_pushButton_XYZ_clicked в хедере, сишнике присутствуют и в генерируемом МОС_СРР и нет в UI! остается только по имени on_..._clicked

Эгэж.
Igore
Igore Граф сигнал-слотов
20.03.2015 06:39
Здравствуйте, nen777w, Вы писали:

N>Добрый час!

N>Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.
Давным давно был такое проект, может он тебе поможет и натолкнет на мысли
SaZ
SaZ Граф сигнал-слотов
25.03.2015 10:00
Нарыл такой интересный репозиторий: https://github.com/KDAB/GammaRay

Руки пока не дошли, чтобы поковырять. Но походу это круто.
Igore
Igore
28.05.2015 08:36
Здравствуйте, SaZ, Вы писали:

SaZ>Нарыл такой интересный репозиторий: https://github.com/KDAB/GammaRay

SaZ>Руки пока не дошли, чтобы поковырять. Но походу это круто.
Тоже пока руки не дошли посмотреть что это , но описание понравилось http://habrahabr.ru/company/infopulse/blog/258411/
velkin
velkin Граф сигнал-слотов
02.05.2015 04:07
Здравствуйте, nen777w, Вы писали:

N>Добрый час!

N>Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.
N>По сути на уровне Qt это можно было бы сделать, но не сделали.
N>А я пока смог найти вот такое наколенное подделие которое умеет писать файлы для graphviz .

Для начала нужно понять, что все соединения и разъединения сигналов и слотов делаются только во время исполнения, во время компиляции ничего не происходит. В принципе можно было бы доставать информацию о текущих соединениях, в своё время даже покопался в исходниках Qt, но тут углубляемся в тему Private Classes, D-Pointer и так далее. Если нет времени, лучше забить на это дело.
SaZ
SaZ
04.05.2015 10:14
Здравствуйте, velkin, Вы писали:

V>Для начала нужно понять, что все соединения и разъединения сигналов и слотов делаются только во время исполнения, во время компиляции ничего не происходит.

Сами соединения — да. А вот в коде есть определённые номера строк, которые за это отвечают. Вопрос в том, что мы хотим получить.

V>В принципе можно было бы доставать информацию о текущих соединениях, в своё время даже покопался в исходниках Qt, но тут углубляемся в тему Private Classes, D-Pointer и так далее.

Чтобы получить эту инфу не нужно лезть в исходники. Тем более в private. Достаточно почитать документацию по метасистеме Qt. Вопрос в наличии готовой реализации с приемлемым выхлопом.

V>Если нет времени, лучше забить на это дело.

Если без этого практически невозможно понять, как работает код, то есть смысл потратить на это время, чтобы не стоять на месте.
velkin
velkin
04.05.2015 12:11
Здравствуйте, SaZ, Вы писали:

SaZ>Здравствуйте, velkin, Вы писали:

V>>Для начала нужно понять, что все соединения и разъединения сигналов и слотов делаются только во время исполнения, во время компиляции ничего не происходит.
SaZ>Сами соединения — да. А вот в коде есть определённые номера строк, которые за это отвечают. Вопрос в том, что мы хотим получить.

Анализировать исходный код изначально неправильно, это значит, что есть глубокое непонимание исходных принципов работы метаобъектной системы Qt. Сразу оговорюсь, что сам использую Qt 4.8.x и говорю именно о нём.

V>>В принципе можно было бы доставать информацию о текущих соединениях, в своё время даже покопался в исходниках Qt, но тут углубляемся в тему Private Classes, D-Pointer и так далее.

SaZ>Чтобы получить эту инфу не нужно лезть в исходники. Тем более в private. Достаточно почитать документацию по метасистеме Qt. Вопрос в наличии готовой реализации с приемлемым выхлопом.

Вот автор и почитал документацию, если знаете как это сделать с помощью метаобъектной системы напишите в доказательство код. Проблема в том, что можно затереть мануалы до дыр и прийти к тому, что так просто это не сделать.

Есть ли утилита позволяющая визуализировать в виде графа подключения сигнал-слотов, в компайл тайме или даже лучше в рантайме.
По сути на уровне Qt это можно было бы сделать, но не сделали.


Лично я пришёл к двум ответам. Один из них получать информацию с самого объекта, другой строить свои списки. По второму варианту есть множество способов реализации, но это своего рода костыль, хотя я бы не отметал его сразу, в некоторых случаях вариант хорош, ведь свой список может обладать любыми возможностями.

Что касается первого варианта, то для начала нужно понять, что происходит во время соединения и разъединения.
/*! \internal
   Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex

    method_index is relative to the rmeta metaobject, if rmeta is null, then it is absolute index
 */
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    QObjectPrivate::StaticMetaCallFunction callFunction =
        (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
        ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

    if (type & Qt::UniqueConnection) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (c2->receiver == receiver && c2->method() == method_index_absolute)
                    return false;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

    QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
    c->sender = s;
    c->receiver = r;
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->argumentTypes = types;
    c->nextConnectionList = 0;
    c->callFunction = callFunction;

    QT_TRY {
        QObjectPrivate::get(s)->addConnection(signal_index, c);
    } QT_CATCH(...) {
        delete c;
        QT_RETHROW;
    }

    c->prev = &(QObjectPrivate::get(r)->senders);
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;

    QObjectPrivate *const sender_d = QObjectPrivate::get(s);
    if (signal_index < 0) {
        sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
    } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
        sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
    }

    return true;
}


/*! \internal
    Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex
 */
bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
                                    const QObject *receiver, int method_index,
                                    DisconnectType disconnectType)
{
    if (!sender)
        return false;

    QObject *s = const_cast<QObject *>(sender);

    QMutex *senderMutex = signalSlotLock(sender);
    QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0;
    QOrderedMutexLocker locker(senderMutex, receiverMutex);

    QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
    if (!connectionLists)
        return false;

    // prevent incoming connections changing the connectionLists while unlocked
    ++connectionLists->inUse;

    bool success = false;
    if (signal_index < 0) {
        // remove from all connection lists
        for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) {
            QObjectPrivate::Connection *c =
                (*connectionLists)[signal_index].first;
            if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) {
                success = true;
                connectionLists->dirty = true;
            }
        }
    } else if (signal_index < connectionLists->count()) {
        QObjectPrivate::Connection *c =
            (*connectionLists)[signal_index].first;
        if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) {
            success = true;
            connectionLists->dirty = true;
        }
    }

    --connectionLists->inUse;
    Q_ASSERT(connectionLists->inUse >= 0);
    if (connectionLists->orphaned && !connectionLists->inUse)
        delete connectionLists;

    return success;
}


/*
    This vector contains the all connections from an object.

    Each object may have one vector containing the lists of
    connections for a given signal. The index in the vector correspond
    to the signal index. The signal index is the one returned by
    QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal).
    Negative index means connections to all signals.

    This vector is protected by the object mutex (signalSlotMutexes())

    Each Connection is also part of a 'senders' linked list. The mutex
    of the receiver must be locked when touching the pointers of this
    linked list.
*/
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
{
public:
    bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse
    bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
    int inUse; //number of functions that are currently accessing this object or its connections
    QObjectPrivate::ConnectionList allsignals;

    QObjectConnectionListVector()
        : QVector<QObjectPrivate::ConnectionList>(), orphaned(false), dirty(false), inUse(0)
    { }

    QObjectPrivate::ConnectionList &operator[](int at)
    {
        if (at < 0)
            return allsignals;
        return QVector<QObjectPrivate::ConnectionList>::operator[](at);
    }
};


Или вот QObject-Internals. Помнится, когда пытался решить эту задачу в линуксе меня ещё заставили поставить libqt4-private-dev, иначе не было нужной зависимости, а это плохо с точки зрения простой компиляции. Причём речь всего лишь об извлечении информации о соединениях, а не о построении графа, который лично я бы строил с помощью QGraphicsScene.

V>>Если нет времени, лучше забить на это дело.

SaZ>Если без этого практически невозможно понять, как работает код, то есть смысл потратить на это время, чтобы не стоять на месте.

Лёгкой победы не будет, нужно знать Qt на более глубоком уровне, а у автора нет времени, потому и говорю забить. Может это не критичный функционал с низким приоритетом. К тому же ещё нужно дать ответ на вопрос дублировать или извлекать списки соединений. Где-то на просторах интернета было решение.