突出显示与Qt5

时间:2015-05-29 03:56:27

标签: c++ qt search

首先让我解释一下我想要实现的目标:

在浏览器中,按Ctrl + f并输入" q"。该操作的结果是我想要实现的。看起来这应该是一个已经解决的问题,但尽管我花了很多时间研究,阅读文档,并在Qt IRC中询问,我仍然坚持。也许这里的某个人能够帮助我。

以下Qt课程是我目前正在处理的内容,如果您想要稍微刷新一下: Qt::DisplayRole
QModelIndex::data
QAbstractItemView::setDelegate
QTextEdit::setExtraSelections (only as a reference)
QAbstractDocumentLayout::Selection
QTextDocument::drawContents
QStyledItemDelegate::paint
QStyleOptionViewItem
QStyleOption::rect
QTableView
QAbstractItemModel::match
Learn to Model/View Program with Qt

现在,让我解释一下我是如何设置的,以及我的研究结果和与Qt IRC的互动让我相信我应该做些什么。

我正在使用QStandardItemModel和QTableView。附加到QStandardItemModel的每一行都有几列。众所周知,这些列中的每一列都在QStandardItemModel中表示为QModelIndex。我们可以通过访问其数据(Qt :: DisplayRole)来提取其显示的文本。

方便地,给定一个搜索字符串,QStandardItemModel :: match将返回每个QModelIndex的QModelIndexList,它恰好在列中匹配。当然,如果每行中有多个列,则需要在每列上预先形成匹配,但我稍后会担心性能。

太酷了,这个问题就解决了。我们列出了每个具有匹配字符串的QModelIndex。现在,我想要做的是在每列中突出显示该字符串。例如:

搜索字符串:" col"
这是一个 col umn |这是 Col umn |这是 Col umn |这是一个合作

突出显示的部分是上面粗体显示的部分。为了实现这一点,我通过阅读模型/视图文档知道我需要继承 QStyledItemDelegate 并重新实现其绘制功能。所以我从那里开始。

要解决的下一个问题是,世界上如何选择DisplayRole中的特定文本并仅突出显示它?你不能使用Qt :: BackgroundRole来设置整个索引的背景颜色。输入QTextDocument。

我仍然需要做更多的挖掘才能看到我究竟能够实现这种行为,但是从Qt IRC中我所说的,QTextEdit有一个名为setExtraSelections的函数。看看它是如何实现的,它利用QAbstractTextDocumentLayout :: Selection和QTextDocument中可用的各种游标函数。

然而,遗憾的是我甚至无法解决这个问题,因为第一步是确保我可以使用我重新实现的自定义委托绘制函数将QTextDocument渲染到QTableView。这就是我目前所处的位置。我在这里和它所引用的那篇文章中看过这篇文章: Similar question that doesn't solve my issue

它不完全是我想要的,但我认为它会帮助我至少得到一些渲染。看起来就像在他的代码中一样,他绘制了控件(字面意思是QStyledItemDelegate所做的),然后尝试在其上绘制他的QTextDocument。也许这不是正在做的事情,但它看起来确实如此。

无论如何,当我尝试时,看起来QTextDocument没有效果。如果我注释掉了drawControl调用,那么根本不会呈现任何文本。这是我的代码:

void CustomDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 
{
    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);

    if (index.column() == contentColumn)
    {
        painter->save();

        QTextDocument contentDocument;
        //opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);

        opt.text = "Things";
        painter->setBrush(QBrush(Qt::darkCyan));
        contentDocument.drawContents(painter, opt.rect);

        painter->restore();
    }
    else
    {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

你可以看到我只输入了文字" Things"看看我是否可以让它渲染。无济于事。

在总结我的问题之前,我想提一下,不,我不能使用QTextEdit。我需要自动换行的特定列,而其他我不想自动换行。 QTableView完全按照我期望的方式显示数据。

所以我的问题是: QTextDocument通过子类 QStyledItemDelegate 的paint事件呈现是否是处理此问题的首选方法?如果没有,我还应该怎么处理呢?

如果是这样,我如何让它按预期工作?我的代码出了什么问题?

Bonus 2 parter question 一旦我可以渲染,我怎样才能实际利用QTextDocument API来仅突出显示特定部分,因为包含要突出显示的文本的模型索引列表是在完全不同的函数中发现的,并且在与绘制函数不同的时间发现执行?

更新0 使用QTextDocument API进行多项选择看起来是不可能的壮举。我要绘制内容的列需要自动换行和展开。

据我所知,我手动调用drawContents意味着手动处理调整大小,自动换行和许多其他事情,如果情况确实如此,这不是我想要的路径。

我还有另一种方法,如果有效,我会更新。

更新1 我以另一种方式完成了我想要的东西。不幸的是,我不能将Qt用于我想要的东西。 Qt的模型/视图系统效率太低,并且让它做我需要的工作会导致它变得极其缓慢且无法接受。我是如何完成突出显示的?

我将在答案中描述。如果没有人给我一个更好的答案,我会选择我的最佳答案。

3 个答案:

答案 0 :(得分:0)

我相信你需要做的是在你的QItemDelegate子类中重写这个方法:

void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text) const

QItemDelegate实现此方法的问题是最后,我们看到这个方法调用(它实际上在两个地方调用,但因为它们是相同的调用我只是显示一次):

d->textLayout.draw(painter, layoutRect.topLeft(), QVector<QTextLayout::FormatRange>(), layoutRect);

这是你要改变的第三个论点 - 在QItemDelegate :: drawDisplay()中,它被硬编码为永远是一个空的QVector,这意味着所有的绘制的文本字符串中的字符将始终具有相同的格式。如果你能以某种方式通过QVector&lt; QTextLayout::FormatRange&gt;来调用它。相反,它包含你的每子串格式化首选项,你会得到你想要的效果。

当然,魔鬼在于细节。如果您不介意攻击Qt源代码以符合您的目的,您可以这样做;但这将是一个丑陋的解决方案,因为这意味着当使用非自定义Qt版本编译时,您的程序将无法正常工作(并且每次升级到新的Qt版本时都必须重新修补)。

因此,您可能希望首先将QItemDelegate :: drawDisplay()方法的内容复制到子类的方法,然后根据需要修改复制的版本。 (当然这有其自身的问题,因为QItemDelegate :: drawDisplay()引用了您的子类无法访问的私有成员变量,但您可以解决这些问题。您可能还想查看Qt人员复制一个这样的方法体是否有任何法律问题;我不确定是否有。如果你不习惯复制它,你至少可以看一下关于你可能还需要执行drawDisplay()的各种事情的灵感。

答案 1 :(得分:0)

基本上,我发现让QTableView显示富文本是论坛上人们试图完成的常见用例。由于这是一个已解决的问题,我试图了解如何利用HTML。

首先,我设置自定义委托来处理富文本。然后我的算法有点像这样:

   clear all html tags from the display role in every row of the specified column

for every row in the specified column
    populate a list of QModelIndex with matching text in the Display Role

for every QModelIndex with matching text in the display role
    while there is another occurance of the matching string in the display role
        inject html highlight (span) around the matching word

这当然是极其痛苦,令人无法接受的缓慢。它确实有效,与击中ctrl + f具有完全相同的效果。但是,我不能使用它。令人遗憾的是,Qt并不支持像这样基本的东西。哦,好吧。

答案 2 :(得分:0)

我解决该问题的方法是使用委托中的paint功能从QTextDocument中的光标呈现一个或多个位置。

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if ( !index.isValid() )
        return;


    // Higlight some text
    {
        QString dataHighlight QString("col"); // The text to highlight.    
        QString value = index.model()->data(index, Qt::DisplayRole).toString();

        QTextDocument *doc = new QTextDocument(value);
        QTextCharFormat selection;

        int position = 0;
        QTextCursor cur;

        // We have to iterate through the QTextDocument to find ALL matching places
        do {

            cur = doc->find(dataHighlight,position);            
            cur.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
            cur.selectionStart();
            cur.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
            cur.selectionEnd();
            position = cur.position();
            selection.setBackground(Qt::yellow);
            cur.setCharFormat(selection);

        } while (!cur.isNull());

        painter->save();
        painter->translate(option.rect.x(), option.rect.y());
        doc->drawContents(painter);
        painter->restore();

        delete doc;
    }
}