使用Qt控件(例如QWidget或QLabel)做视频播放,当需要在视频上画框或者画点线做标记时,一般无法直接在播放的控件上画出。在正在播放的视频窗口,画一个小于视频窗口的区域,然后截取该区域保存至图片,是一个很常见的抓拍需求。
这个时候的思路是在正在播放视频的窗口叠加一层窗口,将该叠加窗口设置为透明,设置一些基本窗口属性,重写其paintEvent()事件。当鼠标进入视频窗口后,显示这个透明窗口,然后在透明窗口的paintEvent()中根据需要的大小画截图框即可。如下图所示:
接着重写鼠标按下事件pressEvent()事件,在该事件中做需要的动作。
本文给出叠加透明窗口的一个基本思路和demo实现,以及记录下需要注意的事项。demo示例程序使用 Qt5.8.0,msvc版。demo界面如下图所示(鼠标进入后会显示在截图框最中央,且变成十字,截图无法展示):
主窗体
示例程序中命名为 FormMainWidget,声明在FormMainWidget.h,继承QWidget类,没什么好说的。
视频窗口
命名为FormVideoDialog,继承QLabel。因为要获取其各种事件,故在主窗体FormMainWidget中将该类提升为FormVideoDialog。
透明窗口
命名为FormTransparentDialog类,是FormVideoDialog的类成员。
1、透明窗口FormTransparentDialog不能和FormVideoDialog是父子窗口关系,否则FormTransparentDialog将正常显示;
2、透明窗口FormTransparentDialog在初始化时要加入下述三句代码:
setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow | Qt::WindowStaysOnTopHint);// 设置无边框、子窗口
setAttribute(Qt::WA_TranslucentBackground, true); // 设置为透明窗口
setMouseTracking(true); // 设置鼠标移动事件生效
3、如果透明窗口显示,主窗口FormMainWidget在移动时需要重写moveEvent()事件,并将该事件通知到FormVideoDialog和FormTransparentDialog(本程序是以信号槽的形式通知到FormTransparentDialog),否则FormTransparentDialog不会跟着主窗体的移动而移动。当然可以选择鼠标离开FormTransparentDialog时让FormTransparentDialog隐藏。
以下是FormTransparentDialog的paintEvent,在该事件中画截图框,最关键的部分是AdjustPaintArea()函数,该函数计算框的位置,和边界判断,具体看代码:
void FormTransparentDialog::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.fillRect(rect(), m_transparentColor);
painter.setPen(QPen(Qt::red, m_iLineWidth, Qt::SolidLine, Qt::RoundCap));
GetAreaDialogSize();
int iPaintLeft = 0, iPaintTop = 0;
int iPaintWidth = m_iAreaDialogWidth, iPaintHeight = m_iAreaDialogHeight;
QPoint mousePoint = mapFromGlobal(QCursor::pos());
AdjustPaintArea(iPaintLeft, iPaintTop, mousePoint);
painter.drawRect(QRect(iPaintLeft, iPaintTop, iPaintWidth, iPaintHeight));
}
void FormTransparentDialog::GetAreaDialogSize()
{
m_iAreaDialogWidth = FormMainWidget::GetMainWidget()->GetAreaDialogWidth();
m_iAreaDialogWidth = m_iAreaDialogWidth > 0 ? m_iAreaDialogWidth : 300;
m_iAreaDialogHeight = FormMainWidget::GetMainWidget()->GetAreaDialogHeight();
m_iAreaDialogHeight = m_iAreaDialogHeight > 0 ? m_iAreaDialogHeight : 200;
}
void FormTransparentDialog::AdjustPaintArea(int& iPaintLeft, int& iPaintTop, const QPoint& mousePoint)
{
CalculateAreaLeftTop(iPaintLeft, iPaintTop, mousePoint);
}
void FormTransparentDialog::CalculateAreaLeftTop(int& iPaintLeft, int& iPaintTop, const QPoint& mousePoint)
{
if (mousePoint.x() < (m_iAreaDialogWidth + 2 * m_iLineWidth) / 2)
{
iPaintLeft = 0;
CalculateAreaTop(iPaintTop, mousePoint);
}
else if (mousePoint.x() > size().width() - (m_iAreaDialogWidth + 2 * m_iLineWidth) / 2)
{
iPaintLeft = size().width() - (m_iAreaDialogWidth + 2 * m_iLineWidth);
CalculateAreaTop(iPaintTop, mousePoint);
}
else
{
iPaintLeft = mousePoint.x() - (m_iAreaDialogWidth + 2 * m_iLineWidth) / 2;
CalculateAreaTop(iPaintTop, mousePoint);
}
}
void FormTransparentDialog::CalculateAreaTop(int& iPaintTop, const QPoint& mousePoint)
{
if (mousePoint.y() < (m_iAreaDialogHeight + 2 * m_iLineWidth) / 2)
{
iPaintTop = 0;
}
else if (mousePoint.y() > size().height() - (m_iAreaDialogHeight + 2 * m_iLineWidth) / 2)
{
iPaintTop = size().height() - (m_iAreaDialogHeight + 2 * m_iLineWidth);
}
else
{
iPaintTop = mousePoint.y() - (m_iAreaDialogHeight + 2 * m_iLineWidth) / 2;
}
}
demo的代码资源链接: https://download.csdn.net/download/explorer114/11097452