Qt подключить сигнал к слоту

У меня есть класс главного окна, который содержит виджет QSplitter (среди других виджетов). Содержимое этого сплиттера заполняется виджетами другого класса.

Внутри этого другого Widgwt у меня есть макет с добавленным QPushButton с именем test_btn. У меня также есть функция, определенная в файле заголовка под public slots:, называемая test_function, которая имеет соответствующий код в файле cpp:

cout << "Test!" << endl;

Я пытаюсь вызвать эту функцию, используя следующий код:

connect(test_btn, SIGNAL(clicked()), SLOT(test_function()));

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

Если я добавлю тот же код connect в главное окно, он будет работать (для вызова тестовой функции из главного окна), т.е.

connect(cc_point.test_btn, SIGNAL(clicked()), SLOT(main_win_test_function()));

Я не могу вызвать тестовую функцию из другого класса, если я не вызову ее из main_win_test_function()

Я знаю, что мне не хватает «виджета приемника» в операторе подключения, хотя он работает без него в классе главного окна, и при компиляции я не получаю никаких ошибок.

Нужно ли мне определять родителя или что-то в other_class?

main_window.cpp:

main_window::main_window()
{
    add_splitter();

    QGridLayout *window_layout = new QGridLayout;

    window_layout->setSpacing(0);
    window_layout->setContentsMargins(0, 0, 0, 0);

    window_layout->addWidget(splitter1, 0, 0);

    set_layout(window_layout);
}

void main_window::add_splitter()
{
     other_class oc_point;

     splitter1 = new QSplitter(Qt::Horizontal);

     splitter1->addWidget(oc_point.oc_widget);
}

другой_класс.cpp:

other_class:other_class()
{
     oc_widget = new QWidget;

     QGridLayout *other_layout = new QGridLayout(oc_widget);

     test_btn = new QPushButton;
     test_btn->setText("Test Button");

     connect(test_btn, SIGNAL(clicked()), test_btn->parent(), SLOT(test_function());
     other_layout->addWidget(test_btn);
}

void other_class::test_function()
{
     cout << "Test output" << endl;
}

основной.cpp:

int main(int argc, char *argv[]) {

QApplication app(argc, argv);

main_window window;

window.resize(1100, 700);
window.setWindowTitle("Qt Test");
window.setWindowIcon(QIcon (":/images/icon_1.png"));
window.setMinimumHeight(500);
window.show();

return app.exec();
}

Как я упоминал выше, все это прекрасно работает, поскольку создается окно и отображаются виджеты, но test_function не вызывается, когда я нажимаю кнопку. Дайте мне знать, если мне нужно также включить файлы заголовков.


person user2718679    schedule 26.08.2013    source источник
comment
Теперь вы понимаете, почему публикация кода очень важна. Ошибка была очевидна, как только я посмотрел на нее.   -  person Kuba hasn't forgotten Monica    schedule 27.08.2013


Ответы (2)


В main_window::add_splitter() oc_point уничтожается, как только функция возвращается. Проблема не в соединении, проблема в том, что ваш объект исчезает, прежде чем вы сможете сделать с ним что-нибудь полезное.

Ниже приведен автономный пример, демонстрирующий эту проблему как в Qt 4, так и в 5. Как только вы нажимаете «Удалить другое», кнопка «Тест» не работает.

//main.cpp
#include <QApplication>
#include <QGridLayout>
#include <QSplitter>
#include <QPushButton>
#include <QMessageBox>

class Other : public QObject
{
    Q_OBJECT
    QWidget *m_widget;
    Q_SLOT void testFunction() {
        QMessageBox::information(NULL, "Test", "Slot works.");
    }
public:
    QWidget *widget() const { return m_widget; }
    Other(QObject *parent = 0) : m_widget(new QWidget), QObject(parent) {
        QGridLayout *l = new QGridLayout(m_widget);
        QPushButton *btn = new QPushButton("Test Button");
        l->addWidget(btn);
        connect(btn, SIGNAL(clicked()), this, SLOT(testFunction()));
    }
    ~Other() { if (!m_widget->parent()) delete m_widget; }
};

class Main : public QWidget
{
public:
    Main(QWidget *parent = 0, Qt::WindowFlags f = 0) : QWidget(parent, f) {
        QGridLayout *l = new QGridLayout(this);
        QPushButton *btn = new QPushButton("Delete Other");
        Other *o = new Other(this);
        QSplitter *spl = new QSplitter;
        spl->addWidget(o->widget());
        l->addWidget(spl);
        l->addWidget(btn);
        connect(btn, SIGNAL(clicked()), o, SLOT(deleteLater()));
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Main w;
    w.show();
    return a.exec();
}

#include "main.moc"
person Kuba hasn't forgotten Monica    schedule 26.08.2013
comment
Спасибо за быстрый ответ. Я тоже пробовал это, и это тоже не сработало, возможно ли, что потому, что у меня есть Qwidget с layout в основном классе, у которого есть Qsplitter, который затем имеет «oc_point.QWidget», который, в свою очередь, имеет layout с test_btn добавлено, что родитель test_btn не имеет слота test_function()? (Возможно, это сбивает с толку, поэтому дайте мне знать, если мне будет проще добавить мой фактический код) - person user2718679; 26.08.2013
comment
Вам обязательно нужно показать свой код. Сделайте самодостаточный однофайловый пример. Никаких .h файлов, ничего подобного. Только один файл main.cpp и один файл .pro. Добавьте #include "main.moc" в конце, чтобы вам не понадобился отдельный заголовочный файл. Повторно запустите quake, пересоберите и опубликуйте здесь, как только убедитесь, что он работает и демонстрирует проблему. В противном случае это пустая трата времени для всех. - person Kuba hasn't forgotten Monica; 26.08.2013
comment
Как добавить other_widget к splitter1 без уничтожения объекта? Должен ли я возвращать объект или идентификатор виджета или что-то еще, или есть лучший способ сделать это без oc_point, потому что, очевидно, попытка добавить его с помощью other_class.other_widget дает мне «ожидаемое первичное выражение перед». ошибка' - person user2718679; 27.08.2013
comment
Все QWidgets являются QObjects. Просто создайте новый объект в куче, а не в стеке, и сделайте его принадлежащим чему-то, что живет столько, сколько необходимо. Это все. Ваша проблема в коде, который вы представляете, заключается в том, что oc_point находится в стеке, а не в куче. Это все. - person Kuba hasn't forgotten Monica; 27.08.2013

После того, как ваши объекты инициализированы, вы должны соединить их друг с другом. Явное указание, чьи слоты чьи и чьи сигналы чьи, очень поможет.

Иногда я делаю вспомогательную функцию void Widget::connectTo__Class__(Class * class_ptr);

Затем в этом вызове функции я соединю слоты, пересекающие границы класса/родителя/наследования.

Вот пример реализации:

void Widget::connectTo__Class__(Class * class_ptr)
{
    QObject::connect(this,      SIGNAL(myWidgetSignal()), 
                     class_ptr, SLOT(myClassSlot()));

    QObject::connect(class_ptr, SIGNAL(myClassSignal()), 
                     this,      SLOT(myWidgetSlot()));
}

Также имейте в виду, что среда выполнения будет жаловаться, если вы делаете что-то неправильно, например, если class_ptr является null или если myClassSignal не существует. Вы можете увидеть эти ошибки в выводе приложения, когда вы запускаете его в режиме отладки.

И попробуйте QDebug как-нибудь.

http://qt-project.org/doc/qt-4.8/debug.html

РЕДАКТИРОВАТЬ: Чтобы исправить любые проблемы с рекурсивным определением класса заголовка, выполните следующие действия:

В вашем заголовочном файле для одного из классов только прототип класса, не включайте его:

в виджете.h:

// #include "myclass.cpp" // Should be commented out!

class MyClass; // This prototype avoids the recursion

// ...

class Widget
{
    // ...

    public:
        void connectToMyClass(Class * class_ptr);

    // ...
}

Затем в файле cpp вы включаете.

#include "myclass.h"

void Widget::connectToMyClass(Class * class_ptr)
{
    // ...
}

Другой способ обойти эту проблему — использовать QObject * вместо Class *.

Надеюсь, это поможет.

person phyatt    schedule 26.08.2013
comment
Спасибо за быстрый ответ на это. Я попробовал то, что вы предложили с кодом, хотя я не уверен, что правильно его реализую, я не могу добавить их в файл other_class cpp, потому что он неправильно строится с рекурсивными указателями и добавляет его в класс main_window похоже, тоже не работает, хотя я, вероятно, делаю это неправильно. - person user2718679; 26.08.2013
comment
Чтобы решить эту проблему, не включайте другой класс в оба места. - person phyatt; 26.08.2013