C++ 在Windows 10上的Qt5中,围绕屏幕区域绘制高亮显示边框
我正在Windows 10上编写一个桌面应用程序,它的功能类似于“共享您的屏幕/应用程序窗口”,我遇到了一个典型的问题,即试图在屏幕上突出显示感兴趣的区域。因此,我需要绘制一个明亮而厚实的矩形,使矩形始终可见且“在顶部”,并且不干扰用户输入、鼠标移动等(即全部通过) 我无法使它在QtV5.7中正常工作。我要么得到一个右边框的不透明窗口(我看不见“下面”是什么),要么得到一个只有黑色1像素边框的透明窗口C++ 在Windows 10上的Qt5中,围绕屏幕区域绘制高亮显示边框,c++,qt,windows-10-desktop,C++,Qt,Windows 10 Desktop,我正在Windows 10上编写一个桌面应用程序,它的功能类似于“共享您的屏幕/应用程序窗口”,我遇到了一个典型的问题,即试图在屏幕上突出显示感兴趣的区域。因此,我需要绘制一个明亮而厚实的矩形,使矩形始终可见且“在顶部”,并且不干扰用户输入、鼠标移动等(即全部通过) 我无法使它在QtV5.7中正常工作。我要么得到一个右边框的不透明窗口(我看不见“下面”是什么),要么得到一个只有黑色1像素边框的透明窗口 我隐约知道,如果我使用Windows特定的API,我可以创建一个具有WS_EX_LAYERED
我隐约知道,如果我使用Windows特定的API,我可以创建一个具有
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE
样式的窗口。
(使用C语言中的一个“Chrome窗口”的例子),但是除了我还没有尝试过的事实(需要用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()之前对其进行了编码:
。。但这给了我确切的,呃,什么都没有。QFrame
根本没有显示
案例C因此,作为测试,我删除了Qt::WA_半透明背景的设置。代码如下:
//
// 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_transparcentbackground
并且没有更改样式表,我得到了一个黑色的1像素边框/帧李>
- 设置了
Qt::WA_transparcentbackground
并更改了样式表后,我根本看不到边框/边框(窗口变得不可见)李>
- 如果不设置
Qt::WA_半透明背景,并且更改样式表,我将获得预期的边框/帧李>
。。这似乎意味着,当窗口是透明的时,有一些样式设置用于获取黑色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());
}
};
(注意:在使用X11的平台上,上述要求需要运行复合管理器,例如xcompmgr
。这通常不是问题。)我找到了一个解决方案,它是:
- 在
QFrame
上设置一个遮罩,以排除帧的内部-使其变得完全透明
- 未设置
Qt::WA_半透明背景
(否则,根据问题中的案例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();
我们的结局是:
我有点担心所有这些掩蔽会导致一个不是非常理想的解决方案,但后来我意识到,使用Windows API实际上做了一些非常类似的事情(矩形中的矩形用于排除区域以保持透明)。。因此,也许这是正确的方法。WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE
——确认有效,而且非常简单。如果抽象层(Qt)使某些事情变得太难,那么最好直接使用Win32 API来完成。如果你的应用程序仅适用于Windows,则更是如此。我同意,但我会先尝试Qt(更不用说我的应用程序目前仅适用于Windows),因为它在我工作的地方被广泛使用,而我的应用程序的其余部分是Qt(UI和程序控制)。我的问题可能有一个简单的解决方案,但如果没有,那么我将转而使用CreateWindowEx()等,尽管代码看起来会更长:-)。您可能想看看如何使用无父对象。可能必须使用,但我认为它会给你一些有用的东西。一个有趣的建议,@G.M.,谢谢你,它几乎成功了;-)。今天早上(我的时间),在你发表评论和解决方案之前,我已经有了一个解决方案——我马上就发布。我说QRubberBand(我不知道;但在Qt中还有很多我不知道)几乎起作用了,因为似乎没有办法将边框的不透明度与内部矩形的不透明度分离,除非可以使用样式表设置。我将用我的发现编辑您的解决方案。很抱歉没有尽快回复,但是。。。正如您自己所发现的,使用QWidget::setMask
是更通用的解决方案,应该可以在所有“合理”的情况下工作。请注意,调用QWidget::setWindowOpacity
也适用于QRubberBand
解决方案,但仍需遵守别处指出的X11
注意事项。话虽如此。。。。如果它对你有用的话,那就是最重要的了——把它运出去!须缴付附加费
rubber_band *frame = new rubber_band(QRubberBand::Rectangle);
frame->setGeometry(1000, 500, 600, 300);
frame->show();
//
// 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();