当QAction位于领先位置时如何获取QLineEdit文本偏移

时间:2019-01-16 09:15:21

标签: c++ qt qlineedit

我有一个QLineEdit,一个QAction处于领先位置。我想知道文字的开始位置,但找不到方法:

QLineEdit *le = new QLineEdit(parent);
le->addAction(QIcon(":/myicon"), QLineEdit::LeadingPosition);
// Now I want to get the text start position
// but both return "QMargins(0, 0, 0, 0) QMargins(0, 0, 0, 0)"
qDebug() << le->textMargins() << le->contentsMargins();

我搜索了qt的github源码,以发现addAction()方法是否在内容或文本空白处执行了某些操作,但没有成功。

1 个答案:

答案 0 :(得分:2)

我必须承认(在阅读操作要求之前)我不知道QLineEdit::addAction()。因此,我写了一个小样本testQLineEditAction.cc

#include <QtWidgets>

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // init GUI
  QLineEdit qEdit;
  qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
  qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
  qEdit.show();
  // runtime loop
  return app.exec();
}

这是它的外观(在cygwin64中编译):

Snapshot of testQLineEditAction

然后,我在woboq.org中进行了深入研究,以了解其实现方式。

我是从QLineEdit::paintEvent()开始的:

void QLineEdit::paintEvent(QPaintEvent *)
{

...

  QStyleOptionFrame panel;
  initStyleOption(&panel);

...

  QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
  r.setX(r.x() + d->effectiveLeftTextMargin());
  r.setY(r.y() + d->topTextMargin);
  r.setRight(r.right() - d->effectiveRightTextMargin());
  r.setBottom(r.bottom() - d->bottomTextMargin);

这很有趣:检索内容的矩形,然后通过内部偏移进行校正。

  QFontMetrics fm = fontMetrics();

...

  QRect lineRect(r.x() + d->horizontalMargin, d->vscroll, r.width() - 2*d->horizontalMargin, fm.height());

关于d->horizontalMargin,我不太确定,但是我暂时忽略了它,而是关注d->effectiveLeftTextMargin()

int QLineEditPrivate::effectiveLeftTextMargin() const
{
    return effectiveTextMargin(leftTextMargin, leftSideWidgetList(), sideWidgetParameters());
}

...

static int effectiveTextMargin(int defaultMargin, const QLineEditPrivate::SideWidgetEntryList &widgets,
                               const QLineEditPrivate::SideWidgetParameters &parameters)
{
    if (widgets.empty())
        return defaultMargin;
    return defaultMargin + (parameters.margin + parameters.widgetWidth) *
           int(std::count_if(widgets.begin(), widgets.end(),
                             [](const QLineEditPrivate::SideWidgetEntry &e) {
                                 return e.widget->isVisibleTo(e.widget->parentWidget()); }));
}

因此,我得出的结论是,QLineEditPrivate::effectiveLeftTextMargin()在确定文本矩形的有效大小时会考虑操作图标的空间。

很遗憾,所有这些功能都是private,因此无法从外部访问。考虑了一段时间后,如何从外部访问这些文件并查看文档。不管我是否没有监督什么,我都有想法直接使用QAction来实现这一点:

#include <QtWidgets>

void inspect(const QString &cmd, QAction &qCmd)
{
  qDebug() << (cmd + "->associatedWidgets().size():")
    << qCmd.associatedWidgets().size();
  int i = 0;
  for (QWidget *const pQWidget : qCmd.associatedWidgets()) {
    qDebug() << '[' << i++ << "]:"
      << typeid(*pQWidget).name()
      << "geometry:" << pQWidget->geometry();
  }
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // init GUI
  QLineEdit qEdit;
  qEdit.setText("012345678901234567890123456789");
  QAction *const pQCmd1
    = qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
  QAction *const pQCmd2
    = qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
  qEdit.show();
  qDebug() << "qEdit.geometry():" << qEdit.geometry();
  inspect("pQCmd1", *pQCmd1);
  inspect("pQCmd2", *pQCmd2);
  // runtime loop
  return app.exec();
}

控制台输出:

Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
"pQCmd1->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(4,2 22x18)
"pQCmd2->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(174,2 22x18)

要比较这些值,请放大另一个快照,该快照具有修改后的图标(在SVG中绘制以显示图标大小的框)(系数5):

Magnified Snapshot of testQLineEditAction (factor 5)

QLineEditIconButton报告了位置(4,2),但是图标的左框距离QLineEdit的左边界8个像素。在QLineEditIconButton周围肯定有一个框架也必须考虑(而且我没有研究如何检索它)。框架的宽度可能是样式引擎的主题,因此在平台之间会有所不同。为了使这种尝试变得健壮和可移植,应从小部件或样式中检索相应的值。这变得越来越繁琐,或多或少都有成功的机会。

在尝试回答SO: How to automatically increase/decrease text size in label in Qt时,我遇到了类似情况。


关于QLineEdit::cursorRect()

我认为,使用QLineEdit::cursorRect()也是脆弱的。

我修改了上面的示例以进行检查:

#include <QtWidgets>

class LineEdit: public QLineEdit {

  public:
    QRect cursorRect() const { return QLineEdit::cursorRect(); }
};

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // init GUI
  LineEdit qEdit;
  qEdit.setText("012345678901234567890123456789");
  qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
  qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
  qEdit.show();
  qDebug() << "qEdit.cursorRect():" << qEdit.cursorRect();
  // runtime loop
  return app.exec();
}

控制台输出:

Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
qEdit.cursorRect(): QRect(253,0 9x16)

有趣的是,光标的x位置不仅很高,甚至比qEdit的宽度还高。怎么会?我在"012345678901234567890123456789"中输入的初始文本qEdit使光标靠近右侧,从而发生水平滚动。光标位置似乎与虚拟文本的宽度有关(包括左侧的剪切范围)。