QLineEdit: Set cursor location to beginning on focus

I have a QLineEdit with an input mask, so that some kind of code can easily be entered (or pasted). Since you can place the cursor anywhere in the QLineEdit even if there is no text (because there is a placeholder from the input mask):

If people are careless and unattentive enough this leads to them typing in the middle of the text box whereas they should start typing at the beginning. I tried the trivial way of ensuring that the cursor is at the start upon focus by installing an event filter:

bool MyWindowPrivate::eventFilter(QObject * object, QEvent * event)
{
    if (object == ui.tbFoo && event->type() == QEvent::FocusIn) {
        ui.tbFoo->setCursorPosition(0);
    }
    return false;
}

This works fine with keybaord focus, ie when pressing ⇆ or ⇧+⇆, but when clicking with the mouse the cursor always ends up where I clicked. My guess would be that QLineEdit sets the cursor position upon click itself after it got focus, thereby undoing my position change.

Digging a little deeper, the following events are raised when clicking¹ and thus changing focus, in that order:

  • FocusIn
  • MouseButtonPress
  • MouseButtonRelease
  • I can't exactly catch mouse clicks in the event filter, so is there a good method of setting the cursor position to start only when the control is being focused (whether by mouse or keyboard)?


    ¹ Side note: I hate that Qt has no documentation whatsoever about signal/event orders for common scenarios such as this.


    Below is an implementation that's factored into a separate class. It defers the setting of the cursor to after any pending events are posted for the object, thus sidestepping the issue of event order.

    #include <QApplication>
    #include <QLineEdit>
    #include <QFormLayout>
    #include <QMetaObject>
    
    // Note: A helpful implementation of
    // QDebug operator<<(QDebug str, const QEvent * ev)
    // is given in http://stackoverflow.com/q/22535469/1329652
    
    /// Returns a cursor to zero position on a QLineEdit on focus-in.
    class ReturnOnFocus : public QObject {
       Q_OBJECT
       /// Catches FocusIn events on the target line edit, and appends a call
       /// to resetCursor at the end of the event queue.
       bool eventFilter(QObject * obj, QEvent * ev) {
          QLineEdit * w = qobject_cast<QLineEdit*>(obj);
          // w is nullptr if the object isn't a QLineEdit
          if (w && ev->type() == QEvent::FocusIn) {
             QMetaObject::invokeMethod(this, "resetCursor",
                                       Qt::QueuedConnection, Q_ARG(QWidget*, w));
          }
          // A base QObject is free to be an event filter itself
          return QObject::eventFilter(obj, ev);
       }
       // Q_INVOKABLE is invokable, but is not a slot
       /// Resets the cursor position of a given widget.
       /// The widget must be a line edit.
       Q_INVOKABLE void resetCursor(QWidget * w) {
          static_cast<QLineEdit*>(w)->setCursorPosition(0);
       }
    public:
       ReturnOnFocus(QObject * parent = 0) : QObject(parent) {}
       /// Installs the reset functionality on a given line edit
       void installOn(QLineEdit * ed) { ed->installEventFilter(this); }
    };
    
    class Ui : public QWidget {
       QFormLayout m_layout;
       QLineEdit m_maskedLine, m_line;
       ReturnOnFocus m_return;
    public:
       Ui() : m_layout(this) {
          m_layout.addRow(&m_maskedLine);
          m_layout.addRow(&m_line);
          m_maskedLine.setInputMask("NNNN-NNNN-NNNN-NNNN");
          m_return.installOn(&m_maskedLine);
       }
    };
    
    int main(int argc, char *argv[])
    {
       QApplication a(argc, argv);
       Ui ui;
       ui.show();
       return a.exec();
    }
    
    #include "main.moc"
    

    You could use a QTimer in focusInEvent to call a slot that sets the cursor position to 0.

    This works well because a single shot timer posts a timer event at the end of the object's event queue. A mouse-originating focus-in event necessarily has the mouse clicks already posted to the event queue. Thus you're guaranteed that the timer's event (and the resulting slot call) will be invoked after any lingering mouse press events.

    void LineEdit::focusInEvent(QFocusEvent *e)
    {
        QLineEdit::focusInEvent(e);
        QTimer::singleShot(0, this, SLOT(resetCursorPos()));
    }
    
    void LineEdit::resetCursorPos()
    {
        setCursorPosition(0);
    }
    

    set a validator instead of inputmask . http://doc.qt.io/qt-5/qregularexpressionvalidator.html

    链接地址: http://www.djcxy.com/p/39312.html

    上一篇: 如何从QWebView中的输入字段获取值

    下一篇: QLineEdit:将光标位置设置为开始对焦