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