3

2 台のモニターを使用するように VirtualBox をセットアップしました。2台目のモニターでウィンドウのスクリーンショットを撮ってみました:

import ImageGrab
im = ImageGrab.grab(windowrect)
im.save("img.png")

windowrectウィンドウの正しい rect であることを確認しました。この場合は(1616, 2, 2594, 732). ただし、img.pngこれは単なる大きなブラック ボックスです。2番目の仮想モニターのスクリーンショットを撮ることができるように、画面グラビングとVirtualBoxの間のこの相互作用を修正する方法はありますか?

4

2 に答える 2

2

私はこの同じ問題に直面しました。推測する必要がある場合ImageGrabは、仮想画面全体を提供するSM_VIRTUALSCREENではなく、プライマリモニターのみの座標を提供するSM_SCREENフラグから座標を取得していると思います(ソースを調べていませんただし、単なる推測です)。

とはいえ、Windows API と直接対話し、そのビットマップ出力をより使いやすい PIL オブジェクトに変換することで、これを簡単に回避できます。

いくつかのコード:

def _get_screen_buffer(self, bounds=None):
                # Grabs a DC to the entire virtual screen, but only copies to 
                # the bitmap the the rect defined by the user. 

                SM_XVIRTUALSCREEN = 76  # coordinates for the left side of the virtual screen. 
                SM_YVIRTUALSCREEN = 77  # coordinates for the right side of the virtual screen.  
                SM_CXVIRTUALSCREEN = 78 # width of the virtual screen
                SM_CYVIRTUALSCREEN = 79 # height of the virtual screen

                hDesktopWnd = windll.user32.GetDesktopWindow() #Entire virtual Screen

                left = windll.user32.GetSystemMetrics(SM_XVIRTUALSCREEN)
                top = windll.user32.GetSystemMetrics(SM_YVIRTUALSCREEN)
                width = windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN)
                height = windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)

                if bounds:
                        left, top, right, bottom = bounds
                        width = right - left 
                        height = bottom - top
                
                hDesktopDC = windll.user32.GetWindowDC(hDesktopWnd)
                if not hDesktopDC: print 'GetDC Failed'; sys.exit()
                
                hCaptureDC = windll.gdi32.CreateCompatibleDC(hDesktopDC)
                if not hCaptureDC: print 'CreateCompatibleBitmap Failed'; sys.exit()

                hCaptureBitmap = windll.gdi32.CreateCompatibleBitmap(hDesktopDC, width, height)
                if not hCaptureBitmap: print 'CreateCompatibleBitmap Failed'; sys.exit()
                
                windll.gdi32.SelectObject(hCaptureDC, hCaptureBitmap)

                SRCCOPY = 0x00CC0020
                windll.gdi32.BitBlt(
                        hCaptureDC, 
                        0, 0, 
                        width, height, 
                        hDesktopDC, 
                        left, top, 
                        0x00CC0020
                )
                return hCaptureBitmap

        def _make_image_from_buffer(self, hCaptureBitmap):
                import Image
                bmp_info = BITMAPINFO()
                bmp_header = BITMAPFILEHEADER()
                hdc = windll.user32.GetDC(None)

                bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)

                DIB_RGB_COLORS = 0
                windll.gdi32.GetDIBits(hdc, 
                        hCaptureBitmap, 
                        0,0, 
                        None, byref(bmp_info), 
                        DIB_RGB_COLORS
                )

                bmp_info.bmiHeader.biSizeImage = bmp_info.bmiHeader.biWidth *abs(bmp_info.bmiHeader.biHeight) * (bmp_info.bmiHeader.biBitCount+7)/8;
                size = (bmp_info.bmiHeader.biWidth, bmp_info.bmiHeader.biHeight )
                print size
                pBuf = (c_char * bmp_info.bmiHeader.biSizeImage)()

                windll.gdi32.GetBitmapBits(hCaptureBitmap, bmp_info.bmiHeader.biSizeImage, pBuf)

                return Image.frombuffer('RGB', size, pBuf, 'raw', 'BGRX', 0, 1)

最初の関数は画面のビットマップを取得し、2 番目の関数はそれを PIL オブジェクトに変換します。

他のモニターの座標を自分で把握したくない場合は、特定のモニターなどを対象とする機能を備えたPyRobotという小さなモジュールを用意しています。そしてそれは純粋なPythonなので、PyWin32をインストールする必要はありません:)

于 2013-11-09T21:03:03.597 に答える
0

Audionautics / クリスキール!

私はあなたの投稿とあなたのライブラリのいくつかを取り、それを python 3 用に書き直しました! 大丈夫だと思います。PILLOW (PIL) と python 3.4 エンジンを使用して動作します。

以下を含むファイル duelMonitory.py を呼び出しました

# https://github.com/chriskiehl/pyrobot
# started from Audionautics code on http://stackoverflow.com/questions/3585293/pil-imagegrab-fails-on-2nd-virtual-monitor-of-virtualbox
# updated for PILLOW and Python 3 by Alan Baines (Kizrak)


from PIL import *
#from ctypes import windll, Structure, byref, c_uint
#import ctypes

import ctypes
from ctypes import *
from ctypes.wintypes import *

def get_screen_buffer(bounds=None):
    # Grabs a DC to the entire virtual screen, but only copies to 
    # the bitmap the the rect defined by the user. 

    SM_XVIRTUALSCREEN = 76  # coordinates for the left side of the virtual screen. 
    SM_YVIRTUALSCREEN = 77  # coordinates for the right side of the virtual screen.  
    SM_CXVIRTUALSCREEN = 78 # width of the virtual screen
    SM_CYVIRTUALSCREEN = 79 # height of the virtual screen

    hDesktopWnd = windll.user32.GetDesktopWindow() #Entire virtual Screen

    left = windll.user32.GetSystemMetrics(SM_XVIRTUALSCREEN)
    top = windll.user32.GetSystemMetrics(SM_YVIRTUALSCREEN)
    width = windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN)
    height = windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)

    if bounds:
        left, top, right, bottom = bounds
        width = right - left 
        height = bottom - top

    hDesktopDC = windll.user32.GetWindowDC(hDesktopWnd)
    if not hDesktopDC:
        print ('GetDC Failed')
        sys.exit()

    hCaptureDC = windll.gdi32.CreateCompatibleDC(hDesktopDC)
    if not hCaptureDC:
        print ('CreateCompatibleBitmap Failed')
        sys.exit()

    hCaptureBitmap = windll.gdi32.CreateCompatibleBitmap(hDesktopDC, width, height)
    if not hCaptureBitmap:
        print ('CreateCompatibleBitmap Failed')
        sys.exit()

    windll.gdi32.SelectObject(hCaptureDC, hCaptureBitmap)

    SRCCOPY = 0x00CC0020
    windll.gdi32.BitBlt(
        hCaptureDC, 
        0, 0, 
        width, height, 
        hDesktopDC, 
        left, top, 
        0x00CC0020
    )
    return hCaptureBitmap

def make_image_from_buffer(hCaptureBitmap):
    from PIL import Image
    bmp_info = BITMAPINFO()
    bmp_header = BITMAPFILEHEADER()
    hdc = windll.user32.GetDC(None)

    bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)

    DIB_RGB_COLORS = 0
    windll.gdi32.GetDIBits(hdc, 
        hCaptureBitmap, 
        0,0, 
        None, byref(bmp_info), 
        DIB_RGB_COLORS
    )

    bmp_info.bmiHeader.biSizeImage = int( bmp_info.bmiHeader.biWidth *abs(bmp_info.bmiHeader.biHeight) * (bmp_info.bmiHeader.biBitCount+7)/8 );
    size = (bmp_info.bmiHeader.biWidth, bmp_info.bmiHeader.biHeight )
    print (size)
    pBuf = (c_char * bmp_info.bmiHeader.biSizeImage)()

    windll.gdi32.GetBitmapBits(hCaptureBitmap, bmp_info.bmiHeader.biSizeImage, pBuf)

    return Image.frombuffer('RGB', size, pBuf, 'raw', 'BGRX', 0, 1)


class BITMAPFILEHEADER(ctypes.Structure):
    _fields_ = [
        ('bfType', ctypes.c_short),
        ('bfSize', ctypes.c_uint32),
        ('bfReserved1', ctypes.c_short),
        ('bfReserved2', ctypes.c_short),
        ('bfOffBits', ctypes.c_uint32)
    ]


class BITMAPINFOHEADER(ctypes.Structure):
    _fields_ = [
        ('biSize', ctypes.c_uint32),
        ('biWidth', ctypes.c_int),
        ('biHeight', ctypes.c_int),
        ('biPlanes', ctypes.c_short),
        ('biBitCount', ctypes.c_short),
        ('biCompression', ctypes.c_uint32),
        ('biSizeImage', ctypes.c_uint32),
        ('biXPelsPerMeter', ctypes.c_long),
        ('biYPelsPerMeter', ctypes.c_long),
        ('biClrUsed', ctypes.c_uint32),
        ('biClrImportant', ctypes.c_uint32)
    ]


class BITMAPINFO(ctypes.Structure):
    _fields_ = [
        ('bmiHeader', BITMAPINFOHEADER),
        ('bmiColors', ctypes.c_ulong * 3)
    ]

そして、私のテストコードは次を含む duelMonitor.test.py でした:

from PIL import *

from duelMonitor import *

hCaptureBitmap = get_screen_buffer()

pimage = make_image_from_buffer(hCaptureBitmap)

pimage.save("Hello.png","PNG")

基本的には、Audionautics の投稿と、https://github.com/chriskiehl/pyrobot Web ページからの彼のライブラリと、Python 3 (および PILLOW) への変換を組み合わせただけです。

楽しむ!

(追伸。コメントとして投稿したかったのですが、ポイントが足りません/sadface)

于 2014-06-04T13:33:15.360 に答える