Подтвердить что ты не робот

Есть ли лучший способ wordwrap text в QToolTip, чем просто использовать RegExp?

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

4b9b3361

Ответ 1

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

Здесь тривиальный пример, когда установка шрифта в черный цвет делает его "богатым текстом", и поэтому он обертывается словами. Оставляя декларации шрифтов, подсказка будет иметь простой текст и расширять всю длину экрана.

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 tracker: https://bugreports.qt.io/browse/QTBUG-41051. Он также требует, чтобы ширина была изменчивой.

Ответ 2

Альтернативный ответ: используйте <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, которая по моему опыту имеет тенденцию быть слишком узкой.)

Ответ 3

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

Тем не менее, я обнаружил, что обширная текстовая упаковка 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; 
} 

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

Ответ 4

Согласно Документация Qt QToolTip:

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

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

Ответ 5

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

Что не так с "<font>... </font>", во всяком случае?

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

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

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

Глобальный фильтр событий: правильный путь

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

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

Все выровнялись? Позвольте сделать это.

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

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

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

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

Может быть, маленький кодекс?

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

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

Наконец, мне нравится обширная документация 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 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))

Вуаля! Глобальные разумные подсказки в 20 строках Python. Какие? Двадцать, если вы косо.

Что за Catch?

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

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

Мы здесь.

Ответ 6

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

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);
}