【发布时间】:2020-03-15 11:48:48
【问题描述】:
我正在尝试截取 PyQt5 中当前活动窗口的屏幕截图。我知道截取任何窗口的通用方法是QScreen::grabWindow(winID),其中winID 是implementation-specific ID depending on the window system。由于我正在运行 X 和 KDE,我计划最终使用 CTypes 来调用 Xlib,但现在我只是执行“xdotool getactivewindow”来获取 shell 中的 windowID。
举个例子,我用 QTimer 创建了一个 QMainWindow。当计时器被触发时,我通过执行“xdotool getactivewindow”来识别活动窗口ID,获取它的返回值,调用grabWindow() 来捕获活动窗口,并在QLabel 中显示屏幕截图。在启动时,我还将我的窗口设置为固定的 500x500 大小以供观察,并激活Qt.WindowStaysOnTopHint 标志,以便我的窗口在未聚焦时仍然可见。把它们放在一起,实现是下面的代码。
from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
class ScreenCapture(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.setFixedHeight(500)
self.setFixedWidth(500)
self.label = QtWidgets.QLabel(self)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(500)
self.timer.timeout.connect(self.timer_handler)
self.timer.start()
self.screen = QtWidgets.QApplication.primaryScreen()
@QtCore.pyqtSlot()
def timer_handler(self):
window = int(subprocess.check_output(["xdotool", "getactivewindow"]).decode("ascii"))
self.screenshot = self.screen.grabWindow(window)
self.label.setPixmap(self.screenshot)
self.label.setFixedSize(self.screenshot.size())
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = ScreenCapture()
window.show()
app.exec()
为了测试实现,我启动了脚本并单击了另一个窗口。如果我的应用程序窗口和活动窗口之间没有重叠,它似乎可以正常工作。 See the following screenshot, when Firefox (right) is selected, my application is able to capture the active window of Firefox and display it in the QLabel.
但是,如果应用程序窗口和活动窗口之间存在重叠,则屏幕截图不会按预期工作。应用程序本身的窗口将被捕获,并创建一个积极的反馈。
如果应用程序窗口和活动窗口之间存在重叠。应用程序本身的窗口将被捕获,并创建一个积极的反馈。
我已经在 KDE 的设置中禁用了 3D 合成,但问题仍然存在。上述示例是在禁用所有复合效果的情况下拍摄的。
问题
当应用程序窗口和活动窗口重叠时,为什么这个实现不能正常工作?我怀疑这是由图形系统(Qt 工具包、窗口管理器、X 等)之间某些形式的不需要的交互引起的问题,但我不确定。
是否有可能解决这个问题? (注意:我知道我可以在截图之前
hide(),然后再show(),但这并不能真正解决这个问题,即使存在重叠也会截图。)
【问题讨论】:
-
我认为现象是这样的:grabWindow 不记录到窗口而是记录到窗口占据的区域,在重叠的情况下你看到的效果和使用镜像一样另一面镜子:无限递归。
-
@eyllanesc 同意。我最初认为这个问题是由复合窗口管理器造成的,但就像你所说的那样,我重新检查了文档,是的,“grabWindow() 函数从屏幕上抓取像素,而不是从窗口中,即如果有是另一个窗口部分或完全覆盖您抓取的窗口,您也会从覆盖的窗口中获取像素。”,因此使用grabWindow()从一开始就注定要失败,看来我需要在较低级别进行编程才能获得“真正的”窗口,Qt 太高级了,无法完成这项工作......
标签: python linux pyqt5 screenshot x11