Есть ли лучший способ для переноса текста в QToolTip, чем просто использование RegExp? - PullRequest
11 голосов
/ 25 января 2011

Так что вопрос в названии.QToolTip, похоже, не предоставляет функцию wordwraop.Однако возможно заменить N-е пространство на \ n с помощью регулярных выражений, но мне было интересно, есть ли у кого-нибудь предложение для лучшего решения.В частности, моя проблема с моим подходом заключается в том, что он не учитывает длину текста.Например, я хотел бы, чтобы более длинные тексты формировали более широкие абзацы.

Ответы [ 6 ]

20 голосов
/ 25 января 2011

Если текст во всплывающей подсказке представляет собой форматированный текст, он автоматически переносится по словам.

Вот тривиальный пример, в котором установка черного шрифта делает его «расширенным текстом», и поэтому он переносится по словам.Отсутствие объявлений шрифта означает, что всплывающая подсказка будет обычным текстом и будет расширять всю длину экрана.

QString toolTip = QString("<FONT COLOR=black>");
toolTip += ("I am the very model of a modern major general, I've information vegetable animal and mineral, I know the kinges of England and I quote the fights historical from Marathon to Waterloo in order categorical...");
toolTip += QString("</FONT>");
widget->setToolTip(sToolTip);

Конечно, в этом примере ширина подсказки соответствует платформе.

В трекере ошибок Qt есть новое предложение об этой проблеме: https://bugreports.qt.io/browse/QTBUG-41051. Также запрашивается возможность изменения ширины.

7 голосов
/ 25 ноября 2015

Альтернативный ответ: используйте <nobr> в начале всплывающей подсказки, чтобы указать часть текста, которая будет определять ширину. Например:

<nobr>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed</nobr>
do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.

Текст между <nobr> и </nobr> приведет к минимальной ширине. Остальные обернутся соответственно. (Это полезно, если вы не удовлетворены шириной по умолчанию, используемой Qt, которая по моему опыту имеет тенденцию быть слишком узкой.)

5 голосов
/ 14 сентября 2017

Превалирующих ответов на этот вопрос технически достаточно: краткосрочные технические исправления (также известные как: , когда вам просто нужны подсказки, чтобы работать как рекламируется ), нодалеко не универсальное решение.

Что не так с " ... ", в любом случае?

Если вы не возражаете против ручного встраивания весь текст всплывающей подсказки во всем приложении в тегах HTML с возможными вредными побочными эффектами (например, <font color=black>...</font>) и неизбежной хрупкостью, которая влечет за собой абсолютно ничего. В частности, ручной подход:

  • Предлагает случайные упущения. Забудете вставить еще одну подсказку в 3:14 утра в еще один кризисный уик-энд? Да. Это не обернется.Надеюсь, что вы неукоснительно протестируете с помощью беглого осмотра всех всплывающих подсказок или у вас будет плохое время.
  • Приглашает к случайным коллизиям с синтаксисом HTML - в частности, зарезервированный &, < и > символов.Чтобы избежать коллизий, требуется предварительно обработать текст всплывающей подсказки all во всем приложении.Вы не можете больше слепо копировать и вставлять текст всплывающей подсказки, потому что это больше не гарантирует безопасность.Вы забыли заменить даже одну & на &amp; во всплывающей подсказке во время того же ночного кода? Да. Это не будет разбираться.Надеюсь, что вы неукоснительно протестируете с помощью беглого осмотра всех всплывающих подсказок, или у вас будет плохое время.
  • Не масштабируется. Существующие приложения Qt обычно определяют непристойное числовсплывающие подсказки.Что делать, если ваш?Надеюсь, вам понравится глобально искать и заменять текст подсказки all безопасным для HTML способом, избегая столкновения с зарезервированными символами HTML. вы не будете

Мы установили, что ручной подход - неправильный подход.Итак, каков подход правильный ?Есть ли такая вещь в этом инкрустированном ошибками мире?

Фильтр глобальных событий: правильный путь

На самом деле, правильный путь для компании Qt (QtC) - исправить эта чертова ошибка уже .Нет, правда.Прошло более трех лет.Всплывающие подсказки в виде простого текста все еще остаются принципиально неработоспособными.

Поскольку исправление этой чертовой ошибки уже представляется недостижимым, каждое отдельное приложение Qt должно сделать это глобально.приложение, установив фильтр событий всего приложения .Для тех, кто не знаком с фильтрами событий Qt, hi! Я рекомендую просмотреть этот отрывок из свободного пива из Обработка событий глава печально устаревшего текста C ++ GUI Программирование с помощью Qt4 .Он на удивление хорошо постарел, учитывая радиоактивный период полураспада обычной онлайн-статьи.

Все выровнялись?Давайте сделаем это.

Всякий раз, когда установлена ​​ любая подсказка виджета , событие QEvent::ToolTipChange запускается немедленно до , когда эта подсказка фактически установлена.Аналогичным образом, установка фильтра событий на одноэлементном qApp на основе QApplication отправляет каждое событие для каждого объекта в приложении в eventFilter() метод этого фильтра до того, как отправит это событие куда-либо еще.

Вместе эти два наблюдения дают следующее универсальное решение:

  1. Определение нового QAwesomeTooltipEventFilter подкласса базового класса QObject.
  2. В этом подклассепереопределите метод eventFilter() на:
    1. Если текущее событие является событием QEvent::ToolTipChange:
      1. Определите, является ли всплывающая подсказка, установленная этим событием:
        • Форматированный текст (т. Е. Содержит один или несколько HTML-подобных тегов форматированного текста).
        • Открытый текст (т. Е. Не содержит таких тегов).
      2. Если эта подсказка является открытым текстом:
        1. Избегать всех противоречивых синтаксисов HTML в этой подсказке (например, путем глобальной замены всех & символов на &amp; подстрок).
        2. Вставьте эту подсказку в максимально безопасный HTML-подобный тег расширенного текста, гарантированный всегда , уменьшенный до нуля (т. Е. <qt>...</qt>).
        3. Установите подсказку этого виджета в этот текст.
        4. Прекратить обработку этого события.
    2. В противном случае распространяйте, а не обрабатывайте это событие, гарантируя, что всплывающие подсказки с расширенным текстом и все другие события останутся нетронутыми.
  3. Передача экземпляра этого нового подкласса методу QApplication::installEventFilter() сразу после создания синглтона QApplication для вашего приложения.

Поскольку Qt безоговорочно переносит всплывающие подсказки с форматированным текстом, глобальное преобразование всех текстовых подсказок в всплывающие подсказки таким образом правильно охватывает все всплывающие подсказки, не требуя даже одной строки ручного вмешательства. ... немного аплодисментов, может быть?

Небольшой код, может быть?

Необходимый код зависит от того, является ли ваше приложение:

  • C ++. В этом случае я советую раздолбать официальный официальный bitcoin GUI для вдохновения.Устаревший C ++ превышает мой волонтерский мандат, потому что я ленивый.В частности, вы хотите:
  • Python .В этом случае это ваш счастливый день StackOverflow.

Следующий фильтр событий на основе Python предполагает использование PySide2 Qt привязок , потому что ... причины.Теоретически, переписать этот фильтр для альтернативных привязок Qt (например, PyQt5, PySide) должен сократить до замены всех PySide2 подстрок для предпочитаемых имен привязок.

Наконец, мне нравится copiousДокументация reStructuredText (reST) - особенно для нетривиальных патчей обезьяны с таким поведением ядра Qt.Если вы этого не сделаете, вы знаете, что с ним делать.

import html
from PySide2.QtCore import Qt, QEvent, QObject
from PySide2.QtWidgets import QWidget

class QAwesomeTooltipEventFilter(QObject):
    '''
    Tooltip-specific event filter dramatically improving the tooltips of all
    widgets for which this filter is installed.

    Motivation
    ----------
    **Rich text tooltips** (i.e., tooltips containing one or more HTML-like
    tags) are implicitly wrapped by Qt to the width of their parent windows and
    hence typically behave as expected.

    **Plaintext tooltips** (i.e., tooltips containing no such tags), however,
    are not. For unclear reasons, plaintext tooltips are implicitly truncated to
    the width of their parent windows. The only means of circumventing this
    obscure constraint is to manually inject newlines at the appropriate
    80-character boundaries of such tooltips -- which has the distinct
    disadvantage of failing to scale to edge-case display and device
    environments (e.g., high-DPI). Such tooltips *cannot* be guaranteed to be
    legible in the general case and hence are blatantly broken under *all* Qt
    versions to date. This is a `well-known long-standing issue <issue_>`__ for
    which no official resolution exists.

    This filter globally addresses this issue by implicitly converting *all*
    intercepted plaintext tooltips into rich text tooltips in a general-purpose
    manner, thus wrapping the former exactly like the latter. To do so, this
    filter (in order):

    #. Auto-detects whether the:

       * Current event is a :class:`QEvent.ToolTipChange` event.
       * Current widget has a **non-empty plaintext tooltip**.

    #. When these conditions are satisfied:

       #. Escapes all HTML syntax in this tooltip (e.g., converting all ``&``
          characters to ``&amp;`` substrings).
       #. Embeds this tooltip in the Qt-specific ``<qt>...</qt>`` tag, thus
          implicitly converting this plaintext tooltip into a rich text tooltip.

    .. _issue:
        https://bugreports.qt.io/browse/QTBUG-41051
    '''


    def eventFilter(self, widget: QObject, event: QEvent) -> bool:
        '''
        Tooltip-specific event filter handling the passed Qt object and event.
        '''

        # If this is a tooltip event...
        if event.type() == QEvent.ToolTipChange:
            # If the target Qt object containing this tooltip is *NOT* a widget,
            # raise a human-readable exception. While this should *NEVER* be the
            # case, edge cases are edge cases because they sometimes happen.
            if not isinstance(widget, QWidget):
                raise ValueError('QObject "{}" not a widget.'.format(widget))

            # Tooltip for this widget if any *OR* the empty string otherwise.
            tooltip = widget.toolTip()

            # If this tooltip is both non-empty and not already rich text...
            if tooltip and not Qt.mightBeRichText(tooltip):
                # Convert this plaintext tooltip into a rich text tooltip by:
                #
                #* Escaping all HTML syntax in this tooltip.
                #* Embedding this tooltip in the Qt-specific "<qt>...</qt>" tag.
                tooltip = '<qt>{}</qt>'.format(html.escape(tooltip))

                # Replace this widget's non-working plaintext tooltip with this
                # working rich text tooltip.
                widget.setToolTip(tooltip)

                # Notify the parent event handler this event has been handled.
                return True

        # Else, defer to the default superclass handling of this event.
        return super().eventFilter(widget, event)

Глобальная установка этого фильтра событий затем сводится к следующему двухслойному:

from PySide2.QtGui import qApp
qApp.installEventFilter(QAwesomeTooltipEventFilter(qApp))

Voilà! Глобально вменяемые всплывающие подсказки в двадцати строках Python.Какие?Если ты щуришься, то двадцать.

Что за улов?

В зависимости от того, какую именно привязку Qt вы используете, вызываемая выше служебная функция Qt.mightBeRichText() может не определяться (предположительно из-за парсера привязки).проблемы).Если это печальный случай для вашей привязки, самое простое решение:

  • Предположим, что все всплывающие подсказки являются открытым текстом и просто удалите вызов Qt.mightBeRichText(). не умный
  • Сканирование этой подсказки для HTML-подобных тегов, предположительно с эвристикой регулярного выражения. умный, но превышает мой порог лени

Все готово.

2 голосов
/ 10 сентября 2013

Самое простое решение, как уже отмечалось, состоит в том, чтобы заставить подсказку иметь форматированный текст.Однако, НЕ указывайте цвета текста, если вы действительно не знаете, что делаете.Многие цветовые схемы не используют черный в качестве цвета текста подсказки, а некоторые могут даже использовать его в качестве фона, делая подсказку неразборчивой!Есть много других способов «активировать» режим расширенного текста. Тем не менее, я обнаружил, что стандартное сжатие расширенного текста в Qt иногда приводит к меньшей общей ширине, чем хотелось бы, поэтому я написалэта маленькая функция:

QString splitTooltip(QString text, int width) 
{ 
    QFontMetrics fm(QToolTip::font()); 
    QString result; 

    for (;;) { 
        int i = 0; 
        while (i < text.length()) { 
            if (fm.width(text.left(++i + 1)) > width) { 
                int j = text.lastIndexOf(' ', i); 
                if (j > 0) 
                    i = j; 
                result += text.left(i); 
                result += '\n'; 
                text = text.mid(i+1); 
                break; 
            } 
        } 
        if (i >= text.length()) 
            break; 
    } 
    return result + text; 
} 

Это будет работать для простого текста и позволит вам указать желаемую максимальную ширину (в пикселях).Он учитывает фактическую ширину рендеринга текста, а не только количество символов.

2 голосов
/ 25 января 2011

Согласно документации QtoolTip Qt :

Rich text displayed in a tool tip is implicitly word-wrapped unless specified differently with <p style='white-space:pre'>.

Какую версию Qt вы используете?

0 голосов
/ 03 июля 2018

Чтобы объединить ответы выше, вот моя реализация.Вдохновленные вами, ребята.

template<class Ty>
void BasicDpiAwareWidget<Ty>::setToolTip(const QString& tip) {
    if (tip.isEmpty()) {
        return;
    }

    const float kMinTooltipWidth = 400;

    QFontMetrics fm(font());

    int tip_width = fm.width(tip);

    QString html_escaped_tip = tip.toHtmlEscaped();

    if (tip_width <= kMinTooltipWidth) {
        html_escaped_tip.append("</p>");
    }
    else {
        int line_break_index = kMinTooltipWidth / tip_width * tip.size();
        html_escaped_tip.insert(line_break_index, "</p>");
    }

    // See [http://doc.qt.io/qt-5/qtooltip.html#details] for details.
    // Rich text displayed in a tool tip is implicitly word-wrapped unless specified differently with <p style='white-space:pre'>.
    Ty::setToolTip("<style>p { margin: 0 0 0 0 }</style><p style='white-space:pre'>" + html_escaped_tip);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...