Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Связка слота и сигнала разных объектов
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
AD
Такая ситуация. Есть виджет отрисовки карты, который я переопределил (сделал наследника от QWidget). В нем есть слот, который определяется, когда я в контекстном меню нажимаю определенную галочку.
Каким образом мне определить слот в главном окне так, чтобы он реагировал на то же самое действие?


Чтобы не быть голословным, вот небольшой примерчик кода:
/// Класс виджета карты с траектории
class QTrackWidget: public QWidget
{
    Q_OBJECT

private:
    bool measure_flag;                ///< флаг, сигнализирующий о моменте измерения

private:
       void initActionMeasurement();

private slots:
    void check(bool f);

// anything
};

/// Слот нажатия на опцию измерения расстояния
void QTrackWidget::check(bool f)
{
    if(f) setCursor(Qt::CrossCursor);
    else
    {
        measure_flag = false;
        calc_distance -> clear();
    }
}

/// Инициализация опции для измерения расстояний
void QTrackWidget::initActionMeasurement()
{
    // anything
    connect(measureAction, SIGNAL(toggled(bool)), this, SLOT(check(bool)));
}
spirit
можно создать сигнал в наследуемом виджете , а потом сконнектить его в главном окне с нужным слотом.

или я вопрос не понял?
Litkevich Yuriy
Цитата(spirit @ 15.1.2009, 17:21) *
можно создать сигнал в наследуемом виджете , а потом сконнектить его в главном окне с нужным слотом.

или я вопрос не понял?
полностью аналогично
AD
Сделал вот так вот:
class QTrackWidget: public QWidget
{

signals:
    void checked_dist_measure(bool is_press);

};


/// Слот отображающий в поле вывода координат измеряемое расстояние
void TLV::measureDistance(bool is_measure)
{
    if(!is_measure) return;
    emit trackView -> checked_dist_measure(is_measure);
    editCoords -> setText("Distance measurement");
}


//
connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));
//

Выдает ошибку, что checked_dist_measure - защищенная функция. что я не так сделал? Как тогда следует поступить?
spirit
а Q_OBJECT где?
AD
Цитата(spirit @ 15.1.2009, 14:55) *
а Q_OBJECT где?

ты про что? Не понял. Есть он и в наследнике от QWidget и в классе главного окна (TLV)
spirit
void TLV::measureDistance(bool is_measure)
{
    if(!is_measure) return;
    emit trackView -> checked_dist_measure(is_measure);//это неверно
    editCoords -> setText("Distance measurement");
}



нельзя так вызывать сигнал. сделайте обертку в наследуемом классе типа _q_emit_cheched.... и там делаете делайте emit checked_dist_measure(is_measure) или же через QMetaObject::invokeMethod.

но я вот не пойму зачем такой наворот? разве сразу нельзя вызвать, что надо?




Цитата(AD @ 15.1.2009, 13:58) *
Цитата(spirit @ 15.1.2009, 14:55) *
а Q_OBJECT где?

ты про что? Не понял. Есть он и в наследнике от QWidget и в классе главного окна (TLV)


Q_OBJECT нужно вставлять и для наследуемых классов иначе мок не сгенерит нужную инфу и сигналы-слоты работать не будут.


Цитата
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
AD
Цитата(spirit @ 15.1.2009, 15:02) *
void TLV::measureDistance(bool is_measure)
{
    if(!is_measure) return;
    emit trackView -> checked_dist_measure(is_measure);//это неверно
    editCoords -> setText("Distance measurement");
}



нельзя так вызывать сигнал. сделайте обертку в наследуемом классе типа _q_emit_cheched.... и там делаете делайте emit checked_dist_measure(is_measure) или же через QMetaObject::invokeMethod.

но я вот не пойму зачем такой наворот? разве сразу нельзя вызвать, что надо?

Как именно сделать? Можете показать, пожалуйста? Я не совсем понимаю каким образом это сделать? В каком смысле вызвать напрямую? :)
spirit
вот еть код

void TLV::measureDistance(bool is_measure){


    if(!is_measure) return;
    emit trackView -> checked_dist_measure(is_measure);//это неверно
    editCoords -> setText("Distance measurement");
}



в нем вы пытаетесь послать сигнал checked_dist_measure , который находится в другом класс (это уже неверно т.к. все сигналы это protected методы, компиль ругается правильно :) ) и шлете его самому себе, т.е. TLV судя по коннетку


connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));


а нельзя ли сделать вот так?

void TLV::measureDistance(bool is_measure){


    if(!is_measure) return;
  measureDistance(bool);  
    editCoords -> setText("Distance measurement");
}
Litkevich Yuriy
AD, сигнал должен посылать тот класс в котором этот сигнал есть, т.е. checked_dist_measure(bool is_press) из QTrackWidget

Цитата(AD @ 15.1.2009, 17:18) *
Чтобы не быть голословным, вот небольшой примерчик кода:
кто такой measureAction в этом примере?
AD
Цитата(Litkevich Yuriy @ 15.1.2009, 15:11) *
кто такой measureAction в этом примере?

Опция (QAction), которая включает нужный мне флажок.
Цитата
а нельзя ли сделать вот так?
void TLV::measureDistance(bool is_measure){


    if(!is_measure) return;
  measureDistance(bool);  
    editCoords -> setText("Distance measurement");
}

А где ее вызывать?
Еще раз: я тот слот вызываю всегда, когда у меня идет переключение опции. Мне бы хотелось, чтобы при этом же переключении вызывалась функция из другого класса. Как это можно реализовать? :) Заранее благодарен за помощь....
spirit
тогда этот сигнал надо перенести в TLV и тогда сделать так

void TLV::measureDistance(bool is_measure){
    if(!is_measure) return;
    emit checked_dist_measure(is_measure);
    editCoords -> setText("Distance measurement");
}
....


connect(m_tvlWidget, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));

....
AD
м... а потом сделать указатель на главное окно программы в том виджете и вызвать указанный слот? Или я опять что-то путаю?
kwisp
Цитата(AD @ 15.1.2009, 16:05) *
м... а потом сделать указатель на главное окно программы в том виджете и вызвать указанный слот? Или я опять что-то путаю?

вот видишь ты путаешь а я вообще не пойму.... напиши поподробнее что хочешь сделать. помогу чем могу.
AD
Есть галочка, которую я переключаю в контекстном меню в виджете карты. Необходимо, чтобы при переключении этой галочки, срабатывал слот в другом классе, классе главного окна, в котором содержится данный виджет.
spirit
т.е. иерархия классов такая: главный виджет -> карта ?
AD
Цитата(spirit @ 15.1.2009, 16:34) *
т.е. иерархия классов такая: главный виджет -> карта ?

угу. Только они не связаны наследованием. А скорее это композиция.
spirit
я понял. "карта" обрабатывает клик в контекстном меню или "главное окно"?

если "карта", то создать сигнал в "карте" и связать его с "главным окном", а уже в "главном окне" создать слот


который будет дергать нужный слот "другого" класса.
AD
Вот скрин:
Нажмите для просмотра прикрепленного файла
Задача у меня следующая. При нажатии на "Distance measurement", в поле где отображаются координаты появлялась следующая фраза "Distance measurement". Когда галку убираем, снова были видны координаты. Вот, чего хочу достигнуть!
Litkevich Yuriy
Цитата(AD @ 15.1.2009, 17:18) *
Такая ситуация. Есть виджет отрисовки карты, который я переопределил (сделал наследника от QWidget).
Этот виджет называется QTrackWidget? Если да, то идем дальше:

Цитата(AD @ 15.1.2009, 17:18) *
В нем есть слот, который определяется, когда я в контекстном меню нажимаю определенную галочку.
галочка, видимо, посылает сигнал. Который соединен в классе QTrackWidget с его же слотом? Если да, то идем дальше:

Цитата(AD @ 15.1.2009, 17:18) *
Каким образом мне определить слот в главном окне так, чтобы он реагировал на то же самое действие?
слот определяется как обычно.

Ключевой момент здесь это сигнал от галочки, нужно его сделать доступным из главного окна. Если галочка принадлежит виджету QTrackWidget, то делать галочку видимой для других классов плохой вариант. Я бы сделал так:

В классе QTrackWidget определил бы сигнал "щелкнули галочку", а в конструкторе QTrackWidget, где видимо создается галочка и соеденяется со слотом QTrackWidget'а, соеденил сигнал галочки с сигналом QTrackWidget::"щелкнули галочку". Тогда сигнал будет еще и ретранслироватся (предаваться дальше).
Затем в главном окне после создания экземпляра QTrackWidget, соеденил бы сигнал QTrackWidget::"щелкнули галочку" с подходящим слотом главного окна.
AD
Цитата(spirit @ 15.1.2009, 16:40) *
я понял. "карта" обрабатывает клик в контекстном меню или "главное окно"?

Клик обратывается в контекстном меню! Т.е. как я понял, создать в главном потоке сигнал, который соединить с сигналом, обрабатывающимся в карте?
spirit
вы меня не поняли.

Цитата
Клик обратывается в контекстном меню!


вы где обрабатывает клик? сконнектили вы необходимую акцию из контекстного меню для дальнейшей обработки?

думаю да, вот я испрасл, где эта обработка делается?
SABROG
Цитата(AD @ 15.1.2009, 16:44) *
сигнал...соединить с сигналом


Да, emit одного сигнала повлечет цепную реакцию и будет пущен второй сигнал. Если при этом есть коннекты на оба сигнала, то будет вызвано 2 слота.
kwisp
Цитата(Litkevich Yuriy @ 15.1.2009, 16:44) *
Тогда сигнал будет еще и ретранслироватся (предаваться дальше).

что то я ответить не успел:(
согласен так и стоит делать....это кстати одна из причин существования сигналов ислотов ретранслировать событие по иерархии вверх....
AD
Блин, ну не попадает он в этот слот. Как поправить, прям не знаю.

Вот код:
class QTrackWidget: public QWidget
{
    Q_OBJECT

private:
    QAction* measureAction;            ///< опция для измерения расстояний

private slots:
    void check(bool f);

signals:
    void checked_measure(bool is_press);
};

/// Слот нажатия на опцию измерения расстояния
void QTrackWidget::check(bool f)
{
    if(f) setCursor(Qt::CrossCursor);
    else
    {
        measure_flag = false;
        calc_distance -> clear();
    }
    emit checked_measure(f);
}

/// Инициализация опции для измерения расстояний
void QTrackWidget::initActionMeasurement()
{
    //что-то )
    connect(measureAction, SIGNAL(toggled(bool)), this, SLOT(check(bool)));
    connect(measureAction, SIGNAL(toggled(bool)), this, SLOT(check(bool)));
}

/// Класс главного окна [желательно многие функции вынести из него]
class TLV: public QMainWindow, public TLVClass
{
    Q_OBJECT

signals:
    void checked_dist_measure(bool is_press);

private slots:
    void measureDistance(bool is_measure);
};

/// Слот отображающий в поле вывода координат измеряемое расстояние
void TLV::measureDistance(bool is_measure)
{
    emit checked_dist_measure(is_measure);
    if(is_measure) editCoords -> setText("Distance measurement");
    update();
}

TLV::TLV(QWidget *parent, Qt::WFlags flags)
{
connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));
    connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SIGNAL(checked_measure(bool)));
}


Где я мог ошибиться?
Litkevich Yuriy
Цитата(AD @ 15.1.2009, 20:21) *
в этот слот
какой?

Цитата(AD @ 15.1.2009, 20:21) *
this, SIGNAL(checked_measure(bool)));
тут?

AD, добавление консоли к Qt приложению сильно помагает, MOC будет тебе сообщать о казусах
AD
Цитата(Litkevich Yuriy @ 15.1.2009, 17:37) *
какой?

тут?

AD, добавление консоли к Qt приложению сильно помагает, MOC будет тебе сообщать о казусах

Не попадает в слот void measureDistance(bool is_measure)
Что я должен написать, чтобы увидеть казусы moc? Их можно через дебаггер увидеть а не консоль? Если нельзя, воспользуюсь консолью, но что надо туда выводить?
Litkevich Yuriy
Цитата(AD @ 15.1.2009, 20:42) *
но что надо туда выводить?
ничего, главное сборка в режиме отладки и наличие консоли.
kwisp
Цитата(Litkevich Yuriy @ 15.1.2009, 17:45) *
Цитата(AD @ 15.1.2009, 20:42) *
но что надо туда выводить?
ничего, главное сборка в режиме отладки и наличие консоли.



можно к проекту добавить консоль при первом qmake CONFIG+=console
либо прямо в файле *.pro
как ты раньше без неё жил???

кстати можешь в лог выводить при помощи Qt или std::cout у точно узнаешь попадаешь в слот или нет.
Litkevich Yuriy
AD, ты всех запутал своими примерами. Если у тебя есть какие-то отношения, в данном случае сигнально-слотовые.
то ты должен выкидывать из кода, для примеров, только нисущественные моменты, а все существенные оставить. Т.е. участников отношений.
/// Класс виджета карты с траектории
class QTrackWidget: public QWidget
{
    Q_OBJECT

private:
    bool measure_flag;                ///< флаг, сигнализирующий о моменте измерения

private:
       void initActionMeasurement();

private slots:
    void check(bool f);

// anything
};

/// Слот нажатия на опцию измерения расстояния
void QTrackWidget::check(bool f)
{
    if(f) setCursor(Qt::CrossCursor);
    else
    {
        measure_flag = false;
        calc_distance -> clear();
    }
}

/// Инициализация опции для измерения расстояний
void QTrackWidget::initActionMeasurement()
{
    // anything
    connect(measureAction, SIGNAL(toggled(bool)), this, SLOT(check(bool)));
}

здесь есть слот QTrackWidget::check(bool f); Есть соединение с этим слотом connect(measureAction, SIGNAL(toggled(bool)), this, SLOT(check(bool)));
некого нечто measureAction, кто такой откуда? (где объявлен, где создан экзепляр, приведи код)
Зато есть мусор:
    if(f) setCursor(Qt::CrossCursor);
    else
    {
        measure_flag = false;
        calc_distance -> clear();
    }
который неважен.

Непонятно как используется класс QTrackWidget, где создается его экземпляр.
AD
Цитата(Litkevich Yuriy @ 15.1.2009, 17:56) *
AD, ты всех запутал своими примерами. Если у тебя есть какие-то отношения, в данном случае сигнально-слотовые.
то ты должен выкидывать из кода, для примеров, только нисущественные моменты, а все существенные оставить. Т.е. участников отношений.
Непонятно как используется класс QTrackWidget, где создается его экземпляр.

Этот класс создается сразу при открытии программы. Ведь это карта. Тот слот, который ты считаешь не нужным, как раз и используется для показа где используется сигнал checked_measure для вызова сигнала checked_dist_measure, который и вызывает слот void measureDistance(bool is_measure).
Если бы знать, как в студии консоль прикрепить! :unsure:
kwisp
что в студии компилятор не запускается уже из командной строки?
qmake -project CONFIG+=console
qmake
nmake - ну или как он там??? и всё любуйся на конссоль.
Litkevich Yuriy
Цитата(AD @ 15.1.2009, 21:03) *
Этот класс создается сразу при открытии программы.
приведи в пример код.

Цитата(AD @ 15.1.2009, 21:03) *
Тот слот, который ты считаешь не нужным,
читаешь не внимательно:
Цитата(Litkevich Yuriy @ 15.1.2009, 20:56) *
Зато есть мусор:
    if(f) setCursor(Qt::CrossCursor);
    else
    {
        measure_flag = false;
        calc_distance -> clear();
    }


который неважен.
это тело слота
Цитата(AD @ 15.1.2009, 21:03) *
для показа где используется сигнал checked_measure
нет такого места вэтом слоте
Цитата(AD @ 15.1.2009, 21:03) *
для вызова сигнала checked_dist_measure,
тоже нет кода.

предлагаю тебе попорядку заново привести важные части исходников. Тогда будет и понятно и просто привести тебе пример что куда нужно дописать.
spirit
или компилябельный пример прикрепи :)

ЗЫ. в студии есть окно "Output" туда выводится вся дебажная инфа, конечно если приложение собирается в дебажном режиме.
AD
/// Класс виджета карты с траектории
class QTrackWidget: public QWidget
{
    Q_OBJECT

private:
    QAction* measureAction;            ///< опция для измерения расстояний

private slots:
    void check(bool f);

signals:
    void checked_measure(bool is_press);
};
/// Слот нажатия на опцию измерения расстояния
void QTrackWidget::check(bool f)
{
    ...// anything
    emit checked_measure(f);
}

class Ui_TLVClass
{
public:
    QTrackWidget *trackView;

public:
    void setupUi(QMainWindow *TLVClass);                // setupUi
    void retranslateUi(QMainWindow *TLVClass);            // retranslateUi
};

namespace Ui {
    class TLVClass: public Ui_TLVClass {};
} // namespace Ui

/// Класс главного окна [желательно многие функции вынести из него]
class TLV: public QMainWindow, public TLVClass
{
    Q_OBJECT

signals:
    void checked_dist_measure(bool is_press);

private slots:
    void pressMenu();
    void openRecentFiles();
    void invisibleProgress();
    void pressInnerMenu();
    void measureDistance(bool is_measure);

};

TLV::TLV
{
connect(trackView, SIGNAL(checked_measure(bool)), this,      SIGNAL(checked_dist_measure(bool)));
    connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));
}


/// Слот отображающий в поле вывода координат измеряемое расстояние
void TLV::measureDistance(bool is_measure)
{
    emit checked_dist_measure(is_measure);
    if(is_measure) editCoords -> setText("Distance measurement");
    update();
}
spirit
судя по коду этого сигнала checked_dist_measure нет в QTrackWidget вот поэтому слот TLV::measureDistance не вызывается.
kwisp
TLV::TLV
{
connect(trackView, SIGNAL(checked_measure(bool)), this,      SIGNAL(checked_dist_measure(bool)));
    connect(trackView, SIGNAL(checked_dist_measure(bool)), this, SLOT(measureDistance(bool)));
}


почему так?
один и тот же сигнал один и тотже объект обработчик один и тот же набор параметров.
почему бы не включить вызов сигнала checked_dist_measure(bool) в слот measureDistance(bool) и соединений на одно станет меньше.:)
AD
Цитата(spirit @ 15.1.2009, 18:28) *
судя по коду этого сигнала checked_dist_measure нет в QTrackWidget вот поэтому слот TLV::measureDistance не вызывается.

Вот это откровение для меня, если честно. Если он там нужен, где его там употребить?
Litkevich Yuriy
AD, отредактируй свой пост, чтобы показать в классе TLV объявления всех его слотов и сигналов
spirit
Цитата(AD @ 15.1.2009, 17:29) *
Цитата(spirit @ 15.1.2009, 18:28) *
судя по коду этого сигнала checked_dist_measure нет в QTrackWidget вот поэтому слот TLV::measureDistance не вызывается.

Вот это откровение для меня, если честно. Если он там нужен, где его там употребить?


имхо, вот все что нужно


TLV::TLV(...)
{
connect(trackView, SIGNAL(checked_measure(bool)), this,      SIGNAL(measureDistance(bool)));
}


а если надо слать сигнал из TLV еще куда-то, то либо в слоте measureDistance делать emit вот так

class TLV ...

{

Q_OBJECT

public:

    TLV(...);

signals:

  void measureChanged(bool changed);

};

void TLV::measure(bool changed)

{

  ....

emit measureChanged(changed);

.....

}
Litkevich Yuriy
Цитата(kwisp @ 15.1.2009, 21:28) *
почему так?
вот после редактирования поста № 35 (! :o: ) стало видно ошибки:
у trackView нет сигнала checked_dist_measure

Цитата(AD @ 15.1.2009, 21:29) *
Вот это откровение для меня, если честно. Если он там нужен, где его там употребить?
нарисуй на бумажке квадратики (классы) и соедени их стралками (сигнал/слот) как они должны общатся, и сразу поймешь, где и что добавить.
AD
Эврика, заработало! :)

Litkevich Yuriy, kwisp, spirit, спасибо огромное.

P.S. "спасибо" уже поставил, поэтому благодарю так! :)))
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2022 IPS, Inc.