【问题标题】:Drawing a highlighting border around a region of the screen in Qt5 on Windows 10在 Windows 10 上的 Qt5 中围绕屏幕区域绘制高亮边框
【发布时间】:2017-04-27 18:24:09
【问题描述】:

我正在 Windows 10 上编写一个桌面应用程序,它执行类似于“共享您的屏幕/应用程序窗口”的操作,并且我遇到了试图突出显示屏幕上感兴趣的区域的经典问题。所以我需要画一个明亮而厚实的矩形,让矩形始终可见且“位于顶部”,并且不会干扰用户输入、鼠标移动等(即所有传递)。

我无法让它与 Qt v5.7 一起正常工作。我要么得到一个带有右边框的不透明窗口(我看不到“下面”的内容),要么得到一个只有黑色 1 像素边框的透明窗口。

我隐约知道,如果我要使用特定于 Windows 的 API,我可以创建一个具有 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() 之前编写了这个代码:

// 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();

那个窗口仍然是完全通过的,停留在顶部等,并且有右边框,但它当然是不透明的。

现在,事实是:

  • 在设置Qt::WA_TranslucentBackground 且不更改样式表的情况下,我得到一个黑色的 1 像素边框/框架;
  • 设置Qt::WA_TranslucentBackground更改样式表,我根本没有边框/框架(窗口变得不可见);
  • 没有 Qt::WA_TranslucentBackground 设置和 更改样式表,我得到了预期的边框/框架;

.. 似乎暗示当窗口透明时,设置了一些样式来获取黑色 1 像素 outside 边框。当我自己更改边框样式时,它完全保留在窗口框架内,因此当窗口透明时消失(案例 B) - 我认为。

有人知道正确的样式表设置应该是什么,这样我才能让窗口完全透明,并带有 5 像素的绿色边框?

此外,我找不到任何文档可以准确地告诉我哪些样式可以应用于什么类型的小部件/窗口(尤其是 QFrame,对于我的测试用例)。这在任何地方都存在吗?知道会很方便,因为我还想为边框使用渐变色,可能还有其他效果。

【问题讨论】:

  • WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVE -- 确认可以工作,而且相当简单。如果抽象层 (Qt) 使某些事情变得过于困难,那么直接使用 Win32 API 来完成任务可能会更好。如果您的应用仅适用于 Windows,则更是如此。
  • 我同意,但我首先尝试 Qt(不要介意我的应用程序目前仅适用于 Windows),因为它在我工作的地方被广泛使用,而我的应用程序的其余部分是 Qt(UI 和程序控制)。我的问题可能有一个简单的解决方法,但如果没有,那么我将使用 CreateWindowEx() 等,尽管看起来代码会更长:-)。
  • 您可能想看看使用没有父母的QRubberBand。可能必须使用QWidget::setWindowOpacity,但我认为它会给你一些有用的东西。

标签: c++ qt windows-10-desktop


【解决方案1】:

尝试将代码中的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。这通常不是问题。)

【讨论】:

  • 一个有趣的建议,@G.M.,谢谢,它几乎奏效了 ;-)。实际上,在您发表评论和解决方案之前,我今天早上(我的时间)已经有了一个解决方案 - 我稍后会发布它。我说 QRubberBand(我不知道;但在 Qt 中还有很多我不知道的)几乎起作用了,因为似乎没有办法将边界的不透明度与内部矩形 - 除非可以通过样式表设置来完成。我将根据我的发现编辑您的解决方案。
【解决方案2】:

我找到了一个的解决办法,就是:

  • QFrame 上设置一个蒙版以排除框架内部 - 这样就变得完全透明了。
  • 不设置Qt::WA_TranslucentBackground(否则什么都看不到,根据问题中的case 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 的C# example 实际上做了非常相似的事情(一个矩形内的一个矩形来排除该区域以保持透明) ..所以也许这是正确的方法。

【讨论】:

  • 很抱歉没有尽快回复,但是......正如您自己发现的那样,使用QWidget::setMask 是更/最通用的解决方案,应该适用于所有“合理”的情况。请注意,尽管调用QWidget::setWindowOpacity 也适用于QRubberBand 解决方案,但仍需遵守其他地方指出的X11 警告。说了这么多......如果它对你有用,那么这就是最重要的——发货!当然要经过测试:-)
  • 没问题。您是否注意到我在尝试QRubberBand 解决方案时对您的解决方案进行了编辑?我在发布我的解决方案之前进行了编辑,在您发布您的解决方案之前我已经解决了这个问题。我认为,最重要的发现确实是setWindowOpacity() 的影响——我的编辑正在等待“同行评审”(我猜可能是你的)。 PS:实际实施正在进行中 - 当然会进行测试:-D。
  • 这有点问题,如果我在视频播放窗口周围画这样的边框,它会闪烁。如何解决这个问题?
  • 嗨托尼。我有直播视频的QFrame,它很好而且稳定。如果您的边框闪烁,可能是因为它被擦除并重新绘制(检查您的代码以查看是否会发生这种情况),或者是环境(操作系统、其他应用程序)的一些干扰。首先检查第一个案例以确保/修复它。
猜你喜欢
  • 2014-06-17
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-09
  • 1970-01-01
  • 2016-01-14
相关资源
最近更新 更多