Qt将信号连接到插槽

Posted

技术标签:

【中文标题】Qt将信号连接到插槽【英文标题】:Qt connect signal to slot 【发布时间】:2013-08-26 16:47:47 【问题描述】:

我有一个包含 QSplitter 小部件(以及其他小部件)的主窗口类。此拆分器的内容由另一个类中的小部件填充。

在另一个 Widgwt 中,我有一个布局,其中添加了一个名为 test_btn 的 QPushButton 我还在 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);

other_class.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;

main.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。如果我还需要包含我的头文件,请告诉我。

【问题讨论】:

现在您明白为什么发布代码非常重要了。当我看到它的那一刻,这个错误就很明显了。 【参考方案1】:

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"

【讨论】:

感谢您的快速回复。我也试过这个,它也没有工作,有没有可能是因为我在主类中有一个Qwidget 和一个layout,它有一个Qsplitter,然后有一个'oc_point.QWidget`这又有一个layouttest_btn 添加test_btn 的父级没有test_function() 插槽? (这可能会让人感到困惑,所以让我知道添加我的实际代码是否更容易) 绝对,肯定需要展示您的代码。制作一个独立的单文件示例。没有.h 文件,没有类似的。只有一个main.cpp 文件和一个.pro 文件。在末尾添加#include "main.moc",这样就不需要单独的头文件了。一旦您确认它可以工作并演示了问题,请重新运行 quake、rebuild 并在此处发布。否则就是浪费大家的时间。 如何在不破坏对象的情况下将other_widget 添加到splitter1?我是否必须返回对象或小部件 ID 或其他内容,或者是否有更好的方法可以在没有 oc_point 的情况下执行此操作,因为显然尝试使用 other_class.other_widget 添加它会给我“预期的主表达式之前”。错误' 所有的QWidgets都是QObjects。只需在堆上而不是在堆栈上创建新对象,并使其为必要时存在的东西所拥有。就这样。您提供的代码中的问题是 oc_point 在堆栈上,而不是在堆上。就是这样。【参考方案2】:

在您的对象初始化之后,您应该将它们相互连接起来。明确命名谁的槽是谁的,谁的信号是谁的,会有很大帮助。

有时我会做一个辅助函数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

编辑: 要修复任何头类递归定义问题,请执行以下操作:

在您的类之一的头文件中仅对该类进行原型化,不要包含它:

在 widget.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 *

希望对您有所帮助。

【讨论】:

感谢您对此的快速回复。我尝试了您对代码的建议,虽然我不确定我是否正确实现它,但我无法将这些添加到 other_class cpp 文件中,因为它无法使用递归指针正确构建并将其添加到main_window 类似乎也不起作用,尽管我可能做得不对。 要解决这个问题,不要在两个地方都包含其他类。

以上是关于Qt将信号连接到插槽的主要内容,如果未能解决你的问题,请参考以下文章

Qt将信号连接到插槽

如何将信号连接到插槽

Qt for Qt:如何连接到具有新信号/插槽风格的信号?

如何在 Qt 中将插槽连接到信号 QProcess::started()?

QT将不同类的信号和插槽连接到主窗口类?

将 QML 信号连接到 PySide2 插槽