You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
12 KiB
388 lines
12 KiB
5 years ago
|
import Quartz
|
||
|
import pygetwindow
|
||
|
|
||
|
|
||
|
def getAllTitles():
|
||
|
"""Returns a list of strings of window titles for all visible windows.
|
||
|
"""
|
||
|
|
||
|
# Source: https://stackoverflow.com/questions/53237278/obtain-list-of-all-window-titles-on-macos-from-a-python-script/53985082#53985082
|
||
|
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
||
|
return ['%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')) for win in windows]
|
||
|
|
||
|
|
||
|
def getFocusedWindow():
|
||
|
"""Returns a Window object of the currently focused Window."""
|
||
|
|
||
|
# Source: https://stackoverflow.com/questions/5286274/front-most-window-using-cgwindowlistcopywindowinfo
|
||
|
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
||
|
for win in windows:
|
||
|
if win['kCGWindowLayer'] == 0:
|
||
|
return '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')) # Temporary. For now, we'll just return the title of the focused window.
|
||
|
raise Exception('Could not find a focused window.') # Temporary hack.
|
||
|
|
||
|
|
||
|
def getWindowsAt(x, y):
|
||
|
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
||
|
matches = []
|
||
|
for win in windows:
|
||
|
w = win['kCGWindowBounds']
|
||
|
if pygetwindow.pointInRect(x, y, w['X'], w['Y'], w['Width'], w['Height']):
|
||
|
matches.append('%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')))
|
||
|
return matches
|
||
|
|
||
|
|
||
|
|
||
|
def focusWindow():
|
||
|
# TEMP - this is not a real api, I'm just using this name to store these notes for now.
|
||
|
|
||
|
# Source: https://stackoverflow.com/questions/7460092/nswindow-makekeyandorderfront-makes-window-appear-but-not-key-or-front?rq=1
|
||
|
# Source: https://stackoverflow.com/questions/4905024/is-it-possible-to-bring-window-to-front-without-taking-focus?rq=1
|
||
|
pass
|
||
|
|
||
|
|
||
|
def getWindowGeometry(title):
|
||
|
# TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
|
||
|
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
||
|
for win in windows:
|
||
|
if title in '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')):
|
||
|
w = win['kCGWindowBounds']
|
||
|
return (w['X'], w['Y'], w['Width'], w['Height'])
|
||
|
|
||
|
|
||
|
def isVisible(title):
|
||
|
# TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
|
||
|
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
||
|
for win in windows:
|
||
|
if title in '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')):
|
||
|
return win['kCGWindowAlpha'] != 0.0
|
||
|
|
||
|
def isMinimized():
|
||
|
# TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
|
||
|
# Source: https://stackoverflow.com/questions/10258676/how-to-know-whether-a-window-is-minimised-or-not
|
||
|
# Use the kCGWindowIsOnscreen to check this. Minimized windows are considered to not be on the screen. (But I'm not sure if there are other situations where a window is "off screen".)
|
||
|
|
||
|
# I'm not sure how kCGWindowListOptionOnScreenOnly interferes with this.
|
||
|
pass
|
||
|
|
||
|
# TODO: This class doesn't work yet. I've copied the Win32Window class and will make adjustments as needed here.
|
||
|
|
||
|
class MacOSWindow():
|
||
|
def __init__(self, hWnd):
|
||
|
self._hWnd = hWnd # TODO fix this, this is a LP_c_long insead of an int.
|
||
|
|
||
|
def _onRead(attrName):
|
||
|
r = _getWindowRect(self._hWnd)
|
||
|
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 = _getWindowRect(self._hWnd)
|
||
|
self._rect = pyrect.Rect(r.left, r.top, r.right - r.left, r.bottom - r.top, onChange=_onChange, onRead=_onRead)
|
||
|
|
||
|
def __str__(self):
|
||
|
r = _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__.__name__, r.left, r.top, width, height, self.title)
|
||
|
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '%s(hWnd=%s)' % (self.__class__.__name__, self._hWnd)
|
||
|
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
return isinstance(other, Win32Window) and self._hWnd == other._hWnd
|
||
|
|
||
|
|
||
|
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."""
|
||
|
result = ctypes.windll.user32.PostMessageA(self._hWnd, WM_CLOSE, 0, 0)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
def minimize(self):
|
||
|
"""Minimizes this window."""
|
||
|
ctypes.windll.user32.ShowWindow(self._hWnd, SW_MINIMIZE)
|
||
|
|
||
|
|
||
|
def maximize(self):
|
||
|
"""Maximizes this window."""
|
||
|
ctypes.windll.user32.ShowWindow(self._hWnd, SW_MAXIMIZE)
|
||
|
|
||
|
|
||
|
def restore(self):
|
||
|
"""If maximized or minimized, restores the window to it's normal size."""
|
||
|
ctypes.windll.user32.ShowWindow(self._hWnd, SW_RESTORE)
|
||
|
|
||
|
|
||
|
def focus(self):
|
||
|
"""Focus this window and make it the foreground window."""
|
||
|
result = ctypes.windll.user32.SetForegroundWindow(self._hWnd)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
def resizeRel(self, widthOffset, heightOffset):
|
||
|
"""Resizes the window relative to its current size."""
|
||
|
result = ctypes.windll.user32.SetWindowPos(self._hWnd, HWND_TOP, self.left, self.top, self.width + widthOffset, self.height + heightOffset, 0)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
def resizeTo(self, newWidth, newHeight):
|
||
|
"""Resizes the window to a new width and height."""
|
||
|
result = ctypes.windll.user32.SetWindowPos(self._hWnd, HWND_TOP, self.left, self.top, newWidth, newHeight, 0)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
def moveRel(self, xOffset, yOffset):
|
||
|
"""Moves the window relative to its current position."""
|
||
|
result = ctypes.windll.user32.SetWindowPos(self._hWnd, HWND_TOP, self.left + xOffset, self.top + yOffset, self.width, self.height, 0)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
def moveTo(self, newLeft, newTop):
|
||
|
"""Moves the window to new coordinates on the screen."""
|
||
|
result = ctypes.windll.user32.SetWindowPos(self._hWnd, HWND_TOP, newLeft, newTop, self.width, self.height, 0)
|
||
|
if result == 0:
|
||
|
_raiseWithLastError()
|
||
|
|
||
|
|
||
|
@property
|
||
|
def isMinimized(self):
|
||
|
"""Returns True if the window is currently minimized."""
|
||
|
return ctypes.windll.user32.IsIconic(self._hWnd) != 0
|
||
|
|
||
|
@property
|
||
|
def isMaximized(self):
|
||
|
"""Returns True if the window is currently maximized."""
|
||
|
return ctypes.windll.user32.IsZoomed(self._hWnd) != 0
|
||
|
|
||
|
@property
|
||
|
def isFocused(self):
|
||
|
"""Returns True if the window is currently the focused, foreground window."""
|
||
|
return getFocusedWindow() == self
|
||
|
|
||
|
@property
|
||
|
def title(self):
|
||
|
"""Returns the window title as a string."""
|
||
|
return _getWindowText(self._hWnd)
|
||
|
|
||
|
@property
|
||
|
def visible(self):
|
||
|
return isWindowVisible(self._hWnd)
|
||
|
|
||
|
|
||
|
|
||
|
# 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
|