Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Общие вопросы _ Связка слота и сигнала разных объектов

Автор: AD 15.1.2009, 14:18

Такая ситуация. Есть виджет отрисовки карты, который я переопределил (сделал наследника от 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 15.1.2009, 14:21

можно создать сигнал в наследуемом виджете , а потом сконнектить его в главном окне с нужным слотом.

или я вопрос не понял?

Автор: Litkevich Yuriy 15.1.2009, 14:29

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

или я вопрос не понял?
полностью аналогично

Автор: AD 15.1.2009, 14:52

Сделал вот так вот:

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 15.1.2009, 14:55

а Q_OBJECT где?

Автор: AD 15.1.2009, 14:58

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

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

Автор: 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.

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




Цитата(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 15.1.2009, 15:04

Цитата(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 15.1.2009, 15:09

вот еть код

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 15.1.2009, 15:11

AD, сигнал должен посылать тот класс в котором этот сигнал есть, т.е. checked_dist_measure(bool is_press) из QTrackWidget

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

Автор: AD 15.1.2009, 15:30

Цитата(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 15.1.2009, 15:35

тогда этот сигнал надо перенести в 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 15.1.2009, 16:05

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

Автор: kwisp 15.1.2009, 16:09

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

вот видишь ты путаешь а я вообще не пойму.... напиши поподробнее что хочешь сделать. помогу чем могу.

Автор: AD 15.1.2009, 16:31

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

Автор: spirit 15.1.2009, 16:34

т.е. иерархия классов такая: главный виджет -> карта ?

Автор: AD 15.1.2009, 16:35

Цитата(spirit @ 15.1.2009, 16:34) *
т.е. иерархия классов такая: главный виджет -> карта ?

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

Автор: spirit 15.1.2009, 16:40

я понял. "карта" обрабатывает клик в контекстном меню или "главное окно"?

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


который будет дергать нужный слот "другого" класса.

Автор: AD 15.1.2009, 16:41

Вот скрин:
[attachment=379:dist_galk.JPG]
Задача у меня следующая. При нажатии на "Distance measurement", в поле где отображаются координаты появлялась следующая фраза "Distance measurement". Когда галку убираем, снова были видны координаты. Вот, чего хочу достигнуть!

Автор: Litkevich Yuriy 15.1.2009, 16:44

Цитата(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 15.1.2009, 16:44

Цитата(spirit @ 15.1.2009, 16:40) *
я понял. "карта" обрабатывает клик в контекстном меню или "главное окно"?

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

Автор: spirit 15.1.2009, 16:52

вы меня не поняли.

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


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

думаю да, вот я испрасл, где эта обработка делается?

Автор: SABROG 15.1.2009, 16:53

Цитата(AD @ 15.1.2009, 16:44) *
сигнал...соединить с сигналом


Да, emit одного сигнала повлечет цепную реакцию и будет пущен второй сигнал. Если при этом есть коннекты на оба сигнала, то будет вызвано 2 слота.

Автор: kwisp 15.1.2009, 16:54

Цитата(Litkevich Yuriy @ 15.1.2009, 16:44) *
Тогда сигнал будет еще и ретранслироватся (предаваться дальше).

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

Автор: AD 15.1.2009, 17:21

Блин, ну не попадает он в этот слот. Как поправить, прям не знаю.

Вот код:
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 15.1.2009, 17:37

Цитата(AD @ 15.1.2009, 20:21) *
в этот слот
какой?

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

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

Автор: AD 15.1.2009, 17:42

Цитата(Litkevich Yuriy @ 15.1.2009, 17:37) *
какой?

тут?

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

Не попадает в слот void measureDistance(bool is_measure)
Что я должен написать, чтобы увидеть казусы moc? Их можно через дебаггер увидеть а не консоль? Если нельзя, воспользуюсь консолью, но что надо туда выводить?

Автор: Litkevich Yuriy 15.1.2009, 17:45

Цитата(AD @ 15.1.2009, 20:42) *
но что надо туда выводить?
ничего, главное сборка в режиме отладки и наличие консоли.

Автор: kwisp 15.1.2009, 17:53

Цитата(Litkevich Yuriy @ 15.1.2009, 17:45) *
Цитата(AD @ 15.1.2009, 20:42) *
но что надо туда выводить?
ничего, главное сборка в режиме отладки и наличие консоли.



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

кстати можешь в лог выводить при помощи Qt или std::cout у точно узнаешь попадаешь в слот или нет.

Автор: Litkevich Yuriy 15.1.2009, 17:56

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 15.1.2009, 18:03

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

Этот класс создается сразу при открытии программы. Ведь это карта. Тот слот, который ты считаешь не нужным, как раз и используется для показа где используется сигнал checked_measure для вызова сигнала checked_dist_measure, который и вызывает слот void measureDistance(bool is_measure).
Если бы знать, как в студии консоль прикрепить! :unsure:

Автор: kwisp 15.1.2009, 18:08

что в студии компилятор не запускается уже из командной строки?
qmake -project CONFIG+=console
qmake
nmake - ну или как он там??? и всё любуйся на конссоль.

Автор: Litkevich Yuriy 15.1.2009, 18:13

Цитата(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 15.1.2009, 18:18

или компилябельный пример прикрепи :)

ЗЫ. в студии есть окно "Output" туда выводится вся дебажная инфа, конечно если приложение собирается в дебажном режиме.

Автор: AD 15.1.2009, 18:18

/// Класс виджета карты с траектории
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 15.1.2009, 18:28

судя по коду этого сигнала checked_dist_measure нет в QTrackWidget вот поэтому слот TLV::measureDistance не вызывается.

Автор: kwisp 15.1.2009, 18:28

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 15.1.2009, 18:29

Цитата(spirit @ 15.1.2009, 18:28) *
судя по коду этого сигнала checked_dist_measure нет в QTrackWidget вот поэтому слот TLV::measureDistance не вызывается.

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

Автор: Litkevich Yuriy 15.1.2009, 18:30

AD, отредактируй свой пост, чтобы показать в классе TLV объявления всех его слотов и сигналов

Автор: spirit 15.1.2009, 18:38

Цитата(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 15.1.2009, 18:42

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

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

Автор: AD 15.1.2009, 18:51

Эврика, заработало! :)

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

P.S. "спасибо" уже поставил, поэтому благодарю так! :)))

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)