Qt 等で開発された X11 アプリケーションを利用するためには X サーバのクライアントが必要になります。X サーバは仮想的に Xvfb コマンドで作成することができます。X サーバのクライアントには fluxbox
などの X11 ウィンドウマネージャ、ssh -X
や x11vnc
、その他 Qt 等で開発された X11 アプリケーションがあります。
ここでは X サーバのクライアントを Xlib で作成して X11 アプリケーションを自動操作してみます。Python の Xlib 実装は python-xlib です。
pip 等でインストールできます。
sudo pip install python-xlib
仮想ディスプレイ DISPLAY=:99
を作成します。
Xvfb :99 -screen 0 1024x768x24 -listen tcp -ac
GUI アプリケーションを接続します。
sudo apt install mesa-utils
DISPLAY=localhost:99 glxgears
以下のようにしてスクリーンショットを取得できます。
DISPLAY=localhost:99 ipython
RAW 画像は pillow(PIL) の Image.frombytes を利用して NumPy の ndarray に変換すると簡単です。
from Xlib.display import Display
from Xlib.X import ZPixmap
from PIL import Image
from numpy import asarray
# DISPLAY=:99 にはウィンドウマネージャが存在しないため root をキャプチャ対象とします。
display = Display()
screen = display.screen()
window = screen.root
# RAW 画像を取得して変換します。
rawimage = window.get_image(0, 0, 1024, 768, ZPixmap, 0xFFFFFFFF).data
image = Image.frombytes('RGB', (1024, 768), rawimage, 'raw', 'RGBX')
# ファイルに保存する場合は以下のようにします。
with open('./sample.png', 'w') as f:
Image.fromarray(asarray(image)).save(f, 'PNG')
仮想ディスプレイ DISPLAY=:99
を作成します。
Xvfb :99 -screen 0 1024x768x24 -listen tcp -ac
GUI アプリケーションを接続します。
sudo apt install x11-apps
DISPLAY=localhost:99 xeyes
仮想ディスプレイに VNC 接続して動作確認できるようにします。
x11vnc -display :99 -listen 0.0.0.0 -forever -xkb -shared -nopw
DISPLAY=:0 gvncviewer localhost::5900
以下のようにして左上の (0, 0)
をマウスで左クリックできます。
DISPLAY=localhost:99 ipython
fake_input を利用します。番号の 1 は左クリックです。後に利用する xev
でもマウスボタンの番号を確認できます。
from Xlib.display import Display
from Xlib.X import MotionNotify
from Xlib.X import ButtonPress
from Xlib.X import ButtonRelease
from Xlib.ext.xtest import fake_input
display = Display()
fake_input(display, MotionNotify, x=0, y=0)
fake_input(display, ButtonPress, 1)
display.sync()
同様にキーボードの操作を表現するためには以下のようにします。
DISPLAY=localhost:99 xev
DISPLAY=localhost:99 ipython
from Xlib.display import Display
from Xlib.X import KeyPress
from Xlib.X import KeyRelease
from Xlib.ext.xtest import fake_input
display = Display()
fake_input(display, KeyPress, 38) # 'a'
display.sync()