我正在尝试拍摄PyQt5中当前活动窗口的屏幕截图。我知道拍摄任何窗口截图的通用方法是QScreen::grabWindow(winID)
,其中winID
是implementation-specific ID depending on the window system。因为我运行的是X和KDE,所以我计划最终使用CTypes调用Xlib,但现在,我只需执行“xdotool getactivewindow”来获取shell中的windowID
对于最小的exmaple,我创建了一个带有QTimer的QMainWindow。当启动计时器时,我通过执行“xdool getactivewindow”来识别活动窗口ID,获取其返回值,调用grabWindow()来捕获活动窗口,并在QLabel中显示screetshot。在启动时,我还将我的窗口设置为固定的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()
为了测试实现,我启动脚本并单击另一个窗口。如果我的应用程序窗口和活动窗口之间没有重叠,那么它似乎可以正常工作。请参见下面的屏幕截图,当选择Firefox(右)时,我的应用程序能够捕获Firefox的活动窗口并将其显示在QLabel中
但是,如果应用程序窗口和活动窗口之间存在重叠,则屏幕截图无法按预期工作。应用程序本身的窗口将被捕获,并创建积极的反馈
如果应用程序窗口和活动窗口之间存在重叠。应用程序本身的窗口将被捕获,并创建积极的反馈
我已经在KDE的设置中禁用了3D合成,但问题仍然存在。以上示例是在禁用所有合成效果的情况下进行的
当应用程序窗口和活动窗口重叠时,为什么此实现不能正常工作?我怀疑这是由图形系统(Qt工具箱、窗口管理器、X等)之间的某些形式的不必要交互引起的问题,但我不确定
甚至有可能解决这个问题吗?(注意:我知道我可以在截图前hide()
再次截图show()
,但这并不能真正解决这个问题,即使存在重叠也要截图。)
正如@eyllanesc所指出的,似乎不可能在Qt中实现这一点,至少不可能在
QScreen::grabWindow
中实现,因为grabWindow()
实际上并没有捕获窗口本身,而只是捕获窗口所占据的区域The documentation包含以下警告结论是在纯Qt中不可能做到这一点。只有编写一个低级的X程序才能实现这样的功能。由于该问题要求“在Qt中”提供解决方案,因此任何可能涉及更深层次、低级X解决方案的答案都超出了范围。此问题可以标记为已解决
这里要学习的课程是:在使用函数或方法之前,始终检查文档
更新:我通过Xlib直接从X读取窗口,成功地解决了这个问题。有点讽刺的是,我的解决方案使用GTK抓取窗口并将其结果发送给Qt。。。无论如何,如果不想使用GTK,可以直接用Xlib编写相同的程序,但我使用了GTK,因为GDK中与Xlib相关的函数非常方便地演示了基本概念
为了获得屏幕截图,我们首先将窗口ID转换为适合在GDK中使用的
GdkWindow
,然后调用Gdk.pixbuf_get_from_window()
获取窗口并将其存储在gdk_pixbuf
中。最后,我们调用save_to_bufferv()
将原始pixbuf转换为合适的图像格式,并将其存储在缓冲区中。此时,缓冲区中的图像适合用于任何程序,包括Qt该文档包含以下警告:
它也有一些关于合成的评论
因此,基本上,只有使用合成窗口管理器才能在X11下抓取部分遮挡的窗口(在Wayland中不可能!)。我在没有合成的情况下测试了它,发现当合成被禁用时窗口会变黑。但当合成被启用时,它似乎可以毫无问题地工作。它可能适用于您的应用程序,也可能不适用于您的应用程序。但是我认为如果你使用X11下的合成,它可能会起作用
现在它完美地捕获了一个活动窗口,即使上面有重叠的窗口
相关问题 更多 >
编程相关推荐