Dave M. Dave M. - 1 month ago 8
C++ Question

What is a good way to grab QKeyEvents in a widget?

Using Qt-5.7.0, I would like to have a QSpinBox to set a timeout value. The value can be in seconds or minutes. I'll put a QLabel after the SpinBox to say which it is.

Now, when the user is operating the control, I would like it to change from seconds to minutes depending on whether the Shift key is pressed. So, for example, when the cursor is over the spinbox but no shift, the label will say

Minutes
and the the spinbox number can be incremented or decremented.

However, when the cursor is over the spinbox and shift is pressed, the label will switch to
Seconds
, the spinbox value will change to
previous_value * 60
, and the value can be altered.

Using QtCreator, I placed the QSpinBox and QLabel in a QFrame, so everything's together in the right place, but I don't see any Slots where I can grab the Shift state. I can check
shift
in the SpinBox
on_SB_valueChanged()
function, but I'd really like the change to happen when the shift button is pressed, not just when the value is changed (and I discover that the shift button was pressed).

Is there a standard way of managing something like this? (I guess I could make a custom widget that does it, but perhaps there's an easier way?) How can such custom widgets be placed in QtCreator?

Answer

You could use an event filter and attach it to a stock widget to intercept the event rather than extending the widget.

Here is some code that should more or less work, although I'd personally prefer to have a small toggle button to switch between minutes and seconds, that would be far more intuitive and less error prone:

class Filter : public QObject {
    Q_OBJECT
    QLabel * l;
  public:
    Filter(QLabel * ll) : l(ll) {}
    bool eventFilter(QObject *, QEvent *e) {
      switch (e->type()) {
        case QEvent::Wheel:
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
          if (QApplication::keyboardModifiers() & Qt::ShiftModifier) l->setText("shift");
          else l->setText("noshift");
      }
      return false;
    }
};

class Widget : public QWidget {
    Q_OBJECT
  public:
    Widget(QWidget *parent = 0) : QWidget(parent) {
      auto ll = new QVBoxLayout(this);
      setLayout(ll);
      ll->addWidget(l = new QLabel(this));
      ll->addWidget(s = new QSpinBox(this));
      s->installEventFilter(new Filter(l));
    }
  private:
  QLabel * l;
  QSpinBox * s;
};