Рисование подсвечивающей границы вокруг области экрана в Qt5 в Windows 10

Я пишу настольное приложение для Windows 10, которое выполняет что-то похожее на «общий доступ к окну / окну приложения», и у меня есть классическая проблема — попытаться выделить интересующую область на экране. Поэтому мне нужно нарисовать яркий и толстый прямоугольник, чтобы он всегда был виден и находился «сверху», и не мешал пользовательскому вводу, движению мыши и т. Д. (Т. Е. Все проходные).

Я не могу заставить его работать должным образом с Qt v5.7. Я либо получаю непрозрачное окно (я не вижу, что «под ним») с правой границей, либо прозрачное окно только с черной 1-пиксельной границей.

Я смутно знаю, что если бы я использовал специфичные для Windows API, я мог бы создать окно с WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE стиль и т. д.
(используя что-то вроде «хромового окна» — пример на C # Вот), но помимо того факта, что я еще не пробовал (и нужно делать на C ++, а не на C #), я бы предпочел сделать это с помощью Qt.

Случай А Я все еще новичок в Qt, но я подумал, что используя QFrame был путь, поэтому я написал код:

//
// A- Displays a completely transparent rectangle with a black 1-pixel border.
//
QFrame  *frame = new QFrame();
frame->setFrameStyle(QFrame::Box | QFrame::Plain);
frame->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus | Qt::WindowStaysOnTopHint);
frame->setAttribute(Qt::WA_TranslucentBackground, true);
frame->setGeometry(1000,500,600,300);    // Just some fixed values to test
frame->show();

Это дает мне прямоугольник с черной 1-пиксельной рамкой толщиной:

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

ChromeWindowCaseA

Дело Б Я думал, что единственной оставшейся проблемой было нарисовать толстую яркую границу, поэтому я закодировал это непосредственно перед вызовом frame->show():

// Set a solid green thick border.
frame->setObjectName("testframe");
frame->setStyleSheet("#testframe {border: 5px solid green;}");

.. но это ничего мне не дало. QFrame не показывал вообще!

Дело С В качестве теста я удалил настройку Qt::WA_TranslucentBackground, Вот код:

//
// C- Displays an opaque pass-through window with a green 5-pixel border.
//
QFrame  *frame = new QFrame();
frame->setFrameStyle(QFrame::Box | QFrame::Plain);
frame->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus | Qt::WindowStaysOnTopHint);
// Disabled Qt::WA_TranslucentBackground
//frame->setAttribute(Qt::WA_TranslucentBackground, true);
frame->setGeometry(1000,500,600,300);    // Just some fixed values to test
// Set a solid green thick border.
frame->setObjectName("testframe");
frame->setStyleSheet("#testframe {border: 5px solid green;}");
frame->show();

ChromeWindowCaseC

Это окно по-прежнему полностью сквозное, остающееся сверху и т. Д. И имеет правильную границу, но, конечно, оно непрозрачное.

Теперь тот факт, что:

  • с Qt::WA_TranslucentBackground установить и не меняя таблицу стилей, я получаю черную 1-пиксельную рамку / рамку;
  • с Qt::WA_TranslucentBackground установить и с изменяя таблицу стилей, я не получаю границы / рамки вообще (окно становится невидимым);
  • без Qt::WA_TranslucentBackground установить и с изменяя таблицу стилей, я получаю ожидаемую границу / рамку;

.. кажется, подразумевает, что есть какой-то набор стилей для получения черного 1-пикселя вне граница, когда окно прозрачно. Когда я сам изменил стиль границы, он полностью остается внутри оконной рамы и, таким образом, исчезает, когда окно прозрачное (случай B) — я думаю.

Кто-нибудь знает, какими должны быть правильные настройки таблицы стилей? чтобы окно стало полностью прозрачным с зеленой границей в 5 пикселей?

Кроме того, я не смог найти никакой документации, которая бы точно указала, какие стили могут применяться к какому типу виджета / окна (и QFrame в частности, для моего теста). Это существует где-нибудь? Это было бы полезно знать, так как я также хотел бы использовать градиентные цвета для границы и, возможно, другие эффекты.

2

Решение

Попробуйте заменить QFrame в вашем коде с чем-то вроде …

class rubber_band: public QRubberBand {
using super = QRubberBand;
public:
template<typename... Types>
explicit rubber_band (const Types &... args)
: super(args...)
{
setAttribute(Qt::WA_TranslucentBackground, true);
}
protected:
virtual void paintEvent (QPaintEvent *event) override
{
QPainter painter(this);
QPen pen(Qt::green);
pen.setWidth(10);
painter.setPen(pen);
painter.drawLine(rect().topLeft(), rect().topRight());
painter.drawLine(rect().topRight(), rect().bottomRight());
painter.drawLine(rect().bottomRight(), rect().bottomLeft());
painter.drawLine(rect().bottomLeft(), rect().topLeft());
}
};

Пример выше rubber_band класс должен отображаться в виде зеленой рамки с полностью прозрачным телом. Использовать как…

rubber_band *frame = new rubber_band(QRubberBand::Rectangle);
frame->setGeometry(1000, 500, 600, 300);
frame->show();

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

1

Другие решения

я нашел решение, которое заключается в:

  • установить маску на QFrame в исключать внутренняя часть рамы — так тот становится полностью прозрачным.
  • не установлен Qt::WA_TranslucentBackground (иначе ничего не видно, согласно случай B в вопрос).
  • Я также установил непрозрачность на 0,5, чтобы визуализируемая граница была частично прозрачной.

Окончательный код:

//
// D- Displays a completely transparent pass-through window with a green 5-pixel translucent border.
//
QFrame  *frame = new QFrame();
frame->setFrameStyle(QFrame::Box | QFrame::Plain);
frame->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus | Qt::WindowStaysOnTopHint);
frame->setGeometry(1000,500,600,300);    // Just some fixed values to test
// Set a solid green thick border.
frame->setObjectName("testframe");
frame->setStyleSheet("#testframe {border: 5px solid green;}");
// IMPORTANT: A QRegion's coordinates are relative to the widget it's used in. This is not documented.
QRegion wholeFrameRegion(0,0,600,300);
QRegion innerFrameRegion = wholeFrameRegion.subtracted(QRegion(5,5,590,290));
frame->setMask(innerFrameRegion);
frame->setWindowOpacity(0.5);
frame->show();

В итоге мы получаем следующее:

ChromeWindowCaseD

Я немного волновался, что вся эта маскировка приведет к не очень оптимальному решению, но потом я понял, что C # пример использование Windows API на самом деле делает нечто очень похожее (прямоугольник внутри прямоугольника, чтобы исключить область, чтобы оставаться прозрачным) .. так что, возможно, это правильный путь.

1

По вопросам рекламы [email protected]