- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试重建一个屏幕记录PyQt应用程序,ScreenToGIF对我来说是一个非常好的演示,它创建了一个只有边框并在“Central Widgets”中记录内容的界面,如下所示:
主要功能为:
但是,它是用C#实现的(链接:https://github.com/NickeManarin/ScreenToGif),我想知道是否可以在不学习C#专业知识的情况下制作类似的PyQt App?
Changing the background image of QMainWidgets to the desktop area been overlayed doesn't make sense, because mouse operation on desktop (such as double click to open files) should be recorded. Mouse event can penetrate the app (like Qt.WindowTransparentForInput applied for inner contents?)
最佳答案
您想要实现的目标需要设置 mask ,允许您拥有一个具有特定“形状”的小部件,而不必是矩形。
主要困难是理解如何 window geometries工作,这可能很棘手。
您必须确保已计算窗口“框架”(包括其边距和标题栏 - 如果有),然后找出内部矩形并相应地创建一个掩码。请注意,在 Linux 上会发生这种情况 "some time"调用 show()
后;我认为您使用的是 Windows,但我已经以一种应该在 Linux、MacOS 和 Windows 上都能正常工作的方式实现了它。如果您确定您的程序仅在 Windows 上运行,则会有相关评论。
最后,我只能在 Linux、Wine 和虚拟化 WinXP 环境上运行它。它应该在任何系统上都能正常工作,但是,根据我的经验,存在一个特定的“外观”错误:标题栏不是根据当前的 Windows 主题绘制的。我认为这是因为每当应用 mask 时,底层 Windows 系统都不会像通常那样绘制其“样式化”窗口框架。如果这种情况也发生在较新的系统中,可能有一个解决方法,但这并不容易,而且我不能保证它能解决这个问题。
注意:请记住,这种方法永远不允许您在“抓取矩形”内绘制任何内容(没有阴影,也没有半透明颜色 mask );这样做的原因是,您显然需要与小部件“下方”的内容实现鼠标交互,并且在其上绘画需要更改覆盖 mask 。
from PyQt5 import QtCore, QtGui, QtWidgets
class VLine(QtWidgets.QFrame):
# a simple VLine, like the one you get from designer
def __init__(self):
super(VLine, self).__init__()
self.setFrameShape(self.VLine|self.Sunken)
class Grabber(QtWidgets.QWidget):
dirty = True
def __init__(self):
super(Grabber, self).__init__()
self.setWindowTitle('Screen grabber')
# ensure that the widget always stays on top, no matter what
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
# limit widget AND layout margins
layout.setContentsMargins(0, 0, 0, 0)
self.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# create a "placeholder" widget for the screen grab geometry
self.grabWidget = QtWidgets.QWidget()
self.grabWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
layout.addWidget(self.grabWidget)
# let's add a configuration panel
self.panel = QtWidgets.QWidget()
layout.addWidget(self.panel)
panelLayout = QtWidgets.QHBoxLayout()
self.panel.setLayout(panelLayout)
panelLayout.setContentsMargins(0, 0, 0, 0)
self.setContentsMargins(1, 1, 1, 1)
self.configButton = QtWidgets.QPushButton(self.style().standardIcon(QtWidgets.QStyle.SP_ComputerIcon), '')
self.configButton.setFlat(True)
panelLayout.addWidget(self.configButton)
panelLayout.addWidget(VLine())
self.fpsSpinBox = QtWidgets.QSpinBox()
panelLayout.addWidget(self.fpsSpinBox)
self.fpsSpinBox.setRange(1, 50)
self.fpsSpinBox.setValue(15)
panelLayout.addWidget(QtWidgets.QLabel('fps'))
panelLayout.addWidget(VLine())
self.widthLabel = QtWidgets.QLabel()
panelLayout.addWidget(self.widthLabel)
self.widthLabel.setFrameShape(QtWidgets.QLabel.StyledPanel|QtWidgets.QLabel.Sunken)
panelLayout.addWidget(QtWidgets.QLabel('x'))
self.heightLabel = QtWidgets.QLabel()
panelLayout.addWidget(self.heightLabel)
self.heightLabel.setFrameShape(QtWidgets.QLabel.StyledPanel|QtWidgets.QLabel.Sunken)
panelLayout.addWidget(QtWidgets.QLabel('px'))
panelLayout.addWidget(VLine())
self.recButton = QtWidgets.QPushButton('rec')
panelLayout.addWidget(self.recButton)
self.playButton = QtWidgets.QPushButton('play')
panelLayout.addWidget(self.playButton)
panelLayout.addStretch(1000)
def updateMask(self):
# get the *whole* window geometry, including its titlebar and borders
frameRect = self.frameGeometry()
# get the grabWidget geometry and remap it to global coordinates
grabGeometry = self.grabWidget.geometry()
grabGeometry.moveTopLeft(self.grabWidget.mapToGlobal(QtCore.QPoint(0, 0)))
# get the actual margins between the grabWidget and the window margins
left = frameRect.left() - grabGeometry.left()
top = frameRect.top() - grabGeometry.top()
right = frameRect.right() - grabGeometry.right()
bottom = frameRect.bottom() - grabGeometry.bottom()
# reset the geometries to get "0-point" rectangles for the mask
frameRect.moveTopLeft(QtCore.QPoint(0, 0))
grabGeometry.moveTopLeft(QtCore.QPoint(0, 0))
# create the base mask region, adjusted to the margins between the
# grabWidget and the window as computed above
region = QtGui.QRegion(frameRect.adjusted(left, top, right, bottom))
# "subtract" the grabWidget rectangle to get a mask that only contains
# the window titlebar, margins and panel
region -= QtGui.QRegion(grabGeometry)
self.setMask(region)
# update the grab size according to grabWidget geometry
self.widthLabel.setText(str(self.grabWidget.width()))
self.heightLabel.setText(str(self.grabWidget.height()))
def resizeEvent(self, event):
super(Grabber, self).resizeEvent(event)
# the first resizeEvent is called *before* any first-time showEvent and
# paintEvent, there's no need to update the mask until then; see below
if not self.dirty:
self.updateMask()
def paintEvent(self, event):
super(Grabber, self).paintEvent(event)
# on Linux the frameGeometry is actually updated "sometime" after show()
# is called; on Windows and MacOS it *should* happen as soon as the first
# non-spontaneous showEvent is called (programmatically called: showEvent
# is also called whenever a window is restored after it has been
# minimized); we can assume that all that has already happened as soon as
# the first paintEvent is called; before then the window is flagged as
# "dirty", meaning that there's no need to update its mask yet.
# Once paintEvent has been called the first time, the geometries should
# have been already updated, we can mark the geometries "clean" and then
# actually apply the mask.
if self.dirty:
self.updateMask()
self.dirty = False
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
grabber = Grabber()
grabber.show()
sys.exit(app.exec_())
关于python - 是否可以创建仅具有外边框的 QMainWindow?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57717331/
我是一名优秀的程序员,十分优秀!