QLineEdit:将光标位置设置为焦点开始

时间:2014-03-20 12:10:25

标签: c++ qt focus qlineedit

我有一个带有输入掩码的QLineEdit,因此可以轻松输入(或粘贴)某种代码。因为即使没有文本(因为输入掩码中有占位符),您可以将光标放在QLineEdit中的任何位置:

enter image description here

如果人们不小心且不专心,这会导致他们在文本框的中间打字,而他们应该在开头打字。我尝试了通过安装事件过滤器确保光标在焦点开始时的简单方法:

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

这适用于keybaord焦点,即当按 + 时,但是当用鼠标点击时光标总是结束我点击的地方。我的猜测是QLineEdit在获得焦点后点击后设置光标位置,从而撤消我的位置变化。

深入挖掘,点击¹,然后按顺序更改焦点,会引发以下事件:

  1. FocusIn
  2. MouseButtonPress
  3. MouseButtonRelease
  4. 我无法在事件过滤器中完全捕捉到鼠标点击,因此有一种很好的方法可以设置光标位置,以便在控制聚焦时(无论是通过鼠标还是键盘)启动


    ¹旁注:我讨厌Qt没有任何关于此类常见场景的信号/事件订单的文档。

2 个答案:

答案 0 :(得分:7)

以下是在单独的类中考虑的实现。在为对象发布任何待处理事件后,它将光标的设置推迟到,从而回避了事件顺序的问题。

#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"

答案 1 :(得分:3)

您可以使用QTimer中的focusInEvent来调用将光标位置设置为0的插槽。

这很有效,因为单发计时器会在对象的事件队列末尾发布计时器事件。引发鼠标的焦点事件必然会将鼠标点击发布到事件队列中。因此,您可以确保在任何拖延鼠标按下事件后调用计时器的事件(以及产生的插槽调用)。

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

void LineEdit::resetCursorPos()
{
    setCursorPosition(0);
}