web-dev-qa-db-fra.com

Comment capturer une capture d'écran d'une fenêtre et de toutes les fenêtres par-dessus?

J'utilise des captures d'écran comme tests de régression pour le logiciel d'interface graphique. Avant de déployer chaque nouvelle version, une série de tâches automatiques est exécutée sur l'ancienne et la nouvelle version, des captures d'écran sont générées après chaque commande dans les deux cas, et les résultats sont comparés. La commande d'importation d'ImageMagick fonctionne très bien pour cela.

Récemment, j'ai ajouté des menus clic droit. Malheureusement, import -window 'id' ne capture pas ces menus.

Quels outils de ligne de commande sous Ubuntu peuvent prendre une capture d’écran d’une fenêtre, et de toutes les fenêtres situées au-dessus?

C'est-à-dire quels outils peuvent, au lieu de prendre une capture d'écran d'une fenêtre correspondant à un ID de fenêtre, prendre une capture d'écran de tout l'écran et la tronquer aux limites d'une fenêtre donnée ?

Je n'ai pas pu obtenir ce résultat de manière simple avec aucun des outils énumérés à la page Quelle est la commande du terminal pour prendre une capture d'écran? .

4
Clément

Utilisez import avec l’option -screen, comme

import -screen -window 'id' test.png
4
Florian Diesch

En utilisant shutter et wmctrl, une version modifiée de ce script fait à peu près exactement ce que vous décrivez: il prend une capture d'écran de la zone , un couvre-fenêtre spécifique sur votre écran, peu importe si et comment la fenêtre est (partiellement) en dessous des autres fenêtres.

La marge autour de la fenêtre, à inclure dans la capture d'écran, est arbitraire; mettez-le à zéro si vous voulez.

En pratique

  • J'ai une fenêtre Inkscape sur mon écran, avec id 0x0520000e, partiellement recouverte par quelques fenêtres gedit.
  • Je lance le script avec l'identifiant de la fenêtre et la marge (dans px) autour de la fenêtre sous forme d'arguments:

    python3 <script> 0x0520000e 10 10 10 10 
    

    (où 10 10 10 10 est la marge dans px autour de la fenêtre gauche/droite/haut/bas. Réglez sur 0 pour ne pas avoir de marge dans l'image)

    Le résultat:

    enter image description here

Le scénario

#!/usr/bin/env python3
import subprocess
import time
import sys

"""
On different window managers, the window geometry as output of wmctrl differs slightly.
The "deviation" should compensate these differences. Most likely appropriate (tested) settings:
Unity: 0, Gnome: -36, Xfce (Xubuntu): -26, KDE (Kubuntu): 0
"""
#---
deviation = 0
#---

get = lambda cmd: subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")
time.sleep(0.5)
# targeted window
target = sys.argv[1]; arg = sys.argv[2:]
f_data = [l.split() for l in get("wmctrl -lG").splitlines() if target in l][0][2:6]
xt_data = get("xprop -id "+target).split()
xt_i = xt_data.index("_NET_FRAME_EXTENTS(CARDINAL)")
xt = [int(n.replace(",", "")) for n in xt_data[xt_i+2:xt_i+6]]
# set data for screenshot command
x = str(int(f_data[0])-int(arg[0])-xt[0])
y = str(int(f_data[1])-int(arg[2])-xt[2]+deviation)
w = str(int(f_data[2])+int(arg[0])+int(arg[1])+xt[0]+xt[1])
h = str(int(f_data[3])+int(arg[3])+int(arg[2])+xt[2]+xt[3])

command = "shutter -s="+(",").join([x,y,w,h])+" -e"
subprocess.call(["/bin/bash", "-c", command])

Comment utiliser

  • Le script utilise Shutter et wmctrl:

    Sudo apt-get install wmctrl shutter
    
  • Copiez le script ci-dessous dans un fichier vide, enregistrez-le sous le nom custom_screenshot.py.

  • Exécutez-le à l'aide de la commande:

    python3 /path/to/custom_screenshot.py <window_id> <left> <right> <top> <bottom>
    

    où, <left> <right> <top> <bottom> sont les marges que vous souhaitez conserver dans l'image autour de la fenêtre, comme dans cette réponse.

    Exemple de commande:

    python3 /path/to/custom_screenshot.py 0x0520000e 20 20 20 20
    

Explication

  • Dans Shutter, il est possible de prendre une capture d'écran d'une zone définie du bureau.

  • Avec l'identifiant de fenêtre comme argument, le script recherche la position exacte de la fenêtre à l'aide de wmctrl (wmctrl -lG pour être précis), et la sortie de xprop -id <window_id> (dans la ligne _NET_FRAME_EXTENTS(CARDINAL) = 0, 0, 28, 0 par exemple).

  • Ensuite, une capture d'écran est prise à partir de la zone trouvée, avec une marge arbitraire.

Remarque

Le script ne remplace pas les captures d'écran existantes. Les nouvelles captures d'écran sont nommées:

outputfile_1.png
outputfile_2.png
outputfile_3.png

etc...


MODIFIER

Comme vous avez mentionné dans un commentaire que la vitesse est un problème:

Basé sur ce script , si nous faisons exactement la même astuce, mais que nous utilisons Scrot au lieu de Shutter, nous pouvons ignorer le sleep 0.5 et le rendre beaucoup plus rapide:

Le scénario

#!/usr/bin/env python3
import subprocess
import sys
import os

"""
On different window managers, the window geometry as output of wmctrl differs slightly.
The "deviation" should compensate these differences. Most likely appropriate (tested) settings:
Unity: 0, Gnome: -36, Xfce (Xubuntu): -26, KDE (Kubuntu): 0
"""
#---
deviation = 0
#---

get = lambda cmd: subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")
# targeted window
target = sys.argv[1]; arg = sys.argv[2:]
f_data = [l.split() for l in get("wmctrl -lG").splitlines() if target in l][0][2:6]
xt_data = get("xprop -id "+target).split()
xt_i = xt_data.index("_NET_FRAME_EXTENTS(CARDINAL)")
xt = [int(n.replace(",", "")) for n in xt_data[xt_i+2:xt_i+6]]
# set data for screenshot command
x = str(int(f_data[0])-int(arg[0])-xt[0])
y = str(int(f_data[1])-int(arg[2])-xt[2]+deviation)
w = str(int(f_data[2])+int(arg[0])+int(arg[1])+xt[0]+xt[1])
h = str(int(f_data[3])+int(arg[3])+int(arg[2])+xt[2]+xt[3])

# setting default directories / filenames
home = os.environ["HOME"]
temp = home+"/"+".scrot_images"
img_in = temp+"/in.png"
# if you prefer, you can change the two line below:
output_directory = home+"/"+"scrot_images" # output directory
filename = "outputfile"                    # filename
# creating needed directories
for dr in [temp, output_directory]:
    if not os.path.exists(dr):
        os.mkdir(dr)
# creating filename (-number) to prevent overwriting previous shots
n = 1
while True:
    img_out = output_directory+"/"+filename+"_"+str(n)+".png"
    if os.path.exists(img_out):
        n = n+1
    else:
        break
# Take screnshot, crop image
subprocess.call(["scrot", img_in])
subprocess.Popen(["convert", img_in, "-crop", w+"x"+h+"+"+x+"+"+y, "+repage", img_out])

Utiliser

Utilisez-le exactement comme le premier script, uniquement:

  • Ce script nécessite scrot, imagemagick et wmctrl

    Sudo apt-get install imagemagick wmctrl scrot
    
  • les images seront stockées dans ~/scrot_images

Explication

Alors que le premier script utilise l'option de ligne de commande Shutter pour créer une section définie du bureau, Scrot ne prend pas cela en charge. Il ne prend qu'une capture d'écran de de tout l'écran .

Nous pouvons toutefois combiner l'option de imagemagick pour créer une image, avec la méthode permettant de trouver les coordonnées exactes de la fenêtre que nous avons utilisées dans le premier script et de rogner l'image en conséquence.
Étant donné que Scrot est extrêmement léger et rapide, même en combinaison avec l'action de rognage de imagemagick, nous disposons d'un moyen assez rapide de créer des captures d'écran de la surface d'une fenêtre.

Pas encore assez rapide?

Je ne sais pas si cela est nécessaire, mais avec un peu de réécriture (voir le script ci-dessous), il serait possible de faire une série de plans encore plus rapidement en tirant d’abord toute la série, , puis (après) faire le recadrage. En supposant que la fenêtre reste dans sa position, cela économiserait une quantité considérable de temps:

  • Tir uniquement avec Scrot (pas de recadrage):

    real    0m0.263s
    user    0m0.205s
    sys     0m0.037s
    
  • Tir, y compris recadrage:

    real    0m0.363s
    user    0m0.293s
    sys     0m0.040s
    

Tir en série

Enfin, à titre d'exemple pour faire une série de captures d'écran, le script ci-dessous, comme suggéré dans l'EDIT.
Celui-ci commence à prendre une photo de toutes les images, , puis recadre toutes les images créées en même temps.

Utilisez le script comme le second, mais avec un argument supplémentaire : le nombre de pousses dans une ligne, par exemple:

python3 /path/to/custom_screenshot.py 0x0520000e 0 0 0 0 20

faire 20 captures d'écran de la fenêtre 0x0520000e à la suite (cela pourrait être des centaines), pas de marge autour de la fenêtre.

Le scénario

#!/usr/bin/env python3
import subprocess
import sys
import os

"""
On different window managers, the window geometry as output of wmctrl differs slightly.
The "deviation" should compensate these differences. Most likely appropriate (tested) settings:
Unity: 0, Gnome: -36, Xfce (Xubuntu): -26, KDE (Kubuntu): 0
"""
#---
deviation = 0
#---

get = lambda cmd: subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")
# targeted window
target = sys.argv[1]; arg = sys.argv[2:]
f_data = [l.split() for l in get("wmctrl -lG").splitlines() if target in l][0][2:6]
xt_data = get("xprop -id "+target).split()
xt_i = xt_data.index("_NET_FRAME_EXTENTS(CARDINAL)")
xt = [int(n.replace(",", "")) for n in xt_data[xt_i+2:xt_i+6]]
# set data for screenshot command
x = str(int(f_data[0])-int(arg[0])-xt[0])
y = str(int(f_data[1])-int(arg[2])-xt[2]+deviation)
w = str(int(f_data[2])+int(arg[0])+int(arg[1])+xt[0]+xt[1])
h = str(int(f_data[3])+int(arg[3])+int(arg[2])+xt[2]+xt[3])
# setting default directories / filenames
home = os.environ["HOME"]
temp = home+"/"+".scrot_images"
# if you prefer, you can change the two line below:
output_directory = home+"/"+"scrot_images" # output directory
filename = "outputfile"                    # filename
# creating needed directories
for dr in [temp, output_directory]:
    if not os.path.exists(dr):
        os.mkdir(dr)
# do the shooting
t = 0; l = []; shots = int(sys.argv[6])
while t < shots:
    img_temp = temp+"/"+str(t)+"in.png"
    l.append(img_temp)
    # reading arguments,arranging commands to perform
    subprocess.call(["scrot", img_temp])
    t += 1
# do the cropping on all images in a row
for img in l:
    n = 1
    while True:
        img_out = output_directory+"/"+filename+"_"+str(n)+".png"
        if os.path.exists(img_out):
            n = n+1
        else:
            break
    subprocess.call(["convert", img , "-crop", w+"x"+h+"+"+x+"+"+y, "+repage", img_out])
6
Jacob Vlijm