0

I'm programming a little calculator in C++/Qt, and I'd like to avoid writing as much slots as there are QPushButton. My teacher suggested me to subclass QPushButton and create a new signal. So far I've written that :

buttoncalc.h :

class ButtonCalc : public QPushButton{

public:
    ButtonCalc(QString string);

signals:
    void sendValue(char c); 
};

But I don't know what to do with that. I should first change the char emmited depending on the string I pass in the constructor but I don't know where I should do it in my code. Should I also redefine the clicked() signal of QPushButton ?

I'm a little lost here so I thank you for your answers.

EDIT (some precisions) : The ButtonClass is used as replacement of QPushButton in the class that draws the calculator. I need a way to use a single slot in that class. This slot should therefore have a way to know which button has been clicked.

Ayzelberg
  • 1
  • 2
  • What exactly should your `ButtonCalc` class do? – Mike Mar 25 '17 at 08:45
  • Making signals send arguments to slots is done with `QObject::connect`, not with inheritance. When should the signal be emitted? – nwp Mar 25 '17 at 08:54
  • The ButtonCalc class is used as replacement of QPushButton in the class that draws all the buttons (numbers, +, -, ...). – Ayzelberg Mar 25 '17 at 09:47
  • I'd like to connect all the ButtonCalc to a unique slot, but it means I have to transfer the information of which button has been clicked and I don't know how to do this. – Ayzelberg Mar 25 '17 at 09:48
  • You say "This slot should therefore have a way to know which button has been clicked". It does -- it can use [`QObject::sender`](http://doc.qt.io/qt-5/qobject.html#sender) to find out which object sent a signal. – G.M. Mar 25 '17 at 10:29
  • 2
    Possible duplicate of [Get index of QPushButton on 2D array QPushButton](http://stackoverflow.com/questions/22641306/get-index-of-qpushbutton-on-2d-array-qpushbutton) – Mike Mar 25 '17 at 23:40

2 Answers2

1

Actually, I intended to make a small sample which should show how to overload QPushButton. But then I realized it is even simpler without overloading but using lambdas for the signal handler (which are supported since 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();
}

Notes:

  1. The specific info for the individual buttons is actually bound to the signal handler itself (instead making it a member of the derived class).

  2. I accessed the lbls array twice - once for the QPushButton label, again for the respective signal handler. Instead, I could have used the QPushButton label itself for the latter. (It's a matter of taste.)

  3. The lambda for the signal handler "binds" all specific info for the individual buttons in its environment:

    • a reference to the qTxt
    • a reference to the lbls array
    • the values of i and j for array access.
  4. The signature of the lambda has to match the one of the QPushButton::clicked signal hence no parameters. The return type is actually missing but in this case, the C++ compile infers it from the return expression. Actually, their is no return statement. So, return type void is inferred matching the QPushButton::clicked signal signature as well.

  5. IMHO, lambdas and signal handlers are fitting together like (hopefully) your left and right shoe. Signal/slots (I used sigc++ in the past) provided the concept of adapters needed for binding and hiding arguments or return values. It worked but everytimes I had to use it I struggled in finding the correct nesting - it was a mess. All these problems are away since lambdas can provide any adapter.

  6. The only difficulty with lambdas I see (beside of the syntax): you have to consider lifetimes of referenced (or pointed) variables in the environment (the thing in the brackets []) carefully. (A lambda is not a closure - it may not extend the lifetime of its environment according to its own.)

Compiled and tested with VS2013, Qt5.6 on Windows 10 (64 bit):

Snapshot of testQCustomButton.exe

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • 1
    Good example, You may want to look at [this answer](http://stackoverflow.com/a/22642431/2666212). It sums up various approaches to this problem. – Mike Mar 25 '17 at 23:39
1

Use QSignalMapper which is designed for exactly this use case. The documentation includes the example so I will not repeat it here:

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

Resurrection
  • 3,916
  • 2
  • 34
  • 56