Как я могу определить, видна ли всплывающая подсказка в данный момент?
Я ищу способ определить, отображается ли всплывающая подсказка виджета Qt в момент нажатия определенной комбинации клавиш. Если это так, я хочу скопировать текст всплывающей подсказки в буфер обмена.
В частности, у меня есть QListView, содержащий сокращенные строки, который настроен (через Qt::ToolTipRole связанной модели) для отображения полной строки соответствующего элемента списка при наведении на него указателя мыши. Поведение, которое я ищу, заключается в том, что если пользователь нажимает CTRL-C (как обнаружено с помощью QShortcut), когда всплывающая подсказка видна, текст всплывающей подсказки копируется в буфер обмена.
Моя первоначальная идея состояла в том, чтобы использовать метод children() виджета QListView, чтобы увидеть, есть ли среди них предустановленная всплывающая подсказка:
// Inisde the slot connected to QShortcut::activated...
auto children = _ui -> myListView -> children();
QString selectionText;
for (const auto & child : children)
{
if (qobject_cast<QToolTip *>(child))
{
selectionText = qobject_cast<QToolTip *>(child) -> text();
break;
}
}
... но это не удалось, потому что оказалось, что QToolTip не наследуется от QObject.
Я также подумал о проверке событий QEvent::QToolTip в главном обработчике событий ListView, и хотя я, вероятно, мог бы заставить это работать, это кажется чрезмерно низкоуровневым; Мне нужно было бы использовать координаты экрана, чтобы определить, над каким элементом в списке происходит наведение курсора, и искать время ожидания виджета, чтобы убедиться, что всплывающая подсказка не исчезла снова к моменту запуска QShortcut. Я был бы разочарован, если бы не было более простого способа.
Есть ли очевидный путь вперед, который я не увидел?
2 ответа
Вероятно, есть несколько возможных решений, но я боюсь, что ни одно из них не является простым. Что бы я сделал, так это использовал бы деталь реализации, которая называется фактическим виджетом всплывающей подсказки. См. https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qtooltip.cpp.html#QTipLabel , и он наследуется от
QLabel
так что вы можете легко получить текст из него.
Я боюсь, что следующее решение - просто дикий взлом. Я не тестировал его, но он должен работать.
- Я бы переопределил модель данных для вашего представления, в частности переопределил метод, который вызывал бы метод исходного класса модели, но кэшировал последнее значение, которое было возвращено, когда этот метод вызывается с помощью
role == Qt::ToolTipRole
. - Затем вам нужно поймать интересующий вас ярлык. После того, как он будет пойман, вы получите все
qApp->topLevelWidgets()
https://doc.qt.io/qt-5/qapplication.html#topLevelWidgets` и просмотрите их и проверьте, имеет ли какое-либо из них имя класса, равное (используйтеQMetaObject::className()
) и виден, т.е.isVisible() == true
. - Если вы получаете этот видимый виджет (вы держите его через
QWidget*
),qobject_cast
это кQLabel*
(вы не можете использовать его, потому что у вас нет доступа к определению класса, потому что он находится в частном исходном файле Qt) и получить текст с помощьюQLabel::text()
. Если текст совпадает с текстом, который вы сохранили на шаге 1, то да, это именно тот текст, который вы ищете, и вы можете скопировать его в буфер обмена или делать с ним все, что хотите.
Отвратительно, не так ли? Но это самое простое, что я могу придумать.
PS: я считаю, что шаг 1 можно реализовать также путем отлова
QEvent::QToolTip
для вашего просмотра, а затем сделать какое-то волшебство, чтобы получить текст, но я думаю, что переопределение
data()
для модели может быть немного проще.
PPS: один очевидный недостаток заключается в том, что Qt может переименовывать
QTipLabel
класс в будущем. Но я бы не беспокоился об этом. Этого не произойдет, потому что они больше не меняют модуль QtWidgets. И если это произойдет, то вы просто переименуете класс в своем коде. Без проблем.
PPPS: Другим потенциальным краеугольным камнем является то, что какой-то другой виджет (чью всплывающую подсказку вы НЕ хотите захватывать с помощью этого ярлыка) на самом деле имеет тот же текст всплывающей подсказки, что и любой из элементов в вашем представлении списка (который вы ДЕЙСТВИТЕЛЬНО хотите захватить). Затем, если вы отображаете всплывающую подсказку для своего элемента списка, затем вы перемещаете указатель мыши на этот другой виджет и наводите курсор, чтобы отображалась его всплывающая подсказка (но вы НЕ хотите ее захватывать), а затем нажимаете этот ярлык... Но я думаю что на самом деле это будет не ваш случай. Я сомневаюсь, что будет это маловероятное столкновение всплывающих подсказок.
Благодаря @VK получилось вот что:
auto candidates = qApp->topLevelWidgets();
QString selectionText;
for (const auto & candidate : candidates)
{
if (strcmp(candidate->metaObject()->className(), "QTipLabel") == 0)
{
QLabel * label = qobject_cast<QLabel *>(candidate);
if (label->isVisible())
{
selectionText = label -> text();
break;
}
}
}
if (!selectionText.isEmpty())
QGuiApplication::clipboard() -> setText(selectionText);