Превалирующих ответов на этот вопрос технически достаточно: краткосрочные технические исправления (также известные как: , когда вам просто нужны подсказки, чтобы работать как рекламируется ), нодалеко не универсальное решение.
Что не так с " ... ", в любом случае?
Если вы не возражаете против ручного встраивания весь текст всплывающей подсказки во всем приложении в тегах HTML с возможными вредными побочными эффектами (например, <font color=black>...</font>
) и неизбежной хрупкостью, которая влечет за собой абсолютно ничего. В частности, ручной подход:
- Предлагает случайные упущения. Забудете вставить еще одну подсказку в 3:14 утра в еще один кризисный уик-энд? Да. Это не обернется.Надеюсь, что вы неукоснительно протестируете с помощью беглого осмотра всех всплывающих подсказок или у вас будет плохое время.
- Приглашает к случайным коллизиям с синтаксисом HTML - в частности, зарезервированный
&
, <
и >
символов.Чтобы избежать коллизий, требуется предварительно обработать текст всплывающей подсказки all во всем приложении.Вы не можете больше слепо копировать и вставлять текст всплывающей подсказки, потому что это больше не гарантирует безопасность.Вы забыли заменить даже одну &
на &
во всплывающей подсказке во время того же ночного кода? Да. Это не будет разбираться.Надеюсь, что вы неукоснительно протестируете с помощью беглого осмотра всех всплывающих подсказок, или у вас будет плохое время. - Не масштабируется. Существующие приложения Qt обычно определяют непристойное числовсплывающие подсказки.Что делать, если ваш?Надеюсь, вам понравится глобально искать и заменять текст подсказки all безопасным для HTML способом, избегая столкновения с зарезервированными символами HTML. вы не будете
Мы установили, что ручной подход - неправильный подход.Итак, каков подход правильный ?Есть ли такая вещь в этом инкрустированном ошибками мире?
Фильтр глобальных событий: правильный путь
На самом деле, правильный путь для компании Qt (QtC) - исправить эта чертова ошибка уже .Нет, правда.Прошло более трех лет.Всплывающие подсказки в виде простого текста все еще остаются принципиально неработоспособными.
Поскольку исправление этой чертовой ошибки уже представляется недостижимым, каждое отдельное приложение Qt должно сделать это глобально.приложение, установив фильтр событий всего приложения .Для тех, кто не знаком с фильтрами событий Qt, hi! Я рекомендую просмотреть этот отрывок из свободного пива из Обработка событий глава печально устаревшего текста C ++ GUI Программирование с помощью Qt4 .Он на удивление хорошо постарел, учитывая радиоактивный период полураспада обычной онлайн-статьи.
Все выровнялись?Давайте сделаем это.
Всякий раз, когда установлена любая подсказка виджета , событие QEvent::ToolTipChange
запускается немедленно до , когда эта подсказка фактически установлена.Аналогичным образом, установка фильтра событий на одноэлементном qApp
на основе QApplication
отправляет каждое событие для каждого объекта в приложении в eventFilter()
метод этого фильтра до того, как отправит это событие куда-либо еще.
Вместе эти два наблюдения дают следующее универсальное решение:
- Определение нового
QAwesomeTooltipEventFilter
подкласса базового класса QObject
. - В этом подклассепереопределите метод
eventFilter()
на: - Если текущее событие является событием
QEvent::ToolTipChange
: - Определите, является ли всплывающая подсказка, установленная этим событием:
- Форматированный текст (т. Е. Содержит один или несколько HTML-подобных тегов форматированного текста).
- Открытый текст (т. Е. Не содержит таких тегов).
- Если эта подсказка является открытым текстом:
- Избегать всех противоречивых синтаксисов HTML в этой подсказке (например, путем глобальной замены всех
&
символов на &
подстрок). - Вставьте эту подсказку в максимально безопасный HTML-подобный тег расширенного текста, гарантированный всегда , уменьшенный до нуля (т. Е.
<qt>...</qt>
). - Установите подсказку этого виджета в этот текст.
- Прекратить обработку этого события.
- В противном случае распространяйте, а не обрабатывайте это событие, гарантируя, что всплывающие подсказки с расширенным текстом и все другие события останутся нетронутыми.
- Передача экземпляра этого нового подкласса методу
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 ``&`` 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-подобных тегов, предположительно с эвристикой регулярного выражения. умный, но превышает мой порог лени
Все готово.