_pygetwindow_macos.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import Quartz
  2. import pygetwindow
  3. def getAllTitles():
  4. """Returns a list of strings of window titles for all visible windows.
  5. """
  6. # Source: https://stackoverflow.com/questions/53237278/obtain-list-of-all-window-titles-on-macos-from-a-python-script/53985082#53985082
  7. windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
  8. return ['%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')) for win in windows]
  9. def getActiveWindow():
  10. """Returns a Window object of the currently active Window."""
  11. # Source: https://stackoverflow.com/questions/5286274/front-most-window-using-cgwindowlistcopywindowinfo
  12. windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
  13. for win in windows:
  14. if win['kCGWindowLayer'] == 0:
  15. return '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')) # Temporary. For now, we'll just return the title of the active window.
  16. raise Exception('Could not find an active window.') # Temporary hack.
  17. def getWindowsAt(x, y):
  18. windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
  19. matches = []
  20. for win in windows:
  21. w = win['kCGWindowBounds']
  22. if pygetwindow.pointInRect(x, y, w['X'], w['Y'], w['Width'], w['Height']):
  23. matches.append('%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')))
  24. return matches
  25. def activate():
  26. # TEMP - this is not a real api, I'm just using this name to store these notes for now.
  27. # Source: https://stackoverflow.com/questions/7460092/nswindow-makekeyandorderfront-makes-window-appear-but-not-key-or-front?rq=1
  28. # Source: https://stackoverflow.com/questions/4905024/is-it-possible-to-bring-window-to-front-without-taking-focus?rq=1
  29. pass
  30. def getWindowGeometry(title):
  31. # TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
  32. windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
  33. for win in windows:
  34. if title in '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')):
  35. w = win['kCGWindowBounds']
  36. return (w['X'], w['Y'], w['Width'], w['Height'])
  37. def isVisible(title):
  38. # TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
  39. windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListExcludeDesktopElements | Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
  40. for win in windows:
  41. if title in '%s %s' % (win[Quartz.kCGWindowOwnerName], win.get(Quartz.kCGWindowName, '')):
  42. return win['kCGWindowAlpha'] != 0.0
  43. def isMinimized():
  44. # TEMP - this is not a real api, I'm just using this name to stoe these notes for now.
  45. # Source: https://stackoverflow.com/questions/10258676/how-to-know-whether-a-window-is-minimised-or-not
  46. # 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".)
  47. # I'm not sure how kCGWindowListOptionOnScreenOnly interferes with this.
  48. pass
  49. # TODO: This class doesn't work yet. I've copied the Win32Window class and will make adjustments as needed here.
  50. class MacOSWindow():
  51. def __init__(self, hWnd):
  52. self._hWnd = hWnd # TODO fix this, this is a LP_c_long insead of an int.
  53. def _onRead(attrName):
  54. r = self._getWindowRect(_hWnd)
  55. self._rect._left = r.left # Setting _left directly to skip the onRead.
  56. self._rect._top = r.top # Setting _top directly to skip the onRead.
  57. self._rect._width = r.right - r.left # Setting _width directly to skip the onRead.
  58. self._rect._height = r.bottom - r.top # Setting _height directly to skip the onRead.
  59. def _onChange(oldBox, newBox):
  60. self.moveTo(newBox.left, newBox.top)
  61. self.resizeTo(newBox.width, newBox.height)
  62. r = self._getWindowRect(_hWnd)
  63. self._rect = pyrect.Rect(r.left, r.top, r.right - r.left, r.bottom - r.top, onChange=_onChange, onRead=_onRead)
  64. def __str__(self):
  65. r = self._getWindowRect(_hWnd)
  66. width = r.right - r.left
  67. height = r.bottom - r.top
  68. return '<%s left="%s", top="%s", width="%s", height="%s", title="%s">' % (self.__class__.__name__, r.left, r.top, width, height, self.title)
  69. def __repr__(self):
  70. return '%s(hWnd=%s)' % (self.__class__.__name__, self._hWnd)
  71. def __eq__(self, other):
  72. return isinstance(other, MacOSWindow) and self._hWnd == other._hWnd
  73. def close(self):
  74. """Closes this window. This may trigger "Are you sure you want to
  75. quit?" dialogs or other actions that prevent the window from
  76. actually closing. This is identical to clicking the X button on the
  77. window."""
  78. raise NotImplementedError
  79. def minimize(self):
  80. """Minimizes this window."""
  81. raise NotImplementedError
  82. def maximize(self):
  83. """Maximizes this window."""
  84. raise NotImplementedError
  85. def restore(self):
  86. """If maximized or minimized, restores the window to it's normal size."""
  87. raise NotImplementedError
  88. def activate(self):
  89. """Activate this window and make it the foreground window."""
  90. raise NotImplementedError
  91. def resizeRel(self, widthOffset, heightOffset):
  92. """Resizes the window relative to its current size."""
  93. raise NotImplementedError
  94. def resizeTo(self, newWidth, newHeight):
  95. """Resizes the window to a new width and height."""
  96. raise NotImplementedError
  97. def moveRel(self, xOffset, yOffset):
  98. """Moves the window relative to its current position."""
  99. raise NotImplementedError
  100. def moveTo(self, newLeft, newTop):
  101. """Moves the window to new coordinates on the screen."""
  102. raise NotImplementedError
  103. @property
  104. def isMinimized(self):
  105. """Returns True if the window is currently minimized."""
  106. raise NotImplementedError
  107. @property
  108. def isMaximized(self):
  109. """Returns True if the window is currently maximized."""
  110. raise NotImplementedError
  111. @property
  112. def isActive(self):
  113. """Returns True if the window is currently the active, foreground window."""
  114. raise NotImplementedError
  115. @property
  116. def title(self):
  117. """Returns the window title as a string."""
  118. raise NotImplementedError
  119. @property
  120. def visible(self):
  121. raise NotImplementedError