【问题标题】:How to subclass QPushButton so that the signal sends an argument to the slot?如何继承 QPushButton 以便信号向插槽发送参数?
【发布时间】:2017-03-25 08:41:53
【问题描述】:

我正在用 C++/Qt 编写一个小计算器,我想避免编写与 QPushButton 一样多的插槽。 我的老师建议我继承 QPushButton 并创建一个新信号。 到目前为止,我已经写了:

buttoncalc.h:

class ButtonCalc : public QPushButton{

public:
    ButtonCalc(QString string);

signals:
    void sendValue(char c); 
};

但我不知道该怎么办。我应该首先根据我在构造函数中传递的字符串更改 char emmited,但我不知道我应该在我的代码中的哪里做。我还应该重新定义 QPushButton 的 clicked() 信号吗?

我有点迷路了,所以谢谢你的回答。

编辑(一些精度): ButtonClass 在绘制计算器的类中用作 QPushButton 的替代品。 我需要一种在该类中使用单个插槽的方法。因此,这个槽应该有办法知道哪个按钮被点击了。

【问题讨论】:

  • 您的ButtonCalc 班级究竟应该做什么?
  • 使信号向槽发送参数是通过QObject::connect 完成的,而不是继承。什么时候应该发出信号?
  • ButtonCalc 类用作绘制所有按钮(数字、+、-、...)的类中 QPushButton 的替代品。
  • 我想把所有的 ButtonCalc 连接到一个唯一的插槽,但这意味着我必须传输点击了哪个按钮的信息,我不知道该怎么做。跨度>

标签: c++ qt


【解决方案1】:

使用专为此用例设计的QSignalMapper。文档中包含示例,此处不再赘述:

http://doc.qt.io/qt-5/qsignalmapper.html

【讨论】:

    【解决方案2】:

    实际上,我打算制作一个小样本来展示如何重载QPushButton。但后来我意识到它更简单,无需重载而是使用 lambdas 作为信号处理程序(自 Qt5 起支持):

    #include <QtWidgets>
    
    int main(int argc, char **argv)
    {
      qDebug() << QT_VERSION_STR;
      // main application
      QApplication app(argc, argv);
      // setup GUI
      QMainWindow qWin;
      QGroupBox qBox;
      QGridLayout qGrid;
      QLineEdit qTxt;
      qGrid.addWidget(&qTxt, 0, 0, 1, 4);
      enum { nCols = 4, nRows = 4 };
      QPushButton *pQBtns[nRows][nCols];
      const char *lbls[nRows][nCols] = {
        { "7", "8", "9", "-" },
        { "4", "5", "6", "+" },
        { "1", "2", "3", "*" },
        { "0", ".", "=", "/" },
      };
      for (int i = 0; i < nRows; ++i) {
        for (int j = 0; j < nCols; ++j) {
          pQBtns[i][j]
            = new QPushButton(QString::fromLatin1(lbls[i][j]), &qWin);
          qGrid.addWidget(pQBtns[i][j], i + 1, j);
        }
      }
      qBox.setLayout(&qGrid);
      qWin.setCentralWidget(&qBox);
      qWin.show();
      // install signal handlers
      for (int i = 0; i < nRows; ++i) {
        for (int j = 0; j < nCols; ++j) {
          QObject::connect(pQBtns[i][j], &QPushButton::clicked,
            // easy done with a lambda:
            [&qTxt, &lbls, i, j]() {
            qTxt.setText(qTxt.text() + QString::fromLatin1(lbls[i][j]));
          });
        }
      }
      // run application
      return app.exec();
    }
    

    注意事项:

    1. 各个按钮的特定信息实际上绑定到信号处理程序本身(而不是使其成为派生类的成员)。

    2. 我访问了 lbls 数组两次 - 一次用于 QPushButton 标签,另一次用于相应的信号处理程序。相反,我可以为后者使用QPushButton 标签本身。 (这是一个品味问题。)

    3. 信号处理程序的 lambda “绑定”其环境中各个按钮的所有特定信息:

      • qTxt 的引用
      • lbls 数组的引用
      • ij 的值用于数组访问。
    4. lambda 的签名必须与QPushButton::clicked 信号之一匹配,因此没有参数。返回类型实际上是缺失的,但在这种情况下,C++ 编译从返回表达式推断它。实际上,他们不是return 声明。因此,返回类型 void 也与 QPushButton::clicked 信号签名相匹配。

    5. 恕我直言,lambdas 和信号处理程序就像(希望)你的左右鞋一样适合。 Signal/slots(我过去使用过 sigc++)提供了绑定和隐藏参数或返回值所需的适配器的概念。它起作用了,但每次我不得不使用它时,我都在努力寻找正确的嵌套 - 它一团糟。所有这些问题都迎刃而解,因为 lambdas 可以提供任何适配器。

    6. 我看到的 lambda 的唯一困难(除了语法):您必须仔细考虑环境中引用(或指向)变量的生命周期(括号中的东西 [])。 (lambda 不是闭包——它可能不会根据自己的情况延长其环境的生命周期。)

    在Windows 10(64位)上使用VS2013、Qt5.6编译测试:

    【讨论】:

    • 很好的例子,你可能想看看this answer。它总结了解决这个问题的各种方法。
    猜你喜欢
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 2016-04-14
    • 1970-01-01
    • 2022-08-03
    • 2012-08-07
    • 2010-10-30
    • 1970-01-01
    相关资源
    最近更新 更多