Я пишу настольное приложение для 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-пиксельной рамкой толщиной:
Это здорово, так как прямоугольник остается поверх всего остального, прозрачен, доступен для ввода мышью и т. Д., Не может получить фокус, изменить размер или переместиться и не отображается на панели задач.
Дело Б Я думал, что единственной оставшейся проблемой было нарисовать толстую яркую границу, поэтому я закодировал это непосредственно перед вызовом 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();
Это окно по-прежнему полностью сквозное, остающееся сверху и т. Д. И имеет правильную границу, но, конечно, оно непрозрачное.
Теперь тот факт, что:
Qt::WA_TranslucentBackground
установить и не меняя таблицу стилей, я получаю черную 1-пиксельную рамку / рамку;Qt::WA_TranslucentBackground
установить и с изменяя таблицу стилей, я не получаю границы / рамки вообще (окно становится невидимым);Qt::WA_TranslucentBackground
установить и с изменяя таблицу стилей, я получаю ожидаемую границу / рамку;.. кажется, подразумевает, что есть какой-то набор стилей для получения черного 1-пикселя вне граница, когда окно прозрачно. Когда я сам изменил стиль границы, он полностью остается внутри оконной рамы и, таким образом, исчезает, когда окно прозрачное (случай B) — я думаю.
Кто-нибудь знает, какими должны быть правильные настройки таблицы стилей? чтобы окно стало полностью прозрачным с зеленой границей в 5 пикселей?
Кроме того, я не смог найти никакой документации, которая бы точно указала, какие стили могут применяться к какому типу виджета / окна (и QFrame
в частности, для моего теста). Это существует где-нибудь? Это было бы полезно знать, так как я также хотел бы использовать градиентные цвета для границы и, возможно, другие эффекты.
Попробуйте заменить 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
бегать Это обычно не проблема.)
я нашел решение, которое заключается в:
QFrame
в исключать внутренняя часть рамы — так тот становится полностью прозрачным.Qt::WA_TranslucentBackground
(иначе ничего не видно, согласно случай B в вопрос).Окончательный код:
//
// 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();
В итоге мы получаем следующее:
Я немного волновался, что вся эта маскировка приведет к не очень оптимальному решению, но потом я понял, что C # пример использование Windows API на самом деле делает нечто очень похожее (прямоугольник внутри прямоугольника, чтобы исключить область, чтобы оставаться прозрачным) .. так что, возможно, это правильный путь.