Как указывает @ eyllanes c, кажется, что это невозможно сделать в Qt, по крайней мере, не с QScreen::grabWindow
, потому что grabWindow()
на самом деле не захватывает само окно, а просто площадь, занимаемая окном. Документация содержит следующее предупреждение.
Функция grabWindow () захватывает пиксели с экрана, а не из окна, т. Е. Если над ним частично или полностью находится другое окно вы захватываете, вы также получаете пиксели из вышележащего окна. Курсор мыши, как правило, не захватывается.
Вывод состоит в том, что это невозможно сделать в чистом Qt. Реализовать такую функциональность можно только написав низкоуровневую X-программу. Поскольку вопрос требует решения «в Qt», любой ответ, который потенциально предполагает более глубокие, низкоуровневые X-решения, выходит за рамки. Этот вопрос можно пометить как решенный.
Урок, который нужно выучить здесь: Всегда Проверьте документацию перед использованием функции или метода.
Обновление: мне удалось решить проблему, прочитав окно прямо из X через Xlib. По иронии судьбы, мое решение использует GTK, чтобы захватить окно и отправить его результат в Qt ... В любом случае, вы можете написать ту же самую программу для Xlib напрямую, если вы не хотите использовать GTK, но я использовал GTK, так как Xlib-связанный Функции в GDK довольно удобны для демонстрации концепции basi c.
Чтобы получить снимок экрана, мы сначала преобразуем наш идентификатор окна в GdkWindow
, подходящий для использования в GDK, и мы вызываем Gdk.pixbuf_get_from_window()
, чтобы захватить окно и сохранить его в gdk_pixbuf
. Наконец, мы вызываем save_to_bufferv()
, чтобы преобразовать необработанный pixbuf в подходящий формат изображения и сохранить его в буфере. На этом этапе изображение в буфере подходит для использования в любой программе, включая Qt.
. Документация содержит следующее предупреждение:
Если окно не отображается на экране, тогда нет данных изображения в затемненных / закадровых областях для размещения в буфере изображения. Содержимое частей pixbuf, соответствующих области вне экрана, не определено.
Если окно, из которого вы получаете данные, частично скрыто другими windows, то содержимое областей pixbuf, соответствующих затемненному регионы не определены.
Если окно не отображается (как правило, потому что оно минимизировано / свернуто или отсутствует в текущем рабочем пространстве), будет возвращено значение NULL.
Если память не может быть выделена вместо возвращаемого значения будет возвращено NULL.
В нем также есть некоторые замечания о компоновке,
gdk_display_supports_composite
устарело с версии 3.16 и не должно быть используется во вновь создаваемом коде.
Компоновка - устаревшая технология, которая когда-либо работала только на X11.
Таким образом, в принципе, возможно получить только частично скрытое окно под X11 (не возможно в Wayland!), с помощью оконного менеджера композитинга. Я проверил это без компоновки, и обнаружил, что окно затемнено, когда композитинг отключен. Но когда композиция включена, кажется, работает без проблем. Это может или не может работать для вашего приложения. Но я думаю, что если вы используете композитинг под X11, он, вероятно, будет работать.
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.screen = QtWidgets.QApplication.primaryScreen()
self.timer = QtCore.QTimer(self)
self.timer.setInterval(500)
self.timer.timeout.connect(self.timer_handler)
self.timer.start()
@staticmethod
def grab_screenshot():
from gi.repository import Gdk, GdkX11
window_id = int(subprocess.check_output(["xdotool", "getactivewindow"]).decode("ascii"))
display = GdkX11.X11Display.get_default()
window = GdkX11.X11Window.foreign_new_for_display(display, window_id)
x, y, width, height = window.get_geometry()
pb = Gdk.pixbuf_get_from_window(window, 0, 0, width, height)
if pb:
buf = pb.save_to_bufferv("bmp", (), ())
return buf[1]
else:
return
@QtCore.pyqtSlot()
def timer_handler(self):
screenshot = self.grab_screenshot()
self.pixmap = QtGui.QPixmap()
if not self.pixmap:
return
self.pixmap.loadFromData(screenshot)
self.label.setPixmap(self.pixmap)
self.label.setFixedSize(self.pixmap.size())
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = ScreenCapture()
window.show()
app.exec()
Теперь он отлично фиксирует активное окно, даже если поверх него перекрывается windows.
![Now it captures an active window perfectly, even if there are overlapping windows on top of it.](https://i.stack.imgur.com/o2Pxh.png)