350 lines
9.8 KiB
Python
350 lines
9.8 KiB
Python
|
# PyGetWindow
|
||
|
# A cross-platform module to find information about the windows on the screen.
|
||
|
|
||
|
# Work in progress
|
||
|
|
||
|
# Useful info:
|
||
|
# https://stackoverflow.com/questions/373020/finding-the-current-active-window-in-mac-os-x-using-python
|
||
|
# https://stackoverflow.com/questions/7142342/get-window-position-size-with-python
|
||
|
|
||
|
|
||
|
# win32 api and ctypes on Windows
|
||
|
# cocoa api and pyobjc on Mac
|
||
|
# Xlib on linux
|
||
|
|
||
|
|
||
|
# Possible Future Features:
|
||
|
# get/click menu (win32: GetMenuItemCount, GetMenuItemInfo, GetMenuItemID, GetMenu, GetMenuItemRect)
|
||
|
|
||
|
|
||
|
__version__ = "0.0.8"
|
||
|
|
||
|
import sys, collections, pyrect
|
||
|
|
||
|
|
||
|
class PyGetWindowException(Exception):
|
||
|
"""
|
||
|
Base class for exceptions raised when PyGetWindow functions
|
||
|
encounter a problem. If PyGetWindow raises an exception that isn't
|
||
|
this class, that indicates a bug in the module.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
|
||
|
def pointInRect(x, y, left, top, width, height):
|
||
|
"""Returns ``True`` if the ``(x, y)`` point is within the box described
|
||
|
by ``(left, top, width, height)``."""
|
||
|
return left < x < left + width and top < y < top + height
|
||
|
|
||
|
|
||
|
# NOTE: `Rect` is a named tuple for use in Python, while structs.RECT represents
|
||
|
# the win32 RECT struct. PyRect's Rect class is used for handling changing
|
||
|
# geometry of rectangular areas.
|
||
|
Rect = collections.namedtuple("Rect", "left top right bottom")
|
||
|
Point = collections.namedtuple("Point", "x y")
|
||
|
Size = collections.namedtuple("Size", "width height")
|
||
|
|
||
|
|
||
|
class BaseWindow:
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
def _setupRectProperties(self):
|
||
|
def _onRead(attrName):
|
||
|
r = self._getWindowRect()
|
||
|
self._rect._left = r.left # Setting _left directly to skip the onRead.
|
||
|
self._rect._top = r.top # Setting _top directly to skip the onRead.
|
||
|
self._rect._width = r.right - r.left # Setting _width directly to skip the onRead.
|
||
|
self._rect._height = r.bottom - r.top # Setting _height directly to skip the onRead.
|
||
|
|
||
|
def _onChange(oldBox, newBox):
|
||
|
self.moveTo(newBox.left, newBox.top)
|
||
|
self.resizeTo(newBox.width, newBox.height)
|
||
|
|
||
|
r = self._getWindowRect()
|
||
|
self._rect = pyrect.Rect(r.left, r.top, r.right - r.left, r.bottom - r.top, onChange=_onChange, onRead=_onRead)
|
||
|
|
||
|
def _getWindowRect(self):
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def __str__(self):
|
||
|
r = self._getWindowRect(self._hWnd)
|
||
|
width = r.right - r.left
|
||
|
height = r.bottom - r.top
|
||
|
return '<%s left="%s", top="%s", width="%s", height="%s", title="%s">' % (
|
||
|
self.__class__.__qualname__,
|
||
|
r.left,
|
||
|
r.top,
|
||
|
width,
|
||
|
height,
|
||
|
self.title,
|
||
|
)
|
||
|
|
||
|
def close(self):
|
||
|
"""Closes this window. This may trigger "Are you sure you want to
|
||
|
quit?" dialogs or other actions that prevent the window from
|
||
|
actually closing. This is identical to clicking the X button on the
|
||
|
window."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def minimize(self):
|
||
|
"""Minimizes this window."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def maximize(self):
|
||
|
"""Maximizes this window."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def restore(self):
|
||
|
"""If maximized or minimized, restores the window to it's normal size."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def activate(self):
|
||
|
"""Activate this window and make it the foreground window."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def resizeRel(self, widthOffset, heightOffset):
|
||
|
"""Resizes the window relative to its current size."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def resizeTo(self, newWidth, newHeight):
|
||
|
"""Resizes the window to a new width and height."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def moveRel(self, xOffset, yOffset):
|
||
|
"""Moves the window relative to its current position."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def moveTo(self, newLeft, newTop):
|
||
|
"""Moves the window to new coordinates on the screen."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@property
|
||
|
def isMinimized(self):
|
||
|
"""Returns True if the window is currently minimized."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@property
|
||
|
def isMaximized(self):
|
||
|
"""Returns True if the window is currently maximized."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@property
|
||
|
def isActive(self):
|
||
|
"""Returns True if the window is currently the active, foreground window."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@property
|
||
|
def title(self):
|
||
|
"""Returns the window title as a string."""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@property
|
||
|
def visible(self):
|
||
|
raise NotImplementedError
|
||
|
|
||
|
# Wrappers for pyrect.Rect object's properties:
|
||
|
@property
|
||
|
def left(self):
|
||
|
return self._rect.left
|
||
|
|
||
|
@left.setter
|
||
|
def left(self, value):
|
||
|
# import pdb; pdb.set_trace()
|
||
|
self._rect.left # Run rect's onRead to update the Rect object.
|
||
|
self._rect.left = value
|
||
|
|
||
|
@property
|
||
|
def right(self):
|
||
|
return self._rect.right
|
||
|
|
||
|
@right.setter
|
||
|
def right(self, value):
|
||
|
self._rect.right # Run rect's onRead to update the Rect object.
|
||
|
self._rect.right = value
|
||
|
|
||
|
@property
|
||
|
def top(self):
|
||
|
return self._rect.top
|
||
|
|
||
|
@top.setter
|
||
|
def top(self, value):
|
||
|
self._rect.top # Run rect's onRead to update the Rect object.
|
||
|
self._rect.top = value
|
||
|
|
||
|
@property
|
||
|
def bottom(self):
|
||
|
return self._rect.bottom
|
||
|
|
||
|
@bottom.setter
|
||
|
def bottom(self, value):
|
||
|
self._rect.bottom # Run rect's onRead to update the Rect object.
|
||
|
self._rect.bottom = value
|
||
|
|
||
|
@property
|
||
|
def topleft(self):
|
||
|
return self._rect.topleft
|
||
|
|
||
|
@topleft.setter
|
||
|
def topleft(self, value):
|
||
|
self._rect.topleft # Run rect's onRead to update the Rect object.
|
||
|
self._rect.topleft = value
|
||
|
|
||
|
@property
|
||
|
def topright(self):
|
||
|
return self._rect.topright
|
||
|
|
||
|
@topright.setter
|
||
|
def topright(self, value):
|
||
|
self._rect.topright # Run rect's onRead to update the Rect object.
|
||
|
self._rect.topright = value
|
||
|
|
||
|
@property
|
||
|
def bottomleft(self):
|
||
|
return self._rect.bottomleft
|
||
|
|
||
|
@bottomleft.setter
|
||
|
def bottomleft(self, value):
|
||
|
self._rect.bottomleft # Run rect's onRead to update the Rect object.
|
||
|
self._rect.bottomleft = value
|
||
|
|
||
|
@property
|
||
|
def bottomright(self):
|
||
|
return self._rect.bottomright
|
||
|
|
||
|
@bottomright.setter
|
||
|
def bottomright(self, value):
|
||
|
self._rect.bottomright # Run rect's onRead to update the Rect object.
|
||
|
self._rect.bottomright = value
|
||
|
|
||
|
@property
|
||
|
def midleft(self):
|
||
|
return self._rect.midleft
|
||
|
|
||
|
@midleft.setter
|
||
|
def midleft(self, value):
|
||
|
self._rect.midleft # Run rect's onRead to update the Rect object.
|
||
|
self._rect.midleft = value
|
||
|
|
||
|
@property
|
||
|
def midright(self):
|
||
|
return self._rect.midright
|
||
|
|
||
|
@midright.setter
|
||
|
def midright(self, value):
|
||
|
self._rect.midright # Run rect's onRead to update the Rect object.
|
||
|
self._rect.midright = value
|
||
|
|
||
|
@property
|
||
|
def midtop(self):
|
||
|
return self._rect.midtop
|
||
|
|
||
|
@midtop.setter
|
||
|
def midtop(self, value):
|
||
|
self._rect.midtop # Run rect's onRead to update the Rect object.
|
||
|
self._rect.midtop = value
|
||
|
|
||
|
@property
|
||
|
def midbottom(self):
|
||
|
return self._rect.midbottom
|
||
|
|
||
|
@midbottom.setter
|
||
|
def midbottom(self, value):
|
||
|
self._rect.midbottom # Run rect's onRead to update the Rect object.
|
||
|
self._rect.midbottom = value
|
||
|
|
||
|
@property
|
||
|
def center(self):
|
||
|
return self._rect.center
|
||
|
|
||
|
@center.setter
|
||
|
def center(self, value):
|
||
|
self._rect.center # Run rect's onRead to update the Rect object.
|
||
|
self._rect.center = value
|
||
|
|
||
|
@property
|
||
|
def centerx(self):
|
||
|
return self._rect.centerx
|
||
|
|
||
|
@centerx.setter
|
||
|
def centerx(self, value):
|
||
|
self._rect.centerx # Run rect's onRead to update the Rect object.
|
||
|
self._rect.centerx = value
|
||
|
|
||
|
@property
|
||
|
def centery(self):
|
||
|
return self._rect.centery
|
||
|
|
||
|
@centery.setter
|
||
|
def centery(self, value):
|
||
|
self._rect.centery # Run rect's onRead to update the Rect object.
|
||
|
self._rect.centery = value
|
||
|
|
||
|
@property
|
||
|
def width(self):
|
||
|
return self._rect.width
|
||
|
|
||
|
@width.setter
|
||
|
def width(self, value):
|
||
|
self._rect.width # Run rect's onRead to update the Rect object.
|
||
|
self._rect.width = value
|
||
|
|
||
|
@property
|
||
|
def height(self):
|
||
|
return self._rect.height
|
||
|
|
||
|
@height.setter
|
||
|
def height(self, value):
|
||
|
self._rect.height # Run rect's onRead to update the Rect object.
|
||
|
self._rect.height = value
|
||
|
|
||
|
@property
|
||
|
def size(self):
|
||
|
return self._rect.size
|
||
|
|
||
|
@size.setter
|
||
|
def size(self, value):
|
||
|
self._rect.size # Run rect's onRead to update the Rect object.
|
||
|
self._rect.size = value
|
||
|
|
||
|
@property
|
||
|
def area(self):
|
||
|
return self._rect.area
|
||
|
|
||
|
@area.setter
|
||
|
def area(self, value):
|
||
|
self._rect.area # Run rect's onRead to update the Rect object.
|
||
|
self._rect.area = value
|
||
|
|
||
|
@property
|
||
|
def box(self):
|
||
|
return self._rect.box
|
||
|
|
||
|
@box.setter
|
||
|
def box(self, value):
|
||
|
self._rect.box # Run rect's onRead to update the Rect object.
|
||
|
self._rect.box = value
|
||
|
|
||
|
|
||
|
if sys.platform == "darwin":
|
||
|
# raise NotImplementedError('PyGetWindow currently does not support macOS. If you have Appkit/Cocoa knowledge, please contribute! https://github.com/asweigart/pygetwindow') # TODO - implement mac
|
||
|
from ._pygetwindow_macos import *
|
||
|
|
||
|
Window = MacOSWindow
|
||
|
elif sys.platform == "win32":
|
||
|
from ._pygetwindow_win import (
|
||
|
Win32Window,
|
||
|
getActiveWindow,
|
||
|
getActiveWindowTitle,
|
||
|
getWindowsAt,
|
||
|
getWindowsWithTitle,
|
||
|
getAllWindows,
|
||
|
getAllTitles,
|
||
|
)
|
||
|
|
||
|
Window = Win32Window
|
||
|
else:
|
||
|
raise NotImplementedError(
|
||
|
"PyGetWindow currently does not support Linux. If you have Xlib knowledge, please contribute! https://github.com/asweigart/pygetwindow"
|
||
|
)
|