C++ 突出显示与Qt5中的搜索字符串匹配的文本

C++ 突出显示与Qt5中的搜索字符串匹配的文本,c++,qt,search,C++,Qt,Search,首先,让我解释一下我想要实现的目标: 在浏览器中,按Ctrl+f并键入q。这次行动的结果正是我想要达到的。这似乎是一个应该解决的问题,但尽管我花了很多时间研究、阅读文档,并在Qt IRC中四处询问,我仍然被卡住了。也许这里有人能帮我一把 以下是我目前正在处理的Qt类,以防您想稍微复习一下: 现在,让我解释一下我是如何设置的,以及我的研究结果和与Qt-IRC的互动让我相信我应该做些什么 我正在使用QStandardItemModel和QTableView。附加到QStandardItemMode

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

在浏览器中,按Ctrl+f并键入q。这次行动的结果正是我想要达到的。这似乎是一个应该解决的问题,但尽管我花了很多时间研究、阅读文档,并在Qt IRC中四处询问,我仍然被卡住了。也许这里有人能帮我一把

以下是我目前正在处理的Qt类,以防您想稍微复习一下:

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

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

方便的是,给定一个搜索字符串,QStandardItemModel::match将返回列中匹配的每个QModelIndex的QModelIndexList。当然,如果每行中有几列,则需要对每列执行此匹配,但稍后我会担心性能

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

搜索字符串:col 这是一列|这是一列|这是一列|这是一个co

突出显示的部分是我上面用粗体显示的部分。为了实现这一点,我从阅读模型/视图文档中了解到,我需要对QStyledItemDelegate进行子类化,并重新实现其绘制功能。所以我从那里开始

下一个要解决的问题是,究竟如何在DisplayRole中选择一段特定的文本并只突出显示它?不能使用Qt::BackgroundRole,这将设置整个索引的背景色。输入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;
    }
}
我仍然需要做更多的挖掘来了解如何准确地实现该行为,但是从Qt IRC中告诉我的,QTextEdit有一个名为setExtraSelections的函数。看看它是如何实现的,它利用了QAbstractTextDocumentLayout::Selection和我在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;
    }
}
然而,遗憾的是,我甚至无法开始解决这个问题,因为第一步是实际确保我可以使用重新实现的自定义委托绘制函数将QTextDocument呈现给QTableView。这就是我目前被困的地方。我看过这篇文章,这里还有它引用的那篇:

这并不完全是我想要的,但我想这至少能帮我得到一些东西。看起来好像在他的代码中,他正在绘制控件,这实际上就是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);
    }
}
你可以看到,我只是输入了一些文本内容,看看我是否能把它们呈现出来。无济于事

在总结我的问题之前,我想说的是,不,我不能使用QTextEdit。有一些特定的列我需要进行换行,而其他列我不想进行换行。QTableView显示的数据与我预期的完全一致

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

如果是这样的话,我如何让它像我期望的那样工作?我的代码有什么问题

奖金二方问题 一旦我可以渲染,如果包含要高亮显示的文本的模型索引列表是在一个完全不同的函数中发现的,并且在与paint函数执行时不同的时间发现的,那么我如何真正利用QTextDocument API来只高亮显示特定的片段呢

更新0 使用qtextdocumentapi进行多重选择看起来是不可能的壮举。我将要绘制内容的专栏需要换行和展开

据我所知,手动调用drawContents意味着手动处理大小调整、单词包装和许多其他事情,如果确实是这样的话,我不想走这条路

我有另一个想法,如果可行,我会更新

更新1 我已经用另一种方式实现了我想要的。不幸的是,我不能使用Qt来满足我的需求。Qt的模型/视图系统效率太低,让它做我需要的事情会导致它极其缓慢,令人无法接受。我是如何完成突出显示的

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

我相信您需要在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调用它,您将获得想要的效果

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


因此,您可能希望首先将QItemDelegate::drawDisplay方法的内容复制到子类的方法中,然后根据需要修改复制的版本。当然,这也有其自身的问题,因为QItemDelegate::drawDisplay引用了子类无权访问的私有成员变量,但您可能可以解决这些问题。您可能还想和Qt人员核实一下,看看复制这样的方法体是否存在任何法律问题;我不确定是否有。如果您对复制它感到不舒服,您至少可以看看它,了解您的drawDisplay实现可能还需要做哪些事情。本质上,我发现让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不支持像这样基本的东西。哦,好吧。

我解决这个问题的方法是使用委托的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;
    }
}

我明白了,非常感谢您抽出时间回复!对不起,我应该更清楚我的CustomDelegate是的一个子类。文档提到,当为自定义委托生成子类时,应该使用QStyledItemDelegate,这就是我要生成的子类。QStyledItemDelegate及其基类QAbstractitemDelegate没有函数drawDisplay。虽然,我正在搜索这两个方面的相同缺陷,但我没有找到与您在其中描述的相同的缺陷。似乎没有调用layout->draw。嗯,看起来QStyledItemDelegate::paint调用drawControlStyle::CE_ItemViewItem来绘制文本以及其他内容。QCommonStyle::drawControlCE_ItemViewItem然后调用viewItemDrawText,然后为单元格中的每行文本调用p->drawText。假设我遵循正确的代码路径,我认为没有一种简单的方法可以让drawText调用以不同的颜色绘制不同的字母。对于QStyledItemDelegate,您可能只需要重写QStyledItemDelegate::paint,直接调用QTextLayout::draw。尽管这样做,我还是会渲染原始文本,然后将高亮显示的文本覆盖在上面。除了效率低下之外,这似乎是一种黑客行为。确实,paint调用了QStyle::drawControl,但由于我正在重新实现该函数,所以根本不需要使用QStyle::drawControl。事实上,我假设这样做不会得到期望的结果,因为QTableView默认使用QStyledItemDelegate。也许QStyle中的其他东西可以被利用?但这就是我尝试QTextDocument::drawContent的原因。此外,我应该指出,目标不是用不同的颜色绘制不同的字母。如果您在浏览器中点击Crtl+f并搜索内容,您将看到文本高亮显示,而不是字体颜色改变。这相当于Qt::BackgroundRole颜色。似乎没有任何方法可以部分设置backgroundRole,但是,使用QTextDocument API,我可以通过编程方式“选择”文本并更改突出显示的颜色。因此,我可以通过选择来产生高亮显示的错觉,就像您将鼠标光标拖动到编辑框中的文本上一样。