4

title pretty much says it all. I have a read-only text box on a form where users can edit the contents of this text box through buttons on the form. The form is basically a keypad. As users click the buttons, a digit will be added to the value in the text box.

Technically, the final application will be running on a machine with no keyboard but a touchscreen. Users interact with the application using the touchscreen and they should not be installing keyboards on the machine but in the event they do, I am making the text box read-only.

Now, how can I have the text box's cursor still blink even though it is read only?

I am wondering if I need to do something similar to this user's solution:

Hide QLineEdit blinking cursor

I have also tried using the setFocus method and I am looking into style sheets. However, nothing has panned out.

philm
  • 797
  • 1
  • 8
  • 29

3 Answers3

1

Romha Korev's answer will appear to work, but it won't catch everything. It can still be possible to paste or drag&drop text into the line edit, or as a result of a locale dependent input-method keyboard event. I don't know all the various ways text can end up being entered into the line edit that way. You'd be hunting for holes to plug.

So I propose to abuse a QValidator for this. Do not set your line edit to read-only mode. Create your own validator that blocks all input unless you specifically disable it:

class InputBlockerValidator final: public QValidator
{
    Q_OBJECT

public:
    void enable()
    { is_active_ = true; }

    void disable()
    { is_active_ = false; }

    QValidator::State validate(QString& /*input*/, int& /*pos*/) const override
    {
        if (is_active_) {
            return QValidator::Invalid;
        }
        return QValidator::Acceptable;
    }

private:
    bool is_active_ = true;
};

Now set an instance of this as the validator of your line edit:

// ...
private:
    QLineEdit lineedit_;
    InputBlockerValidator validator_;
// ...

lineedit_.setValidator(&validator_);

Then, whenever you insert text into the line edit, disable and re-enable the validator:

validator_.disable();
lineedit_.insert(text_to_be_inserted);
validator_.enable();

Do not ever call setText() on the line edit. For some reason, this permanently prevents the validator from blocking input. I don't know if this is intended or a Qt bug. Only use insert(). To simulate setText(), use clear() followed by insert().

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
  • `setText()` does not disable the validator. However, the string passed to `setText()` is not checked against the validator. This means that you can end up in a situation where the text of the `QLineEdit` is invalid for the validator, in this situation the validator will be effectively temporarily disabled until the text is not invalid anymore. – Benjamin T Jun 06 '19 at 09:11
  • @BenjaminT That doesn't make sense to me. The text is always invalid for the validator, unless `is_active_ == false`. In fact, I can create a validator that always returns `QValidator::Invalid`. `setText()` will make it stop working. – Nikos C. Jun 06 '19 at 15:02
  • That's because you have to consider both the `QValidator` and the `QLineEdit`. Initially the `QLineEdit` considers its current text valid (regardless of the validator), when the user tries to input text the validator is checked and returns `Invalid`. The QLineEdit detects that the text will go from valid to invalid and prevent the change. However, once `setText()` has been called, even if the QLineEdit accepts the new text, it will detects it is invalid. Then when the user input texts, the QLineEdit detects the text will go from invalid to invalid and will accept the change. – Benjamin T Jun 06 '19 at 17:18
1

Create a class whiwh inherits from QLineEdit and ignore the key events (events triggered when the user press a key). It will make your line edit read only but without the look-and-feel:

class LineEdit: public QLineEdit
{
    Q_OBJECT
public:
    LineEdit(QWidget* parent=nullptr): QLineEdit(parent)
    {
    }
    virtual void keyPressEvent(QKeyEvent* event)
    {
        event->ignore();
    }

public slots:
    void add(QString const& textToAdd)
    {
        setText(text() + textToAdd);
    }
};

An usage example (the timer simulates the virtual keyboard):

LineEdit* line = new LineEdit;
line->show();

QTimer timer;
timer.setInterval(2000);
QObject::connect(&timer, &QTimer::timeout, [=]() { line->add("a"); });
timer.start();
Dimitry Ernot
  • 6,256
  • 2
  • 25
  • 37
  • Maybe it is a little bit late, but if we click on a QPushButton the blinking stops. –  Jul 01 '21 at 11:30
  • `button->setFocusPolicy(Qt::NoFocus)` this will disable remove focus from the LineEdit –  Jul 01 '21 at 11:40
1

Other answers have given you technical solutions to your question. However, I think that you are going in a wrong direction. You want a QLineEdit that is read only, but with a cursor and still accepts input from a virtual keyboard... yeah, so it is not really read only... It is not smelling good. And in general, arbitrarily and actively disabling standard functions is not a good idea. Especially, if it means hacking your way around standard widget behaviors an semantics to do it.

Let's think from the start. What is the issue of accepting input from a keyboard? From your question I would dare to guess that you want to make sure that the QLineEdit only accepts digits, and forbid the user to input other characters.

If I am right what you want is a QValidator, either a QIntvalidator or a QRegExpValidator. Then you can let the users use a keyboard, but they will only be able to input digits, like they would with your virtual keyboard.

Benjamin T
  • 8,120
  • 20
  • 37
  • After further consideration, the project requirements required me too set an upper limit for the value of the text box. I went with the QIntValidator. After some additional consideration. I kept the keyboard function in tact becuase this will limit the input values to be on digits and can be used as a redundancy as a secondary input in the event the primary interface device fails – philm Jul 22 '19 at 15:47