mrob95/pyvda

pywin32 is already a requirement, Can an hwnd_exists function be added to pyvda.py?

Opened this issue · 0 comments

def hwnd_exists(hwnd) -> bool:
    _exists = False

    def enum_windows(hwnd, _hwnd):
        nonlocal _exists

        if hwnd == _hwnd:
            _exists = True

    win32gui.EnumWindows(enum_windows, hwnd)

    return _exists

I used the pyvda.utils.Managers().view_collection.GetViewsByZOrder() to get the hwnd, and with psutil.Process to create a class that wraps the whole app. I have four windows of PyCharm open and this will return a screenshot of each window that is attached to pycharm64.exe process.

import ctypes

import cv2 as cv
import numpy
import psutil
import pyvda
import pyvda.com_defns
import pyvda.utils
import win32con
import win32gui
import win32ui


class AppProcess(psutil.Process, pyvda.AppView):
    def __init__(self, hwnd: int):
        from win32process import GetWindowThreadProcessId as GetPID

        psutil.Process.__init__(self, pid = GetPID(hwnd)[1])
        pyvda.AppView.__init__(self, hwnd = hwnd)

    @property
    def window_title(self) -> str:
        return win32gui.GetWindowText(self.hwnd)

    @property
    def name(self):
        return psutil.Process.name(self)

    @property
    def screenshot(self):
        def hwnd_exists(hwnd: int) -> bool:
            _exists = False

            def enum_windows(hwnd, _hwnd):
                nonlocal _exists

                if _hwnd == hwnd:
                    _exists = True

            win32gui.EnumWindows(enum_windows, hwnd)

            return _exists

        def reshape(left: int, top: int, right: int, bottom: int):
            return right - left, bottom - top

        if not hwnd_exists(self.hwnd):
            return numpy.array([])

        ctypes.windll.user32.SetProcessDPIAware()

        width, height = reshape(*win32gui.GetWindowRect(self.hwnd))

        wdc = win32gui.GetWindowDC(self.hwnd)
        dc = win32ui.CreateDCFromHandle(wdc)
        cdc = dc.CreateCompatibleDC()
        bitmap = win32ui.CreateBitmap()
        bitmap.CreateCompatibleBitmap(dc, width, height)
        cdc.SelectObject(bitmap)
        cdc.BitBlt((0, 0), (width, height), dc, (0, 0), win32con.SRCCOPY)

        ctypes.windll.user32.PrintWindow(self.hwnd, cdc.GetSafeHdc(), 3)

        _screenshot = numpy.frombuffer(bitmap.GetBitmapBits(True), dtype = numpy.uint8).reshape((height, width, 4))

        dc.DeleteDC()
        cdc.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, wdc)
        win32gui.DeleteObject(bitmap.GetHandle())

        return _screenshot


def get_handles_by_z_order():
    _: list[int] = []
    array = pyvda.utils.Managers().view_collection.GetViewsByZOrder()
    for view in array.iter(pyvda.com_defns.IApplicationView):
        if bool(view.GetShowInSwitchers()):
            _.append(view.GetThumbnailWindow())
    return _


if __name__ == '__main__':
    for hwnd in get_handles_by_z_order().copy():
        process = AppProcess(hwnd = hwnd)
        if process.name == 'pycharm64.exe':
            cv.imshow(winname = 'screenshot', mat = process.screenshot)
            cv.waitKey()

To change it up a bit...

if __name__ == '__main__':
    for hwnd in get_handles_by_z_order().copy():
        process = AppProcess(hwnd = hwnd)
        if process.name == 'ACMirage.exe':
            screenshot = cv.resize(
                src = process.screenshot,
                dsize = (0, 0),
                fx = 0.5,
                fy = 0.5
            )
            cv.imshow(winname = 'screenshot', mat = screenshot)
            cv.waitKey()

Screenshot 2024-07-28 152100

Anyway I see a point in having the def hwnd_exists(hwnd: int) -> bool: function.