diff --git a/Orchestrator/orchestratorMain.py b/Orchestrator/orchestratorMain.py index 043e82ea..355972c0 100644 --- a/Orchestrator/orchestratorMain.py +++ b/Orchestrator/orchestratorMain.py @@ -27,7 +27,11 @@ lDaemonConfigurationObject=Settings.mDict #Единый глобальный словарь (За основу взять из Settings.py) global mGlobalDict -mGlobalDict = Settings.Dict +mGlobalDict = Settings.mDict +orchestratorProcessor.mGlobalDict = mGlobalDict +orchestratorTimer.mGlobalDict = mGlobalDict +orchestratorServer.mGlobalDict = mGlobalDict +orchestratorServer.orchestratorProcessor.mGlobalDict = mGlobalDict #Инициализация настроечных параметров lDaemonLoopSeconds=lDaemonConfigurationObject["Scheduler"]["ActivityTimeCheckLoopSeconds"] @@ -38,6 +42,7 @@ lDaemonStartDateTime=datetime.datetime.now() lGlobalDict["JSONConfigurationDict"]=lDaemonConfigurationObject lGlobalDict["ActivityLogSchedule"]=lDaemonActivityLogDict lGlobalDict["ActivityLogScheduleList"]=[] +lGlobalDict["GlobalDict"]=mGlobalDict orchestratorServer.mActivityLogDict = lDaemonActivityLogDict @@ -97,5 +102,4 @@ while True: #Запуск циклической процедуры orchestratorTimer.activityLoopStart(lItem["ActivityIntervalSeconds"],lActivityTimeEndDateTime, lItem["Activity"]) #Уснуть до следующего прогона - time.sleep(lDaemonLoopSeconds) - + time.sleep(lDaemonLoopSeconds) \ No newline at end of file diff --git a/Orchestrator/orchestratorProcessor.py b/Orchestrator/orchestratorProcessor.py index 74912ca4..f1eb8034 100644 --- a/Orchestrator/orchestratorProcessor.py +++ b/Orchestrator/orchestratorProcessor.py @@ -82,7 +82,7 @@ def Activity(inActivity): #Обработка команды CMDStart ########################################################### if lItem["Type"]=="CMDStart": - lCMDCode="cmd /c "+lItem["code"] + lCMDCode="cmd /c "+lItem["Command"] subprocess.Popen(lCMDCode) lResultCMDRun=1#os.system(lCMDCode) lItem["Result"] = str(lResultCMDRun) @@ -109,7 +109,7 @@ def Activity(inActivity): ########################################################### #Обработка команды GlobalDictKeyListValueGet ########################################################### - if lItem["type"]=="GlobalDictKeyListValueGet": + if lItem["Type"]=="GlobalDictKeyListValueGet": for lItem2 in lItem["KeyList"][:-1]: #Check if key - value exists if lItem2 in mGlobalDict: @@ -181,6 +181,7 @@ def ActivityListOrDict(inActivityListOrDict): #Check arg type (list or dict) if type(inActivityListOrDict)==list: #List activity + lResult=[] for lItem in inActivityListOrDict: lResult.append(Activity(lItem)) return lResult diff --git a/Orchestrator/orchestratorServer.py b/Orchestrator/orchestratorServer.py index abafd4a8..d2cecba1 100644 --- a/Orchestrator/orchestratorServer.py +++ b/Orchestrator/orchestratorServer.py @@ -12,12 +12,19 @@ import orchestratorProcessor import importlib import pdb import imp +from desktopmagic.screengrab_win32 import ( + getDisplayRects, saveScreenToBmp, saveRectToBmp, getScreenAsImage, + getRectAsImage, getDisplaysAsImages) +global mGlobalDict def SaveScreenshot(inFilePath): # grab fullscreen - lScreenshot = ImageGrab.grab() + # Save the entire virtual screen as a PNG + lScreenshot = getScreenAsImage() + lScreenshot.save('screenshot.png', format='png') + #lScreenshot = ScreenshotSecondScreen.grab_screen() # save image file - lScreenshot.save('screenshot.png') + #lScreenshot.save('screenshot.png') #Глобальные переменные global mActivityLogDict @@ -31,7 +38,7 @@ class RobotDaemonServer(Thread): global mJSONConfigurationDict mJSONConfigurationDict=inGlobalDict["JSONConfigurationDict"] #Перенос переменной в orchestratorProcessor - orchestratorProcessor.mGlobalDict=inGlobalDict + orchestratorProcessor.mGlobalDict=inGlobalDict["GlobalDict"] #Init other functions #TODO do module init once when run #lRenderFunctionsRobotList=mJSONConfigurationDict["ControlPanelDict"]["RobotList"] diff --git a/Orchestrator/orchestratorTimer.py b/Orchestrator/orchestratorTimer.py index f42fc01f..77ec002d 100644 --- a/Orchestrator/orchestratorTimer.py +++ b/Orchestrator/orchestratorTimer.py @@ -4,6 +4,9 @@ import subprocess import importlib import logging import orchestratorProcessor + +global mGlobalDict + class RepeatedTimer(object): def __init__(self, interval, function, *args, **kwargs): self._timer = None diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt new file mode 100644 index 00000000..2fd4fa2d --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt @@ -0,0 +1,21 @@ +Desktopmagic license: + +Copyright (c) 2011 Ivan Kozik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA new file mode 100644 index 00000000..86cf8104 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA @@ -0,0 +1,21 @@ +Metadata-Version: 2.1 +Name: Desktopmagic +Version: 14.3.11 +Summary: Robust multi-monitor screenshot grabber (Windows-only right now) +Home-page: https://github.com/ludios/Desktopmagic +Author: Ivan Kozik +Author-email: ivan@ludios.org +License: UNKNOWN +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Win32 (MS Windows) +Classifier: Operating System :: Microsoft :: Windows +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture + +UNKNOWN + + diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD new file mode 100644 index 00000000..b54f12c1 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD @@ -0,0 +1,23 @@ +../../Scripts/screengrab_torture_test,sha256=M0lDv6izfH8QGmYx6xFoeZVTv_Jx9nLVHh0Ci0YkjPc,80 +Desktopmagic-14.3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Desktopmagic-14.3.11.dist-info/LICENSE.txt,sha256=81jpSYGoAoxvlRjLO2NTfL0AHHyDJAMMHSCRURRATKo,1077 +Desktopmagic-14.3.11.dist-info/METADATA,sha256=nt1JjqpQb_fvEJac3LbQY1AONRJEVtX3gs6zM2dT4XI,696 +Desktopmagic-14.3.11.dist-info/RECORD,, +Desktopmagic-14.3.11.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98 +Desktopmagic-14.3.11.dist-info/top_level.txt,sha256=Y3lWkebSjPOAXP7mzfuDAlkutR0_QwhHEENcXjARMOI,13 +desktopmagic/__init__.py,sha256=DO7UEI9j-9pPANCC0gBBxPxTTRTd11-vYhlURkvGqFM,24 +desktopmagic/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/__pycache__/screengrab_win32.cpython-37.pyc,, +desktopmagic/screengrab_win32.py,sha256=-lu2xdSdAWnWFDJMjxfpIcRiqc78TRGC1-XrFFhery4,15899 +desktopmagic/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +desktopmagic/scripts/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_torture_test.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_watch_display_rect.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_watch_virtual_screen_rect.cpython-37.pyc,, +desktopmagic/scripts/screengrab_torture_test.py,sha256=-MpUliKrDSZdx_0gutvK7dslrRT7orbvhfbjy-LqIug,1209 +desktopmagic/scripts/screengrab_watch_display_rect.py,sha256=AhFxIHaCUnL755ds4RoaxhNknbfdzWnLz_3n-fmjhF4,682 +desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py,sha256=yDhmeajPPnV8a2WVvJoVnv6l8DUQpAMubOZfUPwQCaU,697 +desktopmagic/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +desktopmagic/test/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/test/__pycache__/test_screengrab_win32.cpython-37.pyc,, +desktopmagic/test/test_screengrab_win32.py,sha256=vWdtKxFD1CvudkuTeSgoiZwiqTNfgcMW9eq7KjMddjg,3652 diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL new file mode 100644 index 00000000..03075056 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.1) +Root-Is-Purelib: true +Tag: cp37-none-any + diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt new file mode 100644 index 00000000..d96b1fcb --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt @@ -0,0 +1 @@ +desktopmagic diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/__init__.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/__init__.py new file mode 100644 index 00000000..46091a19 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/__init__.py @@ -0,0 +1 @@ +__version__ = '14.3.11' diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/screengrab_win32.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/screengrab_win32.py new file mode 100644 index 00000000..bd670c08 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/screengrab_win32.py @@ -0,0 +1,494 @@ +""" +Robust functions for grabbing and saving screenshots on Windows. +""" + +from __future__ import print_function + +import ctypes +import win32gui +import win32ui +import win32con +import win32api + +try: + long +except NameError: + # Python 3 + long = int + + +def checkRect(rect): + """ + Check C{rect} for validity. + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + """ + try: + left, top, right, bottom = rect + except ValueError: + raise ValueError("%r is not a valid rect; must contain 4 ints" % (rect,)) + if not all(isinstance(x, (int, long)) for x in rect): + raise ValueError("%r is not a valid rect; must contain 4 ints" % (rect,)) + width = right - left + height = bottom - top + if width <= 0 or height <= 0: + raise ValueError("%r is not a valid rect; width and height must not be " + "zero or negative" % (rect,)) + + +class RectFailed(Exception): + """ + Could not get information about the virtual screen or a display. + """ + + + +def getVirtualScreenRect(): + """ + Returns a tuple containing ( + the x-coordinate of the upper-left corner of the virtual screen, + the y-coordinate of the upper-left corner of the virtual screen, + the x-coordinate of the lower-right corner of the virtual screen, + the y-coordinate of the lower-right corner of the virtual screen + ) + + Note that both x and y coordinates may be negative; the (0, 0) origin is + determined by the top-left corner of the main display (not necessarily + Display 1). + + Internally, this grabs the geometry from Windows at least twice to avoid + getting bad geometry during changes to the display configuration. + + Raises L{RectFailed} if the geometry cannot be retrieved, though + this failure mode has never been observed. + """ + # Note that one iteration of the loop takes about 2us on a Q6600. + tries = 150 + lastRect = None + for _ in range(tries): + # Get dimensions of the entire virtual screen. Note that left/top may be negative. + # Any of these may return nonsense numbers during display configuration + # changes (not just "desync" between our calls, but numbers that make little + # sense, as if some Windows state doesn't change synchronously.) + # This is why we get them at least twice. + left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) + top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) + width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) + height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) + + right = left + width + bottom = top + height + + rect = (left, top, right, bottom) + try: + checkRect(rect) + except ValueError: + lastRect = None + else: + if rect == lastRect: + return rect + else: + lastRect = rect + + raise RectFailed("Could not get stable rect information after %d tries; " + "last was %r." % (tries, lastRect)) + + +def getDisplayRects(): + """ + Returns a list containing tuples with the coordinates of each display that is + making up the virtual screen. This list is ordered by display number. + + Each tuple in the list is (left, top, right, bottom), specifically ( + the x-coordinate of the upper-left corner of the display, + the y-coordinate of the upper-left corner of the display, + the x-coordinate of the lower-right corner of the display, + the y-coordinate of the lower-right corner of the display + ) + + Note that the (0, 0) origin is the top-left corner of the main display (not + necessarily Display 1). If you have parts of any monitor to the left or + above the top-left corner of the main display, you will see some negative x/y + coordinates. + + Internally, this grabs the geometry from Windows at least twice to avoid + getting bad geometry during changes to the display configuration. + + Raises L{RectFailed} if the geometry cannot be retrieved, though + this failure mode has never been observed. + """ + HANDLE_MONITOR, HDC_MONITOR, SCREEN_RECT = range(3) + + # My experiments show this needs to be no more than 3 (for 4 iterations + # through the loop), but use 150 in case there are pathological systems. + # Note that one iteration of the loop takes about 90us on a Q6600. + tries = 150 + lastRects = None + for _ in range(tries): + try: + monitors = win32api.EnumDisplayMonitors(None, None) + except SystemError: + # If you are changing your monitor configuration while EnumDisplayMonitors + # is enumerating the displays, it may throw SystemError. We just try + # again in this case. + lastRects = None + else: + for m in monitors: + m[HDC_MONITOR].Close() + rects = list(m[SCREEN_RECT] for m in monitors) + try: + for rect in rects: + checkRect(rect) + except ValueError: + lastRects = None + else: + if rects == lastRects: + return rects + else: + lastRects = rects + + raise RectFailed("Could not get stable rect information after %d tries; " + "last was %r." % (tries, lastRects)) + + +class GrabFailed(Exception): + """ + Could not take a screenshot. + """ + + + +def deleteDCAndBitMap(dc, bitmap): + dc.DeleteDC() + handle = bitmap.GetHandle() + # Trying to DeleteObject(0) will throw an exception; it can be 0 in the case + # of an untouched win32ui.CreateBitmap() + if handle != 0: + win32gui.DeleteObject(handle) + +# In case someone rightfully imported the private helper before we made it public +_deleteDCAndBitMap = deleteDCAndBitMap + + +def getDCAndBitMap(saveBmpFilename=None, rect=None): + """ + Returns a (DC, PyCBitmap). On the returned DC ("device context"), you + *must* call aDC.DeleteDC(). On the returned PyCBitmap, you *must* call + win32gui.DeleteObject(aPyCBitmap.GetHandle()). + + If C{saveBmpFilename} is provided, a .bmp will be saved to the specified + filename. This does not require PIL. The .bmp file will have the same + bit-depth as the screen; it is not guaranteed to be 32-bit. If you provide + this argument, you still must clean up the returned objects, as mentioned. + + If C{rect} is not C{None}, instead of capturing the entire virtual screen, + only the region inside the rect will be captured. C{rect} is a tuple of ( + the x-coordinate of the upper-left corner of the virtual screen, + the y-coordinate of the upper-left corner of the virtual screen, + the x-coordinate of the lower-right corner of the virtual screen, + the y-coordinate of the lower-right corner of the virtual screen + ) + + Note that both x and y coordinates may be negative; the (0, 0) origin is + determined by the top-left corner of the main display (not necessarily + Display 1). + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + """ + if rect is None: + try: + rect = getVirtualScreenRect() + except RectFailed as e: + raise GrabFailed("Error during getVirtualScreenRect: " + str(e)) + # rect is already checked + else: + checkRect(rect) + + left, top, right, bottom = rect + width = right - left + height = bottom - top + + hwndDesktop = win32gui.GetDesktopWindow() + + # Retrieve the device context (DC) for the entire virtual screen. + hwndDevice = win32gui.GetWindowDC(hwndDesktop) + ##print("device", hwndDevice) + assert isinstance(hwndDevice, (int, long)), hwndDevice + + mfcDC = win32ui.CreateDCFromHandle(hwndDevice) + try: + saveDC = mfcDC.CreateCompatibleDC() + saveBitMap = win32ui.CreateBitmap() + # Above line is assumed to never raise an exception. + try: + try: + saveBitMap.CreateCompatibleBitmap(mfcDC, width, height) + except (win32ui.error, OverflowError) as e: + raise GrabFailed("Could not CreateCompatibleBitmap(" + "mfcDC, %r, %r) - perhaps too big? Error was: %s" % (width, height, e)) + saveDC.SelectObject(saveBitMap) + try: + saveDC.BitBlt((0, 0), (width, height), mfcDC, (left, top), win32con.SRCCOPY) + except win32ui.error as e: + raise GrabFailed("Error during BitBlt. " + "Possible reasons: locked workstation, no display, " + "or an active UAC elevation screen. Error was: " + str(e)) + if saveBmpFilename is not None: + saveBitMap.SaveBitmapFile(saveDC, saveBmpFilename) + except: + deleteDCAndBitMap(saveDC, saveBitMap) + # Let's just hope the above line doesn't raise an exception + # (or it will mask the previous exception) + raise + finally: + mfcDC.DeleteDC() + + return saveDC, saveBitMap + + +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) + ] + + + +class DIBFailed(Exception): + pass + + + +def getBGR32(dc, bitmap): + """ + Returns a (raw BGR str, (width, height)) for C{dc}, C{bitmap}. + Guaranteed to be 32-bit. Note that the origin of the returned image is + in the bottom-left corner, and the image has 32-bit line padding. + """ + bmpInfo = bitmap.GetInfo() + width, height = bmpInfo['bmWidth'], bmpInfo['bmHeight'] + + bmi = BITMAPINFO() + ctypes.memset(ctypes.byref(bmi), 0x00, ctypes.sizeof(bmi)) + bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) + bmi.bmiHeader.biWidth = width + bmi.bmiHeader.biHeight = height + bmi.bmiHeader.biBitCount = 24 + bmi.bmiHeader.biPlanes = 1 + + bufferLen = height * ((width * 3 + 3) & -4) + pbBits = ctypes.create_string_buffer(bufferLen) + + ret = ctypes.windll.gdi32.GetDIBits( + dc.GetHandleAttrib(), + bitmap.GetHandle(), + 0, + height, + ctypes.byref(pbBits), + ctypes.pointer(bmi), + win32con.DIB_RGB_COLORS) + if ret == 0: + raise DIBFailed("Return code 0 from GetDIBits") + + assert len(pbBits.raw) == bufferLen, len(pbBits.raw) + + return pbBits.raw, (width, height) + + +def _getRectAsImage(rect): + try: + # Pillow or PIL + from PIL import Image + except ImportError: + # some old PIL installations + import Image + + dc, bitmap = getDCAndBitMap(rect=rect) + try: + bmpInfo = bitmap.GetInfo() + # bmpInfo is something like { + # 'bmType': 0, 'bmWidthBytes': 5120, 'bmHeight': 1024, + # 'bmBitsPixel': 32, 'bmPlanes': 1, 'bmWidth': 1280} + ##print(bmpInfo) + size = (bmpInfo['bmWidth'], bmpInfo['bmHeight']) + + if bmpInfo['bmBitsPixel'] == 32: + # Use GetBitmapBits and BGRX if the bpp == 32, because + # it's ~15% faster than the method below. + data = bitmap.GetBitmapBits(True) # asString=True + return Image.frombuffer( + 'RGB', size, data, 'raw', 'BGRX', 0, 1) + else: + # If bpp != 32, we cannot use GetBitmapBits, because it + # does not return a 24/32-bit image when the screen is at + # a lower color depth. + try: + data, size = getBGR32(dc, bitmap) + except DIBFailed as e: + raise GrabFailed("getBGR32 failed. Error was " + str(e)) + # BGR, 32-bit line padding, origo in lower left corner + return Image.frombuffer( + 'RGB', size, data, 'raw', 'BGR', (size[0] * 3 + 3) & -4, -1) + finally: + deleteDCAndBitMap(dc, bitmap) + + +def getScreenAsImage(): + """ + Returns a PIL Image object (mode RGB) of the entire virtual screen. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + return _getRectAsImage(None) + + +def normalizeRects(rects): + """ + Normalize a list of rects (e.g. as returned by L{getDisplayRects()}) + to make all coordinates >= 0. This is useful if you want to do your own + cropping of an entire virtual screen as returned by L{getScreenAsImage()}. + """ + smallestX = min(rect[0] for rect in rects) + smallestY = min(rect[1] for rect in rects) + return list( + (-smallestX + left, + -smallestY + top, + -smallestX + right, + -smallestY + bottom) for left, top, right, bottom in rects + ) + + +def getDisplaysAsImages(): + """ + Returns a list of PIL Image objects (mode RGB), one for each display. + This list is ordered by display number. + + Internally, this captures the entire virtual screen and then crops out each + Image based on display information. This method ensures that all displays + are captured at the same time (or as close to it as Windows permits). + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + try: + rects = getDisplayRects() + except RectFailed as e: + raise GrabFailed("Error during getDisplayRects: " + str(e)) + # im has an origin at (0, 0) in the top-left corner of the virtual screen, + # but our `rect`s have a (0, 0) origin in the top-left corner of the main + # display. So we normalize all coordinates in the rects to be >= 0. + normalizedRects = normalizeRects(rects) + im = getScreenAsImage() + + return list(im.crop(rect) for rect in normalizedRects) + + +def getRectAsImage(rect): + """ + Returns a PIL Image object (mode RGB) of the region inside the rect. + + See the L{getDCAndBitMap} docstring for C{rect} documentation. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + + Raises L{TypeError} if C{rect} is C{None}. + """ + if rect is None: + raise TypeError("Expected a tuple for rect, got None") + return _getRectAsImage(rect) + + +def saveScreenToBmp(bmpFilename): + """ + Save a screenshot of the entire virtual screen to a .bmp file. Does not + require PIL. The .bmp file will have the same bit-depth as the screen; + it is not guaranteed to be 32-bit. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + dc, bitmap = getDCAndBitMap(bmpFilename) + deleteDCAndBitMap(dc, bitmap) + + +def saveRectToBmp(bmpFilename, rect): + """ + Save a screenshot of the region inside the rect to a .bmp file. Does not + require PIL. The .bmp file will have the same bit-depth as the screen; + it is not guaranteed to be 32-bit. + + See the L{getDCAndBitMap} docstring for C{rect} documentation. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + + Raises L{TypeError} if C{rect} is C{None}. + """ + if rect is None: + raise TypeError("Expected a tuple for rect, got None") + dc, bitmap = getDCAndBitMap(bmpFilename, rect) + deleteDCAndBitMap(dc, bitmap) + + +def _demo(): + # Save the entire virtual screen as a BMP (no PIL required) + saveScreenToBmp('screencapture_entire.bmp') + + # Save an arbitrary rectangle of the virtual screen as a BMP (no PIL required) + saveRectToBmp('screencapture_256_256.bmp', rect=(0, 0, 256, 256)) + + # Save the entire virtual screen as a PNG + entireScreen = getScreenAsImage() + entireScreen.save('screencapture_entire.png', format='png') + + # Get bounding rectangles for all displays, in display order + print("Display rects are:", getDisplayRects()) + # -> something like [(0, 0, 1280, 1024), (-1280, 0, 0, 1024), (1280, -176, 3200, 1024)] + + # Capture an arbitrary rectangle of the virtual screen: (left, top, right, bottom) + rect256 = getRectAsImage((0, 0, 256, 256)) + rect256.save('screencapture_256_256.png', format='png') + + # Unsynchronized capture, one display at a time. + # If you need all displays, use getDisplaysAsImages() instead. + for displayNumber, rect in enumerate(getDisplayRects(), 1): + imDisplay = getRectAsImage(rect) + imDisplay.save('screencapture_unsync_display_%d.png' % (displayNumber,), format='png') + + # Synchronized capture, entire virtual screen at once, cropped to one Image per display. + for displayNumber, im in enumerate(getDisplaysAsImages(), 1): + im.save('screencapture_sync_display_%d.png' % (displayNumber,), format='png') + + +if __name__ == '__main__': + _demo() diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/__init__.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py new file mode 100644 index 00000000..f7f92756 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py @@ -0,0 +1,49 @@ +from __future__ import print_function + +import sys + +from desktopmagic.screengrab_win32 import GrabFailed, getScreenAsImage, getDisplaysAsImages, getRectAsImage + +def main(): + print("""\ +This program helps you test whether screengrab_win32 has memory leaks +and other problems. It takes a screenshot repeatedly and discards it. + +Open Task Manager and make sure Physical Memory % is not ballooning. +Memory leaks might not be blamed on the python process itself (which +will show low memory usage). + +Lock the workstation for a few minutes; make sure there are no leaks +and that there are no uncaught exceptions here. + +Repeat above after RDPing into the workstation and minimizing RDP; +this is like disconnecting the monitor. + +Change your color depth settings. Add and remove monitors. RDP +in at 256 colors. +""") + while True: + try: + getScreenAsImage() + print("S", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + try: + getDisplaysAsImages() + print("D", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + try: + getRectAsImage((0, 0, 1, 1)) + print("R", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py new file mode 100644 index 00000000..6730c96d --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py @@ -0,0 +1,33 @@ +from __future__ import print_function + +from desktopmagic.screengrab_win32 import getDisplayRects + +import time + +def main(): + print("""\ +This program constantly polls your display rect information and prints +it when it changes. + +This can be used to make sure getDisplayRects is free from desync bugs +that occur during monitor configuration changes. +""") + lastRects = None + count = 0 + start = time.time() + while True: + if count != 0 and count % 1000 == 0: + end = time.time() + ##print(end - start, "for 1000 calls") + start = time.time() + + rects = getDisplayRects() + if rects != lastRects: + print(rects) + lastRects = rects + + count += 1 + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py new file mode 100644 index 00000000..94e39980 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py @@ -0,0 +1,33 @@ +from __future__ import print_function + +from desktopmagic.screengrab_win32 import getVirtualScreenRect + +import time + +def main(): + print("""\ +This program constantly polls your virtual screen rect information and +prints it when it changes. + +This can be used to make sure getVirtualScreenRect is free from desync +bugs that occur during monitor configuration changes. +""") + lastRect = None + count = 0 + start = time.time() + while True: + if count != 0 and count % 1000 == 0: + end = time.time() + ##print(end - start, "for 1000 calls") + start = time.time() + + rect = getVirtualScreenRect() + if rect != lastRect: + print(rect) + lastRect = rect + + count += 1 + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/test/__init__.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py new file mode 100644 index 00000000..f7a59cee --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py @@ -0,0 +1,118 @@ +from __future__ import print_function + +import os +try: + # Needed for Python 2.6 compatibility + import unittest2 as unittest +except ImportError: + import unittest +import tempfile + +from desktopmagic.screengrab_win32 import getDisplayRects, saveRectToBmp, getRectAsImage, GrabFailed + +try: + # Pillow or PIL + from PIL import Image +except ImportError: + try: + # some old PIL installations + import Image + except ImportError: + Image = None + + +class GetDisplayRectsTest(unittest.TestCase): + """ + Tests for L{getDisplayRects} + """ + def test_getDisplayRectsReturnsList(self): + """ + L{getDisplayRects} returns a list of length >= 1 with a tuple containing 4 integers, + representing the geometry of each display. + """ + regions = getDisplayRects() + ##print("Display rects are:", regions) + self.assertIsInstance(regions, list) + for region in regions: + self.assertIsInstance(region, tuple) + for num in region: + self.assertIsInstance(num, int) + + + def disabled_test_getDisplayRectsDoesNotLeak(self): + """ + Calling L{getDisplayRects} 100,000 times does not leak memory (you'll have to + open taskmgr to make sure.) + + Disabled because Ivan manually confirmed that it does not leak. + """ + print("Open taskmgr.exe to make sure I'm not leaking memory right now.") + for i in xrange(100000): + getDisplayRects() + + + +class RectTests(unittest.TestCase): + def _tryUnlink(self, fname): + try: + os.unlink(fname) + except OSError: + pass + + + def test_workingCase(self): + if not Image: + self.skipTest("No PIL or Pillow") + + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + saveRectToBmp(fname, rect=(0, 0, 200, 100)) + with open(fname, "rb") as f: + im = Image.open(f) + self.assertEqual((200, 100), im.size) + + + def test_invalidRect(self): + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, 100))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 99, 100))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, 99))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, None))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, "100", None))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100.0, 100, 101, 101))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 101, 101.0))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 200, 200, 200))) + self.assertRaises(TypeError, lambda: saveRectToBmp(fname, rect=None)) + self.assertRaises(TypeError, lambda: getRectAsImage(rect=None)) + + + def test_1x1SizeRect(self): + if not Image: + self.skipTest("No PIL or Pillow") + + fname = tempfile.mktemp() + '.bmp' + fnamePng = tempfile.mktemp() + '.png' + self.addCleanup(self._tryUnlink, fname) + self.addCleanup(self._tryUnlink, fnamePng) + saveRectToBmp(fname, rect=(100, 100, 101, 101)) + + with open(fname, "rb") as f: + im = Image.open(f) + self.assertEqual((1, 1), im.size) + + im = getRectAsImage(rect=(100, 100, 101, 101)) + self.assertEqual((1, 1), im.size) + im.save(fnamePng, format='png') + + with open(fnamePng, "rb") as f: + im = Image.open(f) + self.assertEqual((1, 1), im.size) + + + def test_rectTooBig(self): + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + # Note that 26000x26000 is big enough to fail it on my system + self.assertRaises(GrabFailed, lambda: saveRectToBmp(fname, rect=(0, 0, 2600000, 2600000))) + self.assertRaises(GrabFailed, lambda: saveRectToBmp(fname, rect=(0, 0, 2600000, 260000000000000000))) diff --git a/Resources/WPy32-3720/python-3.7.2/Scripts/screengrab_torture_test b/Resources/WPy32-3720/python-3.7.2/Scripts/screengrab_torture_test new file mode 100644 index 00000000..cb9a9179 --- /dev/null +++ b/Resources/WPy32-3720/python-3.7.2/Scripts/screengrab_torture_test @@ -0,0 +1,5 @@ +#!C:\Abs\Archive\scopeSrcUL\OpenRPA\Resources\WPy32-3720\python-3.7.2\python.exe + +from desktopmagic.scripts.screengrab_torture_test import main + +main() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt new file mode 100644 index 00000000..2fd4fa2d --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/LICENSE.txt @@ -0,0 +1,21 @@ +Desktopmagic license: + +Copyright (c) 2011 Ivan Kozik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA new file mode 100644 index 00000000..86cf8104 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/METADATA @@ -0,0 +1,21 @@ +Metadata-Version: 2.1 +Name: Desktopmagic +Version: 14.3.11 +Summary: Robust multi-monitor screenshot grabber (Windows-only right now) +Home-page: https://github.com/ludios/Desktopmagic +Author: Ivan Kozik +Author-email: ivan@ludios.org +License: UNKNOWN +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Win32 (MS Windows) +Classifier: Operating System :: Microsoft :: Windows +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture + +UNKNOWN + + diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD new file mode 100644 index 00000000..b54f12c1 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/RECORD @@ -0,0 +1,23 @@ +../../Scripts/screengrab_torture_test,sha256=M0lDv6izfH8QGmYx6xFoeZVTv_Jx9nLVHh0Ci0YkjPc,80 +Desktopmagic-14.3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Desktopmagic-14.3.11.dist-info/LICENSE.txt,sha256=81jpSYGoAoxvlRjLO2NTfL0AHHyDJAMMHSCRURRATKo,1077 +Desktopmagic-14.3.11.dist-info/METADATA,sha256=nt1JjqpQb_fvEJac3LbQY1AONRJEVtX3gs6zM2dT4XI,696 +Desktopmagic-14.3.11.dist-info/RECORD,, +Desktopmagic-14.3.11.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98 +Desktopmagic-14.3.11.dist-info/top_level.txt,sha256=Y3lWkebSjPOAXP7mzfuDAlkutR0_QwhHEENcXjARMOI,13 +desktopmagic/__init__.py,sha256=DO7UEI9j-9pPANCC0gBBxPxTTRTd11-vYhlURkvGqFM,24 +desktopmagic/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/__pycache__/screengrab_win32.cpython-37.pyc,, +desktopmagic/screengrab_win32.py,sha256=-lu2xdSdAWnWFDJMjxfpIcRiqc78TRGC1-XrFFhery4,15899 +desktopmagic/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +desktopmagic/scripts/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_torture_test.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_watch_display_rect.cpython-37.pyc,, +desktopmagic/scripts/__pycache__/screengrab_watch_virtual_screen_rect.cpython-37.pyc,, +desktopmagic/scripts/screengrab_torture_test.py,sha256=-MpUliKrDSZdx_0gutvK7dslrRT7orbvhfbjy-LqIug,1209 +desktopmagic/scripts/screengrab_watch_display_rect.py,sha256=AhFxIHaCUnL755ds4RoaxhNknbfdzWnLz_3n-fmjhF4,682 +desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py,sha256=yDhmeajPPnV8a2WVvJoVnv6l8DUQpAMubOZfUPwQCaU,697 +desktopmagic/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +desktopmagic/test/__pycache__/__init__.cpython-37.pyc,, +desktopmagic/test/__pycache__/test_screengrab_win32.cpython-37.pyc,, +desktopmagic/test/test_screengrab_win32.py,sha256=vWdtKxFD1CvudkuTeSgoiZwiqTNfgcMW9eq7KjMddjg,3652 diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL new file mode 100644 index 00000000..03075056 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.1) +Root-Is-Purelib: true +Tag: cp37-none-any + diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt new file mode 100644 index 00000000..d96b1fcb --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/Desktopmagic-14.3.11.dist-info/top_level.txt @@ -0,0 +1 @@ +desktopmagic diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/__init__.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/__init__.py new file mode 100644 index 00000000..46091a19 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/__init__.py @@ -0,0 +1 @@ +__version__ = '14.3.11' diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/screengrab_win32.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/screengrab_win32.py new file mode 100644 index 00000000..bd670c08 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/screengrab_win32.py @@ -0,0 +1,494 @@ +""" +Robust functions for grabbing and saving screenshots on Windows. +""" + +from __future__ import print_function + +import ctypes +import win32gui +import win32ui +import win32con +import win32api + +try: + long +except NameError: + # Python 3 + long = int + + +def checkRect(rect): + """ + Check C{rect} for validity. + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + """ + try: + left, top, right, bottom = rect + except ValueError: + raise ValueError("%r is not a valid rect; must contain 4 ints" % (rect,)) + if not all(isinstance(x, (int, long)) for x in rect): + raise ValueError("%r is not a valid rect; must contain 4 ints" % (rect,)) + width = right - left + height = bottom - top + if width <= 0 or height <= 0: + raise ValueError("%r is not a valid rect; width and height must not be " + "zero or negative" % (rect,)) + + +class RectFailed(Exception): + """ + Could not get information about the virtual screen or a display. + """ + + + +def getVirtualScreenRect(): + """ + Returns a tuple containing ( + the x-coordinate of the upper-left corner of the virtual screen, + the y-coordinate of the upper-left corner of the virtual screen, + the x-coordinate of the lower-right corner of the virtual screen, + the y-coordinate of the lower-right corner of the virtual screen + ) + + Note that both x and y coordinates may be negative; the (0, 0) origin is + determined by the top-left corner of the main display (not necessarily + Display 1). + + Internally, this grabs the geometry from Windows at least twice to avoid + getting bad geometry during changes to the display configuration. + + Raises L{RectFailed} if the geometry cannot be retrieved, though + this failure mode has never been observed. + """ + # Note that one iteration of the loop takes about 2us on a Q6600. + tries = 150 + lastRect = None + for _ in range(tries): + # Get dimensions of the entire virtual screen. Note that left/top may be negative. + # Any of these may return nonsense numbers during display configuration + # changes (not just "desync" between our calls, but numbers that make little + # sense, as if some Windows state doesn't change synchronously.) + # This is why we get them at least twice. + left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) + top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) + width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) + height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) + + right = left + width + bottom = top + height + + rect = (left, top, right, bottom) + try: + checkRect(rect) + except ValueError: + lastRect = None + else: + if rect == lastRect: + return rect + else: + lastRect = rect + + raise RectFailed("Could not get stable rect information after %d tries; " + "last was %r." % (tries, lastRect)) + + +def getDisplayRects(): + """ + Returns a list containing tuples with the coordinates of each display that is + making up the virtual screen. This list is ordered by display number. + + Each tuple in the list is (left, top, right, bottom), specifically ( + the x-coordinate of the upper-left corner of the display, + the y-coordinate of the upper-left corner of the display, + the x-coordinate of the lower-right corner of the display, + the y-coordinate of the lower-right corner of the display + ) + + Note that the (0, 0) origin is the top-left corner of the main display (not + necessarily Display 1). If you have parts of any monitor to the left or + above the top-left corner of the main display, you will see some negative x/y + coordinates. + + Internally, this grabs the geometry from Windows at least twice to avoid + getting bad geometry during changes to the display configuration. + + Raises L{RectFailed} if the geometry cannot be retrieved, though + this failure mode has never been observed. + """ + HANDLE_MONITOR, HDC_MONITOR, SCREEN_RECT = range(3) + + # My experiments show this needs to be no more than 3 (for 4 iterations + # through the loop), but use 150 in case there are pathological systems. + # Note that one iteration of the loop takes about 90us on a Q6600. + tries = 150 + lastRects = None + for _ in range(tries): + try: + monitors = win32api.EnumDisplayMonitors(None, None) + except SystemError: + # If you are changing your monitor configuration while EnumDisplayMonitors + # is enumerating the displays, it may throw SystemError. We just try + # again in this case. + lastRects = None + else: + for m in monitors: + m[HDC_MONITOR].Close() + rects = list(m[SCREEN_RECT] for m in monitors) + try: + for rect in rects: + checkRect(rect) + except ValueError: + lastRects = None + else: + if rects == lastRects: + return rects + else: + lastRects = rects + + raise RectFailed("Could not get stable rect information after %d tries; " + "last was %r." % (tries, lastRects)) + + +class GrabFailed(Exception): + """ + Could not take a screenshot. + """ + + + +def deleteDCAndBitMap(dc, bitmap): + dc.DeleteDC() + handle = bitmap.GetHandle() + # Trying to DeleteObject(0) will throw an exception; it can be 0 in the case + # of an untouched win32ui.CreateBitmap() + if handle != 0: + win32gui.DeleteObject(handle) + +# In case someone rightfully imported the private helper before we made it public +_deleteDCAndBitMap = deleteDCAndBitMap + + +def getDCAndBitMap(saveBmpFilename=None, rect=None): + """ + Returns a (DC, PyCBitmap). On the returned DC ("device context"), you + *must* call aDC.DeleteDC(). On the returned PyCBitmap, you *must* call + win32gui.DeleteObject(aPyCBitmap.GetHandle()). + + If C{saveBmpFilename} is provided, a .bmp will be saved to the specified + filename. This does not require PIL. The .bmp file will have the same + bit-depth as the screen; it is not guaranteed to be 32-bit. If you provide + this argument, you still must clean up the returned objects, as mentioned. + + If C{rect} is not C{None}, instead of capturing the entire virtual screen, + only the region inside the rect will be captured. C{rect} is a tuple of ( + the x-coordinate of the upper-left corner of the virtual screen, + the y-coordinate of the upper-left corner of the virtual screen, + the x-coordinate of the lower-right corner of the virtual screen, + the y-coordinate of the lower-right corner of the virtual screen + ) + + Note that both x and y coordinates may be negative; the (0, 0) origin is + determined by the top-left corner of the main display (not necessarily + Display 1). + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + """ + if rect is None: + try: + rect = getVirtualScreenRect() + except RectFailed as e: + raise GrabFailed("Error during getVirtualScreenRect: " + str(e)) + # rect is already checked + else: + checkRect(rect) + + left, top, right, bottom = rect + width = right - left + height = bottom - top + + hwndDesktop = win32gui.GetDesktopWindow() + + # Retrieve the device context (DC) for the entire virtual screen. + hwndDevice = win32gui.GetWindowDC(hwndDesktop) + ##print("device", hwndDevice) + assert isinstance(hwndDevice, (int, long)), hwndDevice + + mfcDC = win32ui.CreateDCFromHandle(hwndDevice) + try: + saveDC = mfcDC.CreateCompatibleDC() + saveBitMap = win32ui.CreateBitmap() + # Above line is assumed to never raise an exception. + try: + try: + saveBitMap.CreateCompatibleBitmap(mfcDC, width, height) + except (win32ui.error, OverflowError) as e: + raise GrabFailed("Could not CreateCompatibleBitmap(" + "mfcDC, %r, %r) - perhaps too big? Error was: %s" % (width, height, e)) + saveDC.SelectObject(saveBitMap) + try: + saveDC.BitBlt((0, 0), (width, height), mfcDC, (left, top), win32con.SRCCOPY) + except win32ui.error as e: + raise GrabFailed("Error during BitBlt. " + "Possible reasons: locked workstation, no display, " + "or an active UAC elevation screen. Error was: " + str(e)) + if saveBmpFilename is not None: + saveBitMap.SaveBitmapFile(saveDC, saveBmpFilename) + except: + deleteDCAndBitMap(saveDC, saveBitMap) + # Let's just hope the above line doesn't raise an exception + # (or it will mask the previous exception) + raise + finally: + mfcDC.DeleteDC() + + return saveDC, saveBitMap + + +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) + ] + + + +class DIBFailed(Exception): + pass + + + +def getBGR32(dc, bitmap): + """ + Returns a (raw BGR str, (width, height)) for C{dc}, C{bitmap}. + Guaranteed to be 32-bit. Note that the origin of the returned image is + in the bottom-left corner, and the image has 32-bit line padding. + """ + bmpInfo = bitmap.GetInfo() + width, height = bmpInfo['bmWidth'], bmpInfo['bmHeight'] + + bmi = BITMAPINFO() + ctypes.memset(ctypes.byref(bmi), 0x00, ctypes.sizeof(bmi)) + bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) + bmi.bmiHeader.biWidth = width + bmi.bmiHeader.biHeight = height + bmi.bmiHeader.biBitCount = 24 + bmi.bmiHeader.biPlanes = 1 + + bufferLen = height * ((width * 3 + 3) & -4) + pbBits = ctypes.create_string_buffer(bufferLen) + + ret = ctypes.windll.gdi32.GetDIBits( + dc.GetHandleAttrib(), + bitmap.GetHandle(), + 0, + height, + ctypes.byref(pbBits), + ctypes.pointer(bmi), + win32con.DIB_RGB_COLORS) + if ret == 0: + raise DIBFailed("Return code 0 from GetDIBits") + + assert len(pbBits.raw) == bufferLen, len(pbBits.raw) + + return pbBits.raw, (width, height) + + +def _getRectAsImage(rect): + try: + # Pillow or PIL + from PIL import Image + except ImportError: + # some old PIL installations + import Image + + dc, bitmap = getDCAndBitMap(rect=rect) + try: + bmpInfo = bitmap.GetInfo() + # bmpInfo is something like { + # 'bmType': 0, 'bmWidthBytes': 5120, 'bmHeight': 1024, + # 'bmBitsPixel': 32, 'bmPlanes': 1, 'bmWidth': 1280} + ##print(bmpInfo) + size = (bmpInfo['bmWidth'], bmpInfo['bmHeight']) + + if bmpInfo['bmBitsPixel'] == 32: + # Use GetBitmapBits and BGRX if the bpp == 32, because + # it's ~15% faster than the method below. + data = bitmap.GetBitmapBits(True) # asString=True + return Image.frombuffer( + 'RGB', size, data, 'raw', 'BGRX', 0, 1) + else: + # If bpp != 32, we cannot use GetBitmapBits, because it + # does not return a 24/32-bit image when the screen is at + # a lower color depth. + try: + data, size = getBGR32(dc, bitmap) + except DIBFailed as e: + raise GrabFailed("getBGR32 failed. Error was " + str(e)) + # BGR, 32-bit line padding, origo in lower left corner + return Image.frombuffer( + 'RGB', size, data, 'raw', 'BGR', (size[0] * 3 + 3) & -4, -1) + finally: + deleteDCAndBitMap(dc, bitmap) + + +def getScreenAsImage(): + """ + Returns a PIL Image object (mode RGB) of the entire virtual screen. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + return _getRectAsImage(None) + + +def normalizeRects(rects): + """ + Normalize a list of rects (e.g. as returned by L{getDisplayRects()}) + to make all coordinates >= 0. This is useful if you want to do your own + cropping of an entire virtual screen as returned by L{getScreenAsImage()}. + """ + smallestX = min(rect[0] for rect in rects) + smallestY = min(rect[1] for rect in rects) + return list( + (-smallestX + left, + -smallestY + top, + -smallestX + right, + -smallestY + bottom) for left, top, right, bottom in rects + ) + + +def getDisplaysAsImages(): + """ + Returns a list of PIL Image objects (mode RGB), one for each display. + This list is ordered by display number. + + Internally, this captures the entire virtual screen and then crops out each + Image based on display information. This method ensures that all displays + are captured at the same time (or as close to it as Windows permits). + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + try: + rects = getDisplayRects() + except RectFailed as e: + raise GrabFailed("Error during getDisplayRects: " + str(e)) + # im has an origin at (0, 0) in the top-left corner of the virtual screen, + # but our `rect`s have a (0, 0) origin in the top-left corner of the main + # display. So we normalize all coordinates in the rects to be >= 0. + normalizedRects = normalizeRects(rects) + im = getScreenAsImage() + + return list(im.crop(rect) for rect in normalizedRects) + + +def getRectAsImage(rect): + """ + Returns a PIL Image object (mode RGB) of the region inside the rect. + + See the L{getDCAndBitMap} docstring for C{rect} documentation. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + + Raises L{TypeError} if C{rect} is C{None}. + """ + if rect is None: + raise TypeError("Expected a tuple for rect, got None") + return _getRectAsImage(rect) + + +def saveScreenToBmp(bmpFilename): + """ + Save a screenshot of the entire virtual screen to a .bmp file. Does not + require PIL. The .bmp file will have the same bit-depth as the screen; + it is not guaranteed to be 32-bit. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + """ + dc, bitmap = getDCAndBitMap(bmpFilename) + deleteDCAndBitMap(dc, bitmap) + + +def saveRectToBmp(bmpFilename, rect): + """ + Save a screenshot of the region inside the rect to a .bmp file. Does not + require PIL. The .bmp file will have the same bit-depth as the screen; + it is not guaranteed to be 32-bit. + + See the L{getDCAndBitMap} docstring for C{rect} documentation. + + Raises L{GrabFailed} if unable to take a screenshot (e.g. due to locked + workstation, no display, or active UAC elevation screen). + + Raises L{ValueError} if C{rect}'s computed width or height is zero or + negative, or if rect contains nonsense. + + Raises L{TypeError} if C{rect} is C{None}. + """ + if rect is None: + raise TypeError("Expected a tuple for rect, got None") + dc, bitmap = getDCAndBitMap(bmpFilename, rect) + deleteDCAndBitMap(dc, bitmap) + + +def _demo(): + # Save the entire virtual screen as a BMP (no PIL required) + saveScreenToBmp('screencapture_entire.bmp') + + # Save an arbitrary rectangle of the virtual screen as a BMP (no PIL required) + saveRectToBmp('screencapture_256_256.bmp', rect=(0, 0, 256, 256)) + + # Save the entire virtual screen as a PNG + entireScreen = getScreenAsImage() + entireScreen.save('screencapture_entire.png', format='png') + + # Get bounding rectangles for all displays, in display order + print("Display rects are:", getDisplayRects()) + # -> something like [(0, 0, 1280, 1024), (-1280, 0, 0, 1024), (1280, -176, 3200, 1024)] + + # Capture an arbitrary rectangle of the virtual screen: (left, top, right, bottom) + rect256 = getRectAsImage((0, 0, 256, 256)) + rect256.save('screencapture_256_256.png', format='png') + + # Unsynchronized capture, one display at a time. + # If you need all displays, use getDisplaysAsImages() instead. + for displayNumber, rect in enumerate(getDisplayRects(), 1): + imDisplay = getRectAsImage(rect) + imDisplay.save('screencapture_unsync_display_%d.png' % (displayNumber,), format='png') + + # Synchronized capture, entire virtual screen at once, cropped to one Image per display. + for displayNumber, im in enumerate(getDisplaysAsImages(), 1): + im.save('screencapture_sync_display_%d.png' % (displayNumber,), format='png') + + +if __name__ == '__main__': + _demo() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/__init__.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py new file mode 100644 index 00000000..f7f92756 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_torture_test.py @@ -0,0 +1,49 @@ +from __future__ import print_function + +import sys + +from desktopmagic.screengrab_win32 import GrabFailed, getScreenAsImage, getDisplaysAsImages, getRectAsImage + +def main(): + print("""\ +This program helps you test whether screengrab_win32 has memory leaks +and other problems. It takes a screenshot repeatedly and discards it. + +Open Task Manager and make sure Physical Memory % is not ballooning. +Memory leaks might not be blamed on the python process itself (which +will show low memory usage). + +Lock the workstation for a few minutes; make sure there are no leaks +and that there are no uncaught exceptions here. + +Repeat above after RDPing into the workstation and minimizing RDP; +this is like disconnecting the monitor. + +Change your color depth settings. Add and remove monitors. RDP +in at 256 colors. +""") + while True: + try: + getScreenAsImage() + print("S", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + try: + getDisplaysAsImages() + print("D", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + try: + getRectAsImage((0, 0, 1, 1)) + print("R", end=" ") + sys.stdout.flush() + except GrabFailed as e: + print(e) + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py new file mode 100644 index 00000000..6730c96d --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_display_rect.py @@ -0,0 +1,33 @@ +from __future__ import print_function + +from desktopmagic.screengrab_win32 import getDisplayRects + +import time + +def main(): + print("""\ +This program constantly polls your display rect information and prints +it when it changes. + +This can be used to make sure getDisplayRects is free from desync bugs +that occur during monitor configuration changes. +""") + lastRects = None + count = 0 + start = time.time() + while True: + if count != 0 and count % 1000 == 0: + end = time.time() + ##print(end - start, "for 1000 calls") + start = time.time() + + rects = getDisplayRects() + if rects != lastRects: + print(rects) + lastRects = rects + + count += 1 + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py new file mode 100644 index 00000000..94e39980 --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/scripts/screengrab_watch_virtual_screen_rect.py @@ -0,0 +1,33 @@ +from __future__ import print_function + +from desktopmagic.screengrab_win32 import getVirtualScreenRect + +import time + +def main(): + print("""\ +This program constantly polls your virtual screen rect information and +prints it when it changes. + +This can be used to make sure getVirtualScreenRect is free from desync +bugs that occur during monitor configuration changes. +""") + lastRect = None + count = 0 + start = time.time() + while True: + if count != 0 and count % 1000 == 0: + end = time.time() + ##print(end - start, "for 1000 calls") + start = time.time() + + rect = getVirtualScreenRect() + if rect != lastRect: + print(rect) + lastRect = rect + + count += 1 + + +if __name__ == '__main__': + main() diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/test/__init__.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py new file mode 100644 index 00000000..f7a59cee --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/desktopmagic/test/test_screengrab_win32.py @@ -0,0 +1,118 @@ +from __future__ import print_function + +import os +try: + # Needed for Python 2.6 compatibility + import unittest2 as unittest +except ImportError: + import unittest +import tempfile + +from desktopmagic.screengrab_win32 import getDisplayRects, saveRectToBmp, getRectAsImage, GrabFailed + +try: + # Pillow or PIL + from PIL import Image +except ImportError: + try: + # some old PIL installations + import Image + except ImportError: + Image = None + + +class GetDisplayRectsTest(unittest.TestCase): + """ + Tests for L{getDisplayRects} + """ + def test_getDisplayRectsReturnsList(self): + """ + L{getDisplayRects} returns a list of length >= 1 with a tuple containing 4 integers, + representing the geometry of each display. + """ + regions = getDisplayRects() + ##print("Display rects are:", regions) + self.assertIsInstance(regions, list) + for region in regions: + self.assertIsInstance(region, tuple) + for num in region: + self.assertIsInstance(num, int) + + + def disabled_test_getDisplayRectsDoesNotLeak(self): + """ + Calling L{getDisplayRects} 100,000 times does not leak memory (you'll have to + open taskmgr to make sure.) + + Disabled because Ivan manually confirmed that it does not leak. + """ + print("Open taskmgr.exe to make sure I'm not leaking memory right now.") + for i in xrange(100000): + getDisplayRects() + + + +class RectTests(unittest.TestCase): + def _tryUnlink(self, fname): + try: + os.unlink(fname) + except OSError: + pass + + + def test_workingCase(self): + if not Image: + self.skipTest("No PIL or Pillow") + + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + saveRectToBmp(fname, rect=(0, 0, 200, 100)) + with open(fname, "rb") as f: + im = Image.open(f) + self.assertEqual((200, 100), im.size) + + + def test_invalidRect(self): + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, 100))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 99, 100))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, 99))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 100, None))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, "100", None))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100.0, 100, 101, 101))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 101, 101.0))) + self.assertRaises(ValueError, lambda: saveRectToBmp(fname, rect=(100, 100, 200, 200, 200))) + self.assertRaises(TypeError, lambda: saveRectToBmp(fname, rect=None)) + self.assertRaises(TypeError, lambda: getRectAsImage(rect=None)) + + + def test_1x1SizeRect(self): + if not Image: + self.skipTest("No PIL or Pillow") + + fname = tempfile.mktemp() + '.bmp' + fnamePng = tempfile.mktemp() + '.png' + self.addCleanup(self._tryUnlink, fname) + self.addCleanup(self._tryUnlink, fnamePng) + saveRectToBmp(fname, rect=(100, 100, 101, 101)) + + with open(fname, "rb") as f: + im = Image.open(f) + self.assertEqual((1, 1), im.size) + + im = getRectAsImage(rect=(100, 100, 101, 101)) + self.assertEqual((1, 1), im.size) + im.save(fnamePng, format='png') + + with open(fnamePng, "rb") as f: + im = Image.open(f) + self.assertEqual((1, 1), im.size) + + + def test_rectTooBig(self): + fname = tempfile.mktemp() + self.addCleanup(self._tryUnlink, fname) + # Note that 26000x26000 is big enough to fail it on my system + self.assertRaises(GrabFailed, lambda: saveRectToBmp(fname, rect=(0, 0, 2600000, 2600000))) + self.assertRaises(GrabFailed, lambda: saveRectToBmp(fname, rect=(0, 0, 2600000, 260000000000000000))) diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/Scripts/screengrab_torture_test b/Resources/WPy64-3720/python-3.7.2.amd64/Scripts/screengrab_torture_test new file mode 100644 index 00000000..2c8a8bef --- /dev/null +++ b/Resources/WPy64-3720/python-3.7.2.amd64/Scripts/screengrab_torture_test @@ -0,0 +1,5 @@ +#!C:\Abs\Archive\scopeSrcUL\OpenRPA\Resources\WPy64-3720\python-3.7.2.amd64\python.exe + +from desktopmagic.scripts.screengrab_torture_test import main + +main()