Windows 10 在 Qt5 中围绕屏幕区域绘制高亮边框
Drawing a highlighting border around a region of the screen in Qt5 on Windows 10
我正在 Windows 10 上编写一个桌面应用程序,其功能类似于 "share your screen/app window",我遇到了试图在屏幕上突出显示感兴趣区域的经典问题。所以我需要绘制一个亮而粗的矩形,让矩形始终可见并且'on top',并且不干扰用户输入、鼠标移动等(即所有直通)。
我无法让它与 Qt v5.7 一起正常工作。我得到一个不透明的 window(我看不到它是什么 "below")和右边框,或者一个透明的 window 只有一个黑色的 1 像素边框。
我隐约知道如果我要使用 Windows 特定的 API,我可以创建一个 window 和 WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE
风格等。
(使用类似 "chrome window" 的东西 - C# here 中的示例),但除了我还没有尝试过的事实(并且需要在 C++ 而不是 C# 中做),我宁愿这样做使用 Qt 是可能的。
案例 A 我还是 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 像素粗边框的矩形:
很棒,因为矩形位于其他所有内容之上,是透明的,可以通过鼠标输入等,无法获得焦点,无法调整大小或移动,也不会显示在任务栏上.
案例 B 我认为剩下的唯一问题是画一个粗的明亮边框,所以我在调用 frame->show()
:[=26= 之前编写了这个代码]
// Set a solid green thick border.
frame->setObjectName("testframe");
frame->setStyleSheet("#testframe {border: 5px solid green;}");
.. 但这完全没有给我任何帮助。 QFrame
根本没有显示!
案例C 所以作为测试,我去掉了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();
那 window 仍然是完全直通、置顶等,并且有正确的边框,当然它是不透明的。
现在,事实是:
- 设置
Qt::WA_TranslucentBackground
且不更改样式 sheet,我得到一个黑色 1 像素 border/frame;
Qt::WA_TranslucentBackground
设置和 更改样式 sheet,我根本没有得到 border/frame(window 变得不可见);
- 没有
Qt::WA_TranslucentBackground
设置和有改变样式sheet,我得到了预期的border/frame;
。当我自己更改边框样式时,它完全位于 window 框架内,因此当 window 透明时消失(案例 B)- 我认为。
有人知道正确的样式 sheet 设置应该是什么吗 以便我得到 window 完全透明的 5 像素绿色边框?
此外,我找不到任何文档可以确切地告诉我什么样式适用于什么类型的 widget/window(特别是 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
class 的实例应显示为具有完全透明主体的绿色边框。用作...
rubber_band *frame = new rubber_band(QRubberBand::Rectangle);
frame->setGeometry(1000, 500, 600, 300);
frame->show();
(注意:在使用 X11 的平台上,上述内容将要求 xcompmgr
等复合管理器为 运行。这通常不是问题。)
我找到了一个解决方案,即:
- 在
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();
我们最终得到的是:
我有点担心所有这些屏蔽会导致不是非常理想的解决方案,但后来我意识到 C# example 使用 Windows API 实际上做了一些非常相似的事情(矩形内的矩形排除该区域以保持透明)。所以也许这是正确的方法。
我正在 Windows 10 上编写一个桌面应用程序,其功能类似于 "share your screen/app window",我遇到了试图在屏幕上突出显示感兴趣区域的经典问题。所以我需要绘制一个亮而粗的矩形,让矩形始终可见并且'on top',并且不干扰用户输入、鼠标移动等(即所有直通)。
我无法让它与 Qt v5.7 一起正常工作。我得到一个不透明的 window(我看不到它是什么 "below")和右边框,或者一个透明的 window 只有一个黑色的 1 像素边框。
我隐约知道如果我要使用 Windows 特定的 API,我可以创建一个 window 和 WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE
风格等。
(使用类似 "chrome window" 的东西 - C# here 中的示例),但除了我还没有尝试过的事实(并且需要在 C++ 而不是 C# 中做),我宁愿这样做使用 Qt 是可能的。
案例 A 我还是 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 像素粗边框的矩形:
很棒,因为矩形位于其他所有内容之上,是透明的,可以通过鼠标输入等,无法获得焦点,无法调整大小或移动,也不会显示在任务栏上.
案例 B 我认为剩下的唯一问题是画一个粗的明亮边框,所以我在调用 frame->show()
:[=26= 之前编写了这个代码]
// Set a solid green thick border.
frame->setObjectName("testframe");
frame->setStyleSheet("#testframe {border: 5px solid green;}");
.. 但这完全没有给我任何帮助。 QFrame
根本没有显示!
案例C 所以作为测试,我去掉了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();
那 window 仍然是完全直通、置顶等,并且有正确的边框,当然它是不透明的。
现在,事实是:
- 设置
Qt::WA_TranslucentBackground
且不更改样式 sheet,我得到一个黑色 1 像素 border/frame; Qt::WA_TranslucentBackground
设置和 更改样式 sheet,我根本没有得到 border/frame(window 变得不可见);- 没有
Qt::WA_TranslucentBackground
设置和有改变样式sheet,我得到了预期的border/frame;
。当我自己更改边框样式时,它完全位于 window 框架内,因此当 window 透明时消失(案例 B)- 我认为。
有人知道正确的样式 sheet 设置应该是什么吗 以便我得到 window 完全透明的 5 像素绿色边框?
此外,我找不到任何文档可以确切地告诉我什么样式适用于什么类型的 widget/window(特别是 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
class 的实例应显示为具有完全透明主体的绿色边框。用作...
rubber_band *frame = new rubber_band(QRubberBand::Rectangle);
frame->setGeometry(1000, 500, 600, 300);
frame->show();
(注意:在使用 X11 的平台上,上述内容将要求 xcompmgr
等复合管理器为 运行。这通常不是问题。)
我找到了一个解决方案,即:
- 在
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();
我们最终得到的是:
我有点担心所有这些屏蔽会导致不是非常理想的解决方案,但后来我意识到 C# example 使用 Windows API 实际上做了一些非常相似的事情(矩形内的矩形排除该区域以保持透明)。所以也许这是正确的方法。