parent
723b2b4037
commit
84d4412f31
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014, Al Sweigart
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the PyAutoGUI nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,138 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyAutoGUI
|
||||
Version: 0.9.44
|
||||
Summary: A cross-platform module for GUI automation for human beings. Control the keyboard and mouse from a Python script.
|
||||
Home-page: https://github.com/asweigart/pyautogui
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: gui automation test testing keyboard mouse cursor click press keystroke control
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: pymsgbox
|
||||
Requires-Dist: PyTweening (>=1.0.1)
|
||||
Requires-Dist: Pillow
|
||||
Requires-Dist: pyscreeze (>=0.1.21)
|
||||
Requires-Dist: pygetwindow (>=0.0.5)
|
||||
|
||||
PyAutoGUI
|
||||
=========
|
||||
|
||||
PyAutoGUI is a cross-platform GUI automation Python module for human beings. Used to programmatically control the mouse & keyboard.
|
||||
|
||||
`pip install pyautogui`
|
||||
|
||||
Full documentation available at https://pyautogui.readthedocs.org
|
||||
|
||||
Simplified Chinese documentation available at https://muxuezi.github.io/posts/doc-pyautogui.html
|
||||
|
||||
Source code available at https://github.com/asweigart/pyautogui
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
PyAutoGUI supports Python 2 and 3. If you are installing PyAutoGUI from PyPI using pip:
|
||||
|
||||
Windows has no dependencies. The Win32 extensions do not need to be installed.
|
||||
|
||||
OS X needs the pyobjc-core and pyobjc module installed (in that order).
|
||||
|
||||
Linux needs the python3-xlib (or python-xlib for Python 2) module installed.
|
||||
|
||||
Pillow needs to be installed, and on Linux you may need to install additional libraries to make sure Pillow's PNG/JPEG works correctly. See:
|
||||
|
||||
https://stackoverflow.com/questions/7648200/pip-install-pil-e-tickets-1-no-jpeg-png-support
|
||||
|
||||
http://ubuntuforums.org/showthread.php?t=1751455
|
||||
|
||||
If you want to do development and contribute to PyAutoGUI, you will need to install these modules from PyPI:
|
||||
|
||||
* pyscreeze
|
||||
* pymsgbox
|
||||
* pytweening
|
||||
|
||||
Example Usage
|
||||
=============
|
||||
|
||||
Keyboard and Mouse Control
|
||||
--------------------------
|
||||
```python
|
||||
>>> import pyautogui
|
||||
>>> screenWidth, screenHeight = pyautogui.size()
|
||||
>>> currentMouseX, currentMouseY = pyautogui.position()
|
||||
>>> pyautogui.moveTo(100, 150)
|
||||
>>> pyautogui.click()
|
||||
>>> pyautogui.moveRel(None, 10) # move mouse 10 pixels down
|
||||
>>> pyautogui.doubleClick()
|
||||
>>> pyautogui.moveTo(500, 500, duration=2, tween=pyautogui.tweens.easeInOutQuad) # use tweening/easing function to move mouse over 2 seconds.
|
||||
>>> pyautogui.typewrite('Hello world!', interval=0.25) # type with quarter-second pause in between each key
|
||||
>>> pyautogui.press('esc')
|
||||
>>> pyautogui.keyDown('shift')
|
||||
>>> pyautogui.typewrite(['left', 'left', 'left', 'left', 'left', 'left'])
|
||||
>>> pyautogui.keyUp('shift')
|
||||
>>> pyautogui.hotkey('ctrl', 'c')
|
||||
```
|
||||
|
||||
Display Message Boxes
|
||||
---------------------
|
||||
```python
|
||||
>>> import pyautogui
|
||||
>>> pyautogui.alert('This is an alert box.')
|
||||
'OK'
|
||||
>>> pyautogui.confirm('Shall I proceed?')
|
||||
'Cancel'
|
||||
>>> pyautogui.confirm('Enter option.', buttons=['A', 'B', 'C'])
|
||||
'B'
|
||||
>>> pyautogui.prompt('What is your name?')
|
||||
'Al'
|
||||
>>> pyautogui.password('Enter password (text will be hidden)')
|
||||
'swordfish'
|
||||
```
|
||||
Screenshot Functions
|
||||
--------------------
|
||||
|
||||
(PyAutoGUI uses Pillow for image-related features.)
|
||||
```python
|
||||
>>> import pyautogui
|
||||
>>> im1 = pyautogui.screenshot()
|
||||
>>> im1.save('my_screenshot.png')
|
||||
>>> im2 = pyautogui.screenshot('my_screenshot2.png')
|
||||
```
|
||||
You can also locate where an image is on the screen:
|
||||
```python
|
||||
>>> import pyautogui
|
||||
>>> button7location = pyautogui.locateOnScreen('button.png') # returns (left, top, width, height) of matching region
|
||||
>>> button7location
|
||||
(1416, 562, 50, 41)
|
||||
>>> buttonx, buttony = pyautogui.center(button7location)
|
||||
>>> buttonx, buttony
|
||||
(1441, 582)
|
||||
>>> pyautogui.click(buttonx, buttony) # clicks the center of where the button was found
|
||||
```
|
||||
The locateCenterOnScreen() function returns the center of this match region:
|
||||
```python
|
||||
>>> import pyautogui
|
||||
>>> buttonx, buttony = pyautogui.locateCenterOnScreen('button.png') # returns (x, y) of matching region
|
||||
>>> buttonx, buttony
|
||||
(1441, 582)
|
||||
>>> pyautogui.click(buttonx, buttony) # clicks the center of where the button was found
|
||||
```
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
PyAutoGUI-0.9.44.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyAutoGUI-0.9.44.dist-info/LICENSE.txt,sha256=rNMjpKuy_R62UDcXVGvAbRWA3qmJw7EzjRdrp8N8Zl0,1509
|
||||
PyAutoGUI-0.9.44.dist-info/METADATA,sha256=K2mubdEMFXv_Opkrn5f9Ax140-njnxIRCEpFClBe6YU,4964
|
||||
PyAutoGUI-0.9.44.dist-info/RECORD,,
|
||||
PyAutoGUI-0.9.44.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98
|
||||
PyAutoGUI-0.9.44.dist-info/top_level.txt,sha256=YztmWux-iPoF3Dv9x530vZvae0kMBsGbPAoKgWRGAvk,10
|
||||
pyautogui/__init__.py,sha256=RlRUdaTYdsfwwLXCANjBSCJGZhssrguuUoSJQ9g7auI,45305
|
||||
pyautogui/__main__.py,sha256=P8Ny3OScHrutsXUz-UsqKamaFihW-Yuq0YzKHkRGADI,58
|
||||
pyautogui/__pycache__/__init__.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/__main__.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/_pyautogui_java.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/_pyautogui_osx.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/_pyautogui_win.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/_pyautogui_x11.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/_window_win.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/screenshotUtil.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/test1.cpython-37.pyc,,
|
||||
pyautogui/__pycache__/tweens.cpython-37.pyc,,
|
||||
pyautogui/_pyautogui_java.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
pyautogui/_pyautogui_osx.py,sha256=LwG6oFs9-MUxxga7Pj15B2UcGFxBJieuoH6yx5jHnSo,15647
|
||||
pyautogui/_pyautogui_win.py,sha256=qAwFfQk5G1kF8EI5wWa5MVewYuCMZQQnnM-xPCbtQhk,21294
|
||||
pyautogui/_pyautogui_x11.py,sha256=_g7VOXO6HjFvrhEr9bt2R1o1un3s-VzOmTkkPqTvCoE,15634
|
||||
pyautogui/_window_win.py,sha256=ReLnzdOoS-hlsTWkD2dQ-qyU_yuOEplJYHcZNtZxA04,3902
|
||||
pyautogui/screenshotUtil.py,sha256=S6pwcQZV7wMPvplAsp_pkcENcjIQdXqLqxBP926Z-OA,8363
|
||||
pyautogui/test1.py,sha256=hNr3BL5Dv2JSZNzXd_h-NkixMWbrm3EPB10MnB3-iUA,106
|
||||
pyautogui/tweens.py,sha256=NqjZcusP6jcxfQxa3zsMVft5PZrpouxlpDhOy_kd8s8,1440
|
@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: cp37-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pyautogui
|
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,99 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyGetWindow
|
||||
Version: 0.0.5
|
||||
Summary: A simple, cross-platform module for obtaining GUI information on application's windows.
|
||||
Home-page: https://github.com/asweigart/pygetwindow
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: gui window geometry resize minimize maximize close title
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: pyrect
|
||||
|
||||
PyGetWindow
|
||||
===========
|
||||
|
||||
A simple, cross-platform module for obtaining GUI information on and controlling application's windows.
|
||||
|
||||
|
||||
Still under development. Currently only the Windows platform is implemented. If you want to help contribute, please contact al@inventwithpython.com!
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
pip install pygetwindow
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
(For this example, I'm using Winodws and opened the Notepad application, which has a title of "Untitled - Notepad". Most of the effects of these functions can't be seen in text.)
|
||||
|
||||
PyGetWindow has functions for obtaining ``Window`` objects from a place on the screen, from the window title, or just getting all windows. (``hWnd`` is specific to the Windows platform.)
|
||||
|
||||
>>> import pygetwindow as gw
|
||||
>>> gw.getAllTitles()
|
||||
('', 'C:\\WINDOWS\\system32\\cmd.exe - pipenv shell - python', 'C:\\github\\PyGetWindow\\README.md • - Sublime Text', "asweigart/PyGetWindow: A simple, cross-platform module for obtaining GUI information on application's windows. - Google Chrome", 'Untitled - Notepad', 'C:\\Users\\Al\\Desktop\\xlibkey.py • - Sublime Text', 'https://tronche.com/gui/x/xlib/ - Google Chrome', 'Xlib Programming Manual: XGetWindowAttributes - Google Chrome', 'Generic Ubuntu Box [Running] - Oracle VM VirtualBox', 'Oracle VM VirtualBox Manager', 'Microsoft Edge', 'Microsoft Edge', 'Microsoft Edge', '', 'Microsoft Edge', 'Settings', 'Settings', 'Microsoft Store', 'Microsoft Store', '', '', 'Backup and Sync', 'Google Hangouts - asweigart@gmail.com', 'Downloads', '', '', 'Program Manager')
|
||||
>>> gw.getAllWindows()
|
||||
(Win32Window(hWnd=131318), Win32Window(hWnd=1050492), Win32Window(hWnd=67206), Win32Window(hWnd=66754), Win32Window(hWnd=264354), Win32Window(hWnd=329210), Win32Window(hWnd=1114374), Win32Window(hWnd=852550), Win32Window(hWnd=328358), Win32Window(hWnd=66998), Win32Window(hWnd=132508), Win32Window(hWnd=66964), Win32Window(hWnd=66882), Win32Window(hWnd=197282), Win32Window(hWnd=393880), Win32Window(hWnd=66810), Win32Window(hWnd=328466), Win32Window(hWnd=132332), Win32Window(hWnd=262904), Win32Window(hWnd=65962), Win32Window(hWnd=65956), Win32Window(hWnd=197522), Win32Window(hWnd=131944), Win32Window(hWnd=329334), Win32Window(hWnd=395034), Win32Window(hWnd=132928), Win32Window(hWnd=65882))
|
||||
>>> gw.getWindowsWithTitle('Untitled')
|
||||
(Win32Window(hWnd=264354),)
|
||||
>>> gw.getFocusedWindow()
|
||||
Win32Window(hWnd=1050492)
|
||||
>>> gw.getFocusedWindow().title
|
||||
'C:\\WINDOWS\\system32\\cmd.exe - pipenv shell - python'
|
||||
>>> gw.getWindowsAt(10, 10)
|
||||
(Win32Window(hWnd=67206), Win32Window(hWnd=66754), Win32Window(hWnd=329210), Win32Window(hWnd=1114374), Win32Window(hWnd=852550), Win32Window(hWnd=132508), Win32Window(hWnd=66964), Win32Window(hWnd=66882), Win32Window(hWnd=197282), Win32Window(hWnd=393880), Win32Window(hWnd=66810), Win32Window(hWnd=328466), Win32Window(hWnd=395034), Win32Window(hWnd=132928), Win32Window(hWnd=65882))
|
||||
|
||||
|
||||
``Window`` objects can be minimized/maximized/restored/focused/resized/moved/closed and also have attributes for their current position, size, and state.
|
||||
|
||||
>>> notepadWindow = gw.getWindowsWithTitle('Untitled')[0]
|
||||
>>> notepadWindow.isMaximized
|
||||
False
|
||||
>>> notepadWindow.maximize()
|
||||
>>> notepadWindow.isMaximized
|
||||
True
|
||||
>>> notepadWindow.restore()
|
||||
>>> notepadWindow.minimize()
|
||||
>>> notepadWindow.restore()
|
||||
>>> notepadWindow.focus()
|
||||
>>> notepadWindow.resize(10, 10) # increase by 10, 10
|
||||
>>> notepadWindow.resizeTo(100, 100) # set size to 100x100
|
||||
>>> notepadWindow.move(10, 10) # move 10 pixels right and 10 down
|
||||
>>> notepadWindow.moveTo(10, 10) # move window to 10, 10
|
||||
>>> notepadWindow.size
|
||||
(132, 100)
|
||||
>>> notepadWindow.width
|
||||
132
|
||||
>>> notepadWindow.height
|
||||
100
|
||||
>>> notepadWindow.topleft
|
||||
(10, 10)
|
||||
>>> notepadWindow.top
|
||||
10
|
||||
>>> notepadWindow.left
|
||||
10
|
||||
>>> notepadWindow.bottomright
|
||||
(142, 110)
|
||||
>>> notepadWindow.close()
|
||||
>>>
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
PyGetWindow-0.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyGetWindow-0.0.5.dist-info/METADATA,sha256=70cWMr8QUNChH2OwlIYwRKwGkMhXsrNwQh3FWK3PciY,5094
|
||||
PyGetWindow-0.0.5.dist-info/RECORD,,
|
||||
PyGetWindow-0.0.5.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98
|
||||
PyGetWindow-0.0.5.dist-info/top_level.txt,sha256=WxTIgZWkYIMB1QQE7SO_vCL5q-sSKu5Fv0rJ0U7pWZk,12
|
||||
pygetwindow/__init__.py,sha256=Oaj5OilD9O6OP0BoAfivUdBOvkmPKFXAZ5vyIvlhw3I,1730
|
||||
pygetwindow/__pycache__/__init__.cpython-37.pyc,,
|
||||
pygetwindow/__pycache__/_pygetwindow_macos.cpython-37.pyc,,
|
||||
pygetwindow/__pycache__/_pygetwindow_win.cpython-37.pyc,,
|
||||
pygetwindow/__pycache__/foo.cpython-37.pyc,,
|
||||
pygetwindow/_pygetwindow_macos.py,sha256=P_JfzLkZNzDtOvpjdFLwngS_YWlWqZd0clpLyTH32OE,12976
|
||||
pygetwindow/_pygetwindow_win.py,sha256=evD-OUlMQTUb6S96--LlzvGnHaZSNUBeAT8R6F4u7IM,17937
|
||||
pygetwindow/foo.py,sha256=sYZ4q36T_IhSQrPKUdfW-kXMcJ_j4yHkxi9S2fRvpvU,8
|
@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: cp37-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pygetwindow
|
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,31 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyMsgBox
|
||||
Version: 1.0.6
|
||||
Summary: A simple, cross-platform, pure Python module for JavaScript-like message boxes.
|
||||
Home-page: https://github.com/asweigart/PyMsgBox
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: gui msgbox message box dialog confirmation confirm password alert
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
|
||||
UNKNOWN
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
PyMsgBox-1.0.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyMsgBox-1.0.6.dist-info/METADATA,sha256=ecN1wrniIim5bOm7r8WTwWhjn_LFHwfK6HFjJHJgYDU,1178
|
||||
PyMsgBox-1.0.6.dist-info/RECORD,,
|
||||
PyMsgBox-1.0.6.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98
|
||||
PyMsgBox-1.0.6.dist-info/top_level.txt,sha256=VhumxHbRDo-rZSH_txL41DQO4DCaSDI0lz2AIhwUHbk,9
|
||||
pymsgbox/__init__.py,sha256=o_NGGc3p9hn3glO7X1QWq3ZUWZgLR_C8BUZa4aS40M0,13415
|
||||
pymsgbox/__pycache__/__init__.cpython-37.pyc,,
|
||||
pymsgbox/__pycache__/_native_java.cpython-37.pyc,,
|
||||
pymsgbox/__pycache__/_native_osx.cpython-37.pyc,,
|
||||
pymsgbox/__pycache__/_native_win.cpython-37.pyc,,
|
||||
pymsgbox/__pycache__/_native_x11.cpython-37.pyc,,
|
||||
pymsgbox/__pycache__/native.cpython-37.pyc,,
|
||||
pymsgbox/_native_java.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
pymsgbox/_native_osx.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
pymsgbox/_native_win.py,sha256=sbuilTAPOvGpn8wIklnxWzGuVbEfQeMKRqowyUVn5Ss,1982
|
||||
pymsgbox/_native_x11.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
pymsgbox/native.py,sha256=QfDD2vsE9pW2MkHhAnLtg6yW62VL2iwXRCREmDCJ56Q,862
|
@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: cp37-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pymsgbox
|
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,127 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyRect
|
||||
Version: 0.1.4
|
||||
Summary: PyRect is a simple module with a Rect class for Pygame-like rectangular areas.
|
||||
Home-page: https://github.com/asweigart/pyrect
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: pygame rect rectangular rectangle area
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
|
||||
======
|
||||
PyRect
|
||||
======
|
||||
PyRect is a simple module with a Rect class for Pygame-like rectangular areas.
|
||||
|
||||
This module is like a stand-alone version of Pygame's Rect class. It is similar to the Rect module by Simon Wittber, but compatible with both Python 2 and 3.
|
||||
|
||||
Currently under development, though the basic features work.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
``pip install rect``
|
||||
|
||||
Quickstart Guide
|
||||
================
|
||||
|
||||
First, create a Rect object by providing the XY coordinates of its top-left corner, and then the width and height:
|
||||
|
||||
>>> import pyrect
|
||||
>>> r = pyrect.Rect(0, 0, 10, 20)
|
||||
|
||||
There are several attributes that are automatically calculated (they have the same names as Pygame's Rect objects):
|
||||
|
||||
>>> r.width, r.height, r.size
|
||||
(10, 20, (10, 20))
|
||||
>>> r. left
|
||||
0
|
||||
>>> r.right
|
||||
10
|
||||
>>> r.top
|
||||
0
|
||||
>>> r.bottom
|
||||
20
|
||||
>>> r.center
|
||||
(5, 10)
|
||||
>>> r.topleft
|
||||
(0, 0)
|
||||
>>> r.topright
|
||||
(10, 0)
|
||||
>>> r.midleft
|
||||
(0, 10)
|
||||
|
||||
Changing these attributes re-calculates the others. The top-left corner is anchored for any growing or shrinking that takes place.
|
||||
|
||||
>>> r.topleft
|
||||
(0, 0)
|
||||
>>> r.left = 100
|
||||
>>> r.topleft
|
||||
(100, 0)
|
||||
>>> r.topright
|
||||
(110, 0)
|
||||
>>> r.width = 30
|
||||
>>> r.topright
|
||||
(130, 0)
|
||||
|
||||
Rect objects are locked to integers, unless you set `enableFloat` to `True`:
|
||||
|
||||
>>> r = pyrect.Rect(0, 0, 10, 20)
|
||||
>>> r.width = 10.5
|
||||
>>> r.width
|
||||
10
|
||||
>>> r.enableFloat = True
|
||||
>>> r.width = 10.5
|
||||
>>> r.width
|
||||
10.5
|
||||
>>> r2 = pyrect.Rect(0, 0, 10.5, 20.5, enableFloat=True)
|
||||
>>> r2.size
|
||||
(10.5, 20.5)
|
||||
|
||||
Rect Attributes
|
||||
===============
|
||||
|
||||
Rect objects have several attributes that can be read or modified. They are identical to Pygame's Rect objects:
|
||||
|
||||
``x, y``
|
||||
|
||||
``top, left, bottom, right``
|
||||
|
||||
``topleft, bottomleft, topright, bottomright``
|
||||
|
||||
``midtop, midleft, midbottom, midright``
|
||||
|
||||
``center, centerx, centery``
|
||||
|
||||
``size, width, height``
|
||||
|
||||
``w, h``
|
||||
|
||||
There are a couple other attributes as well:
|
||||
|
||||
``box (a tuple (left, top, width, height))``
|
||||
|
||||
``area (read-only)``
|
||||
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
PyRect-0.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyRect-0.1.4.dist-info/METADATA,sha256=qviGiMu-arb3aBayzBqGbFt3OUPfnLnWtLgWbr6IP3k,3325
|
||||
PyRect-0.1.4.dist-info/RECORD,,
|
||||
PyRect-0.1.4.dist-info/WHEEL,sha256=05Ahrak1u2K61DpLp9CDNSbUImLTITCMom_cqTFk6pE,116
|
||||
PyRect-0.1.4.dist-info/top_level.txt,sha256=zRKhS2KGlJmow_PYupJ21SKCmAyorrFofAC7QGsXt5g,7
|
||||
pyrect/__init__.py,sha256=pRHUn6NZRI8NJtM7J5vjuBN3RpfnQcp7547xybl17mI,47934
|
||||
pyrect/__pycache__/__init__.cpython-37.pyc,,
|
@ -0,0 +1,6 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pyrect
|
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014, Al Sweigart
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of PyScreeze nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,156 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyScreeze
|
||||
Version: 0.1.21
|
||||
Summary: A simple, cross-platform screenshot module for Python 2 and 3.
|
||||
Home-page: https://github.com/asweigart/pyscreeze
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: screenshot screen screencap capture scrot screencapture image
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 3 - Alpha
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Requires-Dist: Pillow
|
||||
|
||||
PyScreeze
|
||||
=========
|
||||
|
||||
PyScreeze is a simple, cross-platform screenshot module for Python 2 and 3.
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
PyScreeze can take screenshots, save them to files, and locate images within the screen. This is useful if you have a small image of, say, a button that needs to be clicked and want to locate it on the screen.
|
||||
|
||||
Screenshot functionality requires the Pillow module. OS X uses the `screencapture` command, which comes with the operating system. Linux uses the `scrot` command, which can be installed by running `sudo apt-get install scrot`.
|
||||
|
||||
Special Notes About Ubuntu
|
||||
==========================
|
||||
|
||||
Unfortunately, Ubuntu seems to have several deficiencies with installing Pillow. PNG and JPEG support are not included with Pillow out of the box on Ubuntu. The following links have more information
|
||||
|
||||
The screenshot() Function
|
||||
=========================
|
||||
|
||||
Calling `screenshot()` will return an Image object (see the Pillow or PIL module documentation for details). Passing a string of a filename will save the screenshot to a file as well as return it as an Image object.
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> im1 = pyscreeze.screenshot()
|
||||
>>> im2 = pyscreeze.screenshot('my_screenshot.png')
|
||||
|
||||
On a 1920 x 1080 screen, the `screenshot()` function takes roughly 100 milliseconds - it's not fast but it's not slow.
|
||||
|
||||
There is also an optional `region` keyword argument, if you do not want a screenshot of the entire screen. You can pass a four-integer tuple of the left, top, width, and height of the region to capture:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> im = pyscreeze.screenshot(region=(0,0, 300, 400))
|
||||
|
||||
The Locate Functions
|
||||
====================
|
||||
|
||||
You can visually locate something on the screen if you have an image file of it. You can call the `locateOnScreen('calc7key.png')` function to get the screen coordinates of the 7 button for a calculator app. The return value is a 4-integer tuple: (left, top, width, height). This tuple can be passed to `center()` to get the X and Y coordinates at the center of this region. If the image can't be found on the screen, `locateOnScreen()` returns `None`.
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> button7location = pyscreeze.locateOnScreen('calc7key.png')
|
||||
>>> button7location
|
||||
(1416, 562, 50, 41)
|
||||
>>> button7x, button7y = pyscreeze.center(button7location)
|
||||
>>> button7x, button7y
|
||||
(1441, 582)
|
||||
>>> pyscreeze.click(button7x, button7y) # clicks the center of where the 7 button was found
|
||||
|
||||
The `locateCenterOnScreen()` function is probably the one you want to use most often:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> x, y = pyscreeze.locateCenterOnScreen('calc7key.png')
|
||||
>>> pyscreeze.click(x, y)
|
||||
|
||||
On a 1920 x 1080 screen, the locate function calls take about 1 or 2 seconds. This may be too slow for action video games, but works for most purposes and applications.
|
||||
|
||||
If speed is important, install the optional opencv library (`pip install cv2`). The `locateAll` computation will use it if available, and take less than 1 millisecond to find all matches in a full-screen search. (This does not include the time required to capture a screenshot.)
|
||||
|
||||
There are several "locate" functions. They all start looking at the top-left corner of the screen (or image) and look to the left and then down. The arguments can either be a
|
||||
|
||||
- `locateOnScreen(image, grayscale=False)` - Returns (left, top, width, height) coordinate of first found instance of the `image` on the screen. Returns None if not found on the screen.
|
||||
|
||||
- `locateCenterOnScreen(image, grayscale=False)` - Returns (x, y) coordinates of the center of the first found instance of the `image` on the screen. Returns None if not found on the screen.
|
||||
|
||||
- `locateAllOnScreen(image, grayscale=False)` - Returns a generator that yields (left, top, width, height) tuples for where the image is found on the screen.
|
||||
|
||||
- `locate(needleImage, haystackImage, grayscale=False)` - Returns (left, top, width, height) coordinate of first found instance of `needleImage` in `haystackImage`. Returns None if not found on the screen.
|
||||
|
||||
- `locateAll(needleImage, haystackImage, grayscale=False)` - Returns a generator that yields (left, top, width, height) tuples for where `needleImage` is found in `haystackImage`.
|
||||
|
||||
The "locate all" functions can be used in for loops or passed to `list()`:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> for pos in pyscreeze.locateAllOnScreen('someButton.png')
|
||||
... print(pos)
|
||||
...
|
||||
(1101, 252, 50, 50)
|
||||
(59, 481, 50, 50)
|
||||
(1395, 640, 50, 50)
|
||||
(1838, 676, 50, 50)
|
||||
>>> list(pyscreeze.locateAllOnScreen('someButton.png'))
|
||||
[(1101, 252, 50, 50), (59, 481, 50, 50), (1395, 640, 50, 50), (1838, 676, 50, 50)]
|
||||
|
||||
Grayscale Matching
|
||||
------------------
|
||||
|
||||
Optionally, you can pass `grayscale=True` to the locate functions to give a slight speedup (about 30%-ish). This desaturates the color from the images and screenshots, speeding up the locating but potentially causing false-positive matches.
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> button7location = pyscreeze.locateOnScreen('calc7key.png', grayscale=True)
|
||||
>>> button7location
|
||||
(1416, 562, 50, 41)
|
||||
|
||||
Pixel Matching
|
||||
--------------
|
||||
|
||||
To obtain the RGB color of a pixel in a screenshot, use the Image object's `getpixel()` method:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> im = pyscreeze.screenshot()
|
||||
>>> im.getpixel((100, 200))
|
||||
(130, 135, 144)
|
||||
|
||||
Or as a single function, call the `pixel()` PyScreeze function, which is a wrapper for the previous calls:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> pyscreeze.pixel(100, 200)
|
||||
(130, 135, 144)
|
||||
|
||||
If you just need to verify that a single pixel matches a given pixel, call the `pixelMatchesColor()` function, passing it the X coordinate, Y coordinate, and RGB tuple of the color it represents:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> pyscreeze.pixelMatchesColor(100, 200, (130, 135, 144))
|
||||
True
|
||||
>>> pyscreeze.pixelMatchesColor(100, 200, (0, 0, 0))
|
||||
False
|
||||
|
||||
The optional `tolerance` keyword argument specifies how much each of the red, green, and blue values can vary while still matching:
|
||||
|
||||
>>> import pyscreeze
|
||||
>>> pyscreeze.pixelMatchesColor(100, 200, (130, 135, 144))
|
||||
True
|
||||
>>> pyscreeze.pixelMatchesColor(100, 200, (140, 125, 134))
|
||||
False
|
||||
>>> pyscreeze.pixelMatchesColor(100, 200, (140, 125, 134), tolerance=10)
|
||||
True
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
PyScreeze-0.1.21.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyScreeze-0.1.21.dist-info/LICENSE.txt,sha256=XcpFE3U9k1S0c5N8Taxr3a_LXnlT4mpnKf4eJu7EUXc,1505
|
||||
PyScreeze-0.1.21.dist-info/METADATA,sha256=wVTgkGZEKE6RHotcA5XYTXeIjzJy6mdY4o7DDVPqxFc,7418
|
||||
PyScreeze-0.1.21.dist-info/RECORD,,
|
||||
PyScreeze-0.1.21.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98
|
||||
PyScreeze-0.1.21.dist-info/top_level.txt,sha256=ExHe4LVkQVDYcMxwsJMTH01lHyk4GLzRxiPv4hGDRjA,10
|
||||
pyscreeze/__init__.py,sha256=s418376u9o_LTVgNb8awm8LyqWkGS7qkOufkS8N6Vic,20522
|
||||
pyscreeze/__pycache__/__init__.cpython-37.pyc,,
|
@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: cp37-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pyscreeze
|
@ -0,0 +1 @@
|
||||
pip
|
@ -0,0 +1,26 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: PyTweening
|
||||
Version: 1.0.3
|
||||
Summary: A collection of tweening / easing functions.
|
||||
Home-page: https://github.com/asweigart/pytweening
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: BSD
|
||||
Keywords: 2D animation tween tweening easing
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 3 - Alpha
|
||||
Classifier: Environment :: Win32 (MS Windows)
|
||||
Classifier: Environment :: X11 Applications
|
||||
Classifier: Environment :: MacOS X
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
|
||||
UNKNOWN
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
PyTweening-1.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyTweening-1.0.3.dist-info/METADATA,sha256=j2Mc5ESJwRYqGY5D_NbLOJiDsiVZ9RwSryRGKORkv1M,854
|
||||
PyTweening-1.0.3.dist-info/RECORD,,
|
||||
PyTweening-1.0.3.dist-info/WHEEL,sha256=JtBte-IW7C3UcYx3ZpZORq-KtnjVj4xdM4AJCTZPivc,98
|
||||
PyTweening-1.0.3.dist-info/top_level.txt,sha256=R8O2tKT6wD9sQ2kol4zYmcnqUOfgA9MMsswf3T9zRIo,11
|
||||
pytweening/__init__.py,sha256=IykpXFitgL3pv36qy-rFa8OQwhy5A-QK_FF1JCMPLTs,16939
|
||||
pytweening/__pycache__/__init__.cpython-37.pyc,,
|
@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: cp37-none-any
|
||||
|
@ -0,0 +1 @@
|
||||
pytweening
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,2 @@
|
||||
from . import displayMousePosition
|
||||
displayMousePosition()
|
@ -0,0 +1,442 @@
|
||||
import time
|
||||
import sys
|
||||
|
||||
try:
|
||||
import Quartz
|
||||
except:
|
||||
assert False, "You must first install pyobjc-core and pyobjc: https://pyautogui.readthedocs.io/en/latest/install.html"
|
||||
import AppKit
|
||||
|
||||
import pyautogui
|
||||
|
||||
|
||||
if sys.platform != 'darwin':
|
||||
raise Exception('The pyautogui_osx module should only be loaded on an OS X system.')
|
||||
|
||||
|
||||
|
||||
""" Taken from events.h
|
||||
/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
|
||||
|
||||
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
|
||||
keyUp(), or press() into the code used for the OS-specific keyboard function.
|
||||
|
||||
They should always be lowercase, and the same keys should be used across all OSes."""
|
||||
keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
|
||||
keyboardMapping.update({
|
||||
'a': 0x00, # kVK_ANSI_A
|
||||
's': 0x01, # kVK_ANSI_S
|
||||
'd': 0x02, # kVK_ANSI_D
|
||||
'f': 0x03, # kVK_ANSI_F
|
||||
'h': 0x04, # kVK_ANSI_H
|
||||
'g': 0x05, # kVK_ANSI_G
|
||||
'z': 0x06, # kVK_ANSI_Z
|
||||
'x': 0x07, # kVK_ANSI_X
|
||||
'c': 0x08, # kVK_ANSI_C
|
||||
'v': 0x09, # kVK_ANSI_V
|
||||
'b': 0x0b, # kVK_ANSI_B
|
||||
'q': 0x0c, # kVK_ANSI_Q
|
||||
'w': 0x0d, # kVK_ANSI_W
|
||||
'e': 0x0e, # kVK_ANSI_E
|
||||
'r': 0x0f, # kVK_ANSI_R
|
||||
'y': 0x10, # kVK_ANSI_Y
|
||||
't': 0x11, # kVK_ANSI_T
|
||||
'1': 0x12, # kVK_ANSI_1
|
||||
'!': 0x12, # kVK_ANSI_1
|
||||
'2': 0x13, # kVK_ANSI_2
|
||||
'@': 0x13, # kVK_ANSI_2
|
||||
'3': 0x14, # kVK_ANSI_3
|
||||
'#': 0x14, # kVK_ANSI_3
|
||||
'4': 0x15, # kVK_ANSI_4
|
||||
'$': 0x15, # kVK_ANSI_4
|
||||
'6': 0x16, # kVK_ANSI_6
|
||||
'^': 0x16, # kVK_ANSI_6
|
||||
'5': 0x17, # kVK_ANSI_5
|
||||
'%': 0x17, # kVK_ANSI_5
|
||||
'=': 0x18, # kVK_ANSI_Equal
|
||||
'+': 0x18, # kVK_ANSI_Equal
|
||||
'9': 0x19, # kVK_ANSI_9
|
||||
'(': 0x19, # kVK_ANSI_9
|
||||
'7': 0x1a, # kVK_ANSI_7
|
||||
'&': 0x1a, # kVK_ANSI_7
|
||||
'-': 0x1b, # kVK_ANSI_Minus
|
||||
'_': 0x1b, # kVK_ANSI_Minus
|
||||
'8': 0x1c, # kVK_ANSI_8
|
||||
'*': 0x1c, # kVK_ANSI_8
|
||||
'0': 0x1d, # kVK_ANSI_0
|
||||
')': 0x1d, # kVK_ANSI_0
|
||||
']': 0x1e, # kVK_ANSI_RightBracket
|
||||
'}': 0x1e, # kVK_ANSI_RightBracket
|
||||
'o': 0x1f, # kVK_ANSI_O
|
||||
'u': 0x20, # kVK_ANSI_U
|
||||
'[': 0x21, # kVK_ANSI_LeftBracket
|
||||
'{': 0x21, # kVK_ANSI_LeftBracket
|
||||
'i': 0x22, # kVK_ANSI_I
|
||||
'p': 0x23, # kVK_ANSI_P
|
||||
'l': 0x25, # kVK_ANSI_L
|
||||
'j': 0x26, # kVK_ANSI_J
|
||||
"'": 0x27, # kVK_ANSI_Quote
|
||||
'"': 0x27, # kVK_ANSI_Quote
|
||||
'k': 0x28, # kVK_ANSI_K
|
||||
';': 0x29, # kVK_ANSI_Semicolon
|
||||
':': 0x29, # kVK_ANSI_Semicolon
|
||||
'\\': 0x2a, # kVK_ANSI_Backslash
|
||||
'|': 0x2a, # kVK_ANSI_Backslash
|
||||
',': 0x2b, # kVK_ANSI_Comma
|
||||
'<': 0x2b, # kVK_ANSI_Comma
|
||||
'/': 0x2c, # kVK_ANSI_Slash
|
||||
'?': 0x2c, # kVK_ANSI_Slash
|
||||
'n': 0x2d, # kVK_ANSI_N
|
||||
'm': 0x2e, # kVK_ANSI_M
|
||||
'.': 0x2f, # kVK_ANSI_Period
|
||||
'>': 0x2f, # kVK_ANSI_Period
|
||||
'`': 0x32, # kVK_ANSI_Grave
|
||||
'~': 0x32, # kVK_ANSI_Grave
|
||||
' ': 0x31, # kVK_Space
|
||||
'space': 0x31,
|
||||
'\r': 0x24, # kVK_Return
|
||||
'\n': 0x24, # kVK_Return
|
||||
'enter': 0x24, # kVK_Return
|
||||
'return': 0x24, # kVK_Return
|
||||
'\t': 0x30, # kVK_Tab
|
||||
'tab': 0x30, # kVK_Tab
|
||||
'backspace': 0x33, # kVK_Delete, which is "Backspace" on OS X.
|
||||
'\b': 0x33, # kVK_Delete, which is "Backspace" on OS X.
|
||||
'esc': 0x35, # kVK_Escape
|
||||
'escape': 0x35, # kVK_Escape
|
||||
'command': 0x37, # kVK_Command
|
||||
'shift': 0x38, # kVK_Shift
|
||||
'shiftleft': 0x38, # kVK_Shift
|
||||
'capslock': 0x39, # kVK_CapsLock
|
||||
'option': 0x3a, # kVK_Option
|
||||
'optionleft': 0x3a, # kVK_Option
|
||||
'alt': 0x3a, # kVK_Option
|
||||
'altleft': 0x3a, # kVK_Option
|
||||
'ctrl': 0x3b, # kVK_Control
|
||||
'ctrlleft': 0x3b, # kVK_Control
|
||||
'shiftright': 0x3c, # kVK_RightShift
|
||||
'optionright': 0x3d, # kVK_RightOption
|
||||
'ctrlright': 0x3e, # kVK_RightControl
|
||||
'fn': 0x3f, # kVK_Function
|
||||
'f17': 0x40, # kVK_F17
|
||||
'volumeup': 0x48, # kVK_VolumeUp
|
||||
'volumedown': 0x49, # kVK_VolumeDown
|
||||
'volumemute': 0x4a, # kVK_Mute
|
||||
'f18': 0x4f, # kVK_F18
|
||||
'f19': 0x50, # kVK_F19
|
||||
'f20': 0x5a, # kVK_F20
|
||||
'f5': 0x60, # kVK_F5
|
||||
'f6': 0x61, # kVK_F6
|
||||
'f7': 0x62, # kVK_F7
|
||||
'f3': 0x63, # kVK_F3
|
||||
'f8': 0x64, # kVK_F8
|
||||
'f9': 0x65, # kVK_F9
|
||||
'f11': 0x67, # kVK_F11
|
||||
'f13': 0x69, # kVK_F13
|
||||
'f16': 0x6a, # kVK_F16
|
||||
'f14': 0x6b, # kVK_F14
|
||||
'f10': 0x6d, # kVK_F10
|
||||
'f12': 0x6f, # kVK_F12
|
||||
'f15': 0x71, # kVK_F15
|
||||
'help': 0x72, # kVK_Help
|
||||
'home': 0x73, # kVK_Home
|
||||
'pageup': 0x74, # kVK_PageUp
|
||||
'pgup': 0x74, # kVK_PageUp
|
||||
'del': 0x75, # kVK_ForwardDelete
|
||||
'delete': 0x75, # kVK_ForwardDelete
|
||||
'f4': 0x76, # kVK_F4
|
||||
'end': 0x77, # kVK_End
|
||||
'f2': 0x78, # kVK_F2
|
||||
'pagedown': 0x79, # kVK_PageDown
|
||||
'pgdn': 0x79, # kVK_PageDown
|
||||
'f1': 0x7a, # kVK_F1
|
||||
'left': 0x7b, # kVK_LeftArrow
|
||||
'right': 0x7c, # kVK_RightArrow
|
||||
'down': 0x7d, # kVK_DownArrow
|
||||
'up': 0x7e, # kVK_UpArrow
|
||||
'yen': 0x5d, # kVK_JIS_Yen
|
||||
#'underscore' : 0x5e, # kVK_JIS_Underscore (only applies to Japanese keyboards)
|
||||
#'comma': 0x5f, # kVK_JIS_KeypadComma (only applies to Japanese keyboards)
|
||||
'eisu': 0x66, # kVK_JIS_Eisu
|
||||
'kana': 0x68, # kVK_JIS_Kana
|
||||
})
|
||||
|
||||
"""
|
||||
# TODO - additional key codes to add
|
||||
kVK_ANSI_KeypadDecimal = 0x41,
|
||||
kVK_ANSI_KeypadMultiply = 0x43,
|
||||
kVK_ANSI_KeypadPlus = 0x45,
|
||||
kVK_ANSI_KeypadClear = 0x47,
|
||||
kVK_ANSI_KeypadDivide = 0x4B,
|
||||
kVK_ANSI_KeypadEnter = 0x4C,
|
||||
kVK_ANSI_KeypadMinus = 0x4E,
|
||||
kVK_ANSI_KeypadEquals = 0x51,
|
||||
kVK_ANSI_Keypad0 = 0x52,
|
||||
kVK_ANSI_Keypad1 = 0x53,
|
||||
kVK_ANSI_Keypad2 = 0x54,
|
||||
kVK_ANSI_Keypad3 = 0x55,
|
||||
kVK_ANSI_Keypad4 = 0x56,
|
||||
kVK_ANSI_Keypad5 = 0x57,
|
||||
kVK_ANSI_Keypad6 = 0x58,
|
||||
kVK_ANSI_Keypad7 = 0x59,
|
||||
kVK_ANSI_Keypad8 = 0x5B,
|
||||
kVK_ANSI_Keypad9 = 0x5C,
|
||||
"""
|
||||
|
||||
# add mappings for uppercase letters
|
||||
for c in 'abcdefghijklmnopqrstuvwxyz':
|
||||
keyboardMapping[c.upper()] = keyboardMapping[c]
|
||||
|
||||
# Taken from ev_keymap.h
|
||||
# http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86.1/IOHIDSystem/IOKit/hidsystem/ev_keymap.h
|
||||
special_key_translate_table = {
|
||||
'KEYTYPE_SOUND_UP': 0,
|
||||
'KEYTYPE_SOUND_DOWN': 1,
|
||||
'KEYTYPE_BRIGHTNESS_UP': 2,
|
||||
'KEYTYPE_BRIGHTNESS_DOWN': 3,
|
||||
'KEYTYPE_CAPS_LOCK': 4,
|
||||
'KEYTYPE_HELP': 5,
|
||||
'POWER_KEY': 6,
|
||||
'KEYTYPE_MUTE': 7,
|
||||
'UP_ARROW_KEY': 8,
|
||||
'DOWN_ARROW_KEY': 9,
|
||||
'KEYTYPE_NUM_LOCK': 10,
|
||||
'KEYTYPE_CONTRAST_UP': 11,
|
||||
'KEYTYPE_CONTRAST_DOWN': 12,
|
||||
'KEYTYPE_LAUNCH_PANEL': 13,
|
||||
'KEYTYPE_EJECT': 14,
|
||||
'KEYTYPE_VIDMIRROR': 15,
|
||||
'KEYTYPE_PLAY': 16,
|
||||
'KEYTYPE_NEXT': 17,
|
||||
'KEYTYPE_PREVIOUS': 18,
|
||||
'KEYTYPE_FAST': 19,
|
||||
'KEYTYPE_REWIND': 20,
|
||||
'KEYTYPE_ILLUMINATION_UP': 21,
|
||||
'KEYTYPE_ILLUMINATION_DOWN': 22,
|
||||
'KEYTYPE_ILLUMINATION_TOGGLE': 23
|
||||
}
|
||||
|
||||
def _keyDown(key):
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
if key in special_key_translate_table:
|
||||
_specialKeyEvent(key, 'down')
|
||||
else:
|
||||
_normalKeyEvent(key, 'down')
|
||||
|
||||
def _keyUp(key):
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
if key in special_key_translate_table:
|
||||
_specialKeyEvent(key, 'up')
|
||||
else:
|
||||
_normalKeyEvent(key, 'up')
|
||||
|
||||
|
||||
def _normalKeyEvent(key, upDown):
|
||||
assert upDown in ('up', 'down'), "upDown argument must be 'up' or 'down'"
|
||||
|
||||
try:
|
||||
if pyautogui.isShiftCharacter(key):
|
||||
key_code = keyboardMapping[key.lower()]
|
||||
|
||||
event = Quartz.CGEventCreateKeyboardEvent(None,
|
||||
keyboardMapping['shift'], upDown == 'down')
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
|
||||
# Tiny sleep to let OS X catch up on us pressing shift
|
||||
time.sleep(0.01)
|
||||
|
||||
else:
|
||||
key_code = keyboardMapping[key]
|
||||
|
||||
event = Quartz.CGEventCreateKeyboardEvent(None, key_code, upDown == 'down')
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
|
||||
time.sleep(0.01)
|
||||
|
||||
# TODO - wait, is the shift key's keyup not done?
|
||||
# TODO - get rid of this try-except.
|
||||
|
||||
except KeyError:
|
||||
raise RuntimeError("Key %s not implemented." % (key))
|
||||
|
||||
def _specialKeyEvent(key, upDown):
|
||||
""" Helper method for special keys.
|
||||
|
||||
Source: http://stackoverflow.com/questions/11045814/emulate-media-key-press-on-mac
|
||||
"""
|
||||
assert upDown in ('up', 'down'), "upDown argument must be 'up' or 'down'"
|
||||
|
||||
key_code = special_key_translate_table[key]
|
||||
|
||||
ev = AppKit.NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
||||
Quartz.NSSystemDefined, # type
|
||||
(0,0), # location
|
||||
0xa00 if upDown == 'down' else 0xb00, # flags
|
||||
0, # timestamp
|
||||
0, # window
|
||||
0, # ctx
|
||||
8, # subtype
|
||||
(key_code << 16) | ((0xa if upDown == 'down' else 0xb) << 8), # data1
|
||||
-1 # data2
|
||||
)
|
||||
|
||||
Quartz.CGEventPost(0, ev.CGEvent())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _position():
|
||||
loc = AppKit.NSEvent.mouseLocation()
|
||||
return int(loc.x), int(Quartz.CGDisplayPixelsHigh(0) - loc.y)
|
||||
|
||||
|
||||
def _size():
|
||||
return Quartz.CGDisplayPixelsWide(Quartz.CGMainDisplayID()), Quartz.CGDisplayPixelsHigh(Quartz.CGMainDisplayID())
|
||||
|
||||
|
||||
|
||||
def _scroll(clicks, x=None, y=None):
|
||||
_vscroll(clicks, x, y)
|
||||
|
||||
|
||||
"""
|
||||
According to https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/Quartz.CGEventCreateScrollWheelEvent
|
||||
"Scrolling movement is generally represented by small signed integer values, typically in a range from -10 to +10. Large values may have unexpected results, depending on the application that processes the event."
|
||||
The scrolling functions will create multiple events that scroll 10 each, and then scroll the remainder.
|
||||
"""
|
||||
|
||||
def _vscroll(clicks, x=None, y=None):
|
||||
_moveTo(x, y)
|
||||
clicks = int(clicks)
|
||||
for _ in range(abs(clicks) // 10):
|
||||
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
|
||||
None, # no source
|
||||
Quartz.kCGScrollEventUnitLine, # units
|
||||
1, # wheelCount (number of dimensions)
|
||||
10 if clicks >= 0 else -10) # vertical movement
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
|
||||
|
||||
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
|
||||
None, # no source
|
||||
Quartz.kCGScrollEventUnitLine, # units
|
||||
1, # wheelCount (number of dimensions)
|
||||
clicks % 10 if clicks >= 0 else -1 * (-clicks % 10)) # vertical movement
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
|
||||
|
||||
|
||||
def _hscroll(clicks, x=None, y=None):
|
||||
_moveTo(x, y)
|
||||
clicks = int(clicks)
|
||||
for _ in range(abs(clicks) // 10):
|
||||
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
|
||||
None, # no source
|
||||
Quartz.kCGScrollEventUnitLine, # units
|
||||
2, # wheelCount (number of dimensions)
|
||||
0, # vertical movement
|
||||
10 if clicks >= 0 else -10) # horizontal movement
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
|
||||
|
||||
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
|
||||
None, # no source
|
||||
Quartz.kCGScrollEventUnitLine, # units
|
||||
2, # wheelCount (number of dimensions)
|
||||
0, # vertical movement
|
||||
(clicks % 10) if clicks >= 0 else (-1 * clicks % 10)) # horizontal movement
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
|
||||
|
||||
|
||||
def _mouseDown(x, y, button):
|
||||
if button == 'left':
|
||||
_sendMouseEvent(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft)
|
||||
elif button == 'middle':
|
||||
_sendMouseEvent(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter)
|
||||
elif button == 'right':
|
||||
_sendMouseEvent(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight)
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
|
||||
def _mouseUp(x, y, button):
|
||||
if button == 'left':
|
||||
_sendMouseEvent(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft)
|
||||
elif button == 'middle':
|
||||
_sendMouseEvent(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter)
|
||||
elif button == 'right':
|
||||
_sendMouseEvent(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight)
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
|
||||
def _click(x, y, button):
|
||||
if button == 'left':
|
||||
_sendMouseEvent(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft)
|
||||
_sendMouseEvent(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft)
|
||||
elif button == 'middle':
|
||||
_sendMouseEvent(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter)
|
||||
_sendMouseEvent(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter)
|
||||
elif button == 'right':
|
||||
_sendMouseEvent(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight)
|
||||
_sendMouseEvent(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight)
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
def _multiClick(x, y, button, num):
|
||||
btn = None
|
||||
down = None
|
||||
up = None
|
||||
|
||||
if button == 'left':
|
||||
btn = Quartz.kCGMouseButtonLeft
|
||||
down = Quartz.kCGEventLeftMouseDown
|
||||
up = Quartz.kCGEventLeftMouseUp
|
||||
elif button == 'middle':
|
||||
btn = Quartz.kCGMouseButtonCenter
|
||||
down = Quartz.kCGEventOtherMouseDown
|
||||
up = Quartz.kCGEventOtherMouseUp
|
||||
elif button == 'right':
|
||||
btn = Quartz.kCGMouseButtonRight
|
||||
down = Quartz.kCGEventRightMouseDown
|
||||
up = Quartz.kCGEventRightMouseUp
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
return
|
||||
|
||||
mouseEvent = Quartz.CGEventCreateMouseEvent(None, down, (x, y), btn)
|
||||
Quartz.CGEventSetIntegerValueField(mouseEvent, Quartz.kCGMouseEventClickState, num)
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
|
||||
Quartz.CGEventSetType(mouseEvent, up)
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
|
||||
for i in range(0, num-1):
|
||||
Quartz.CGEventSetType(mouseEvent, down)
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
|
||||
Quartz.CGEventSetType(mouseEvent, up)
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
|
||||
|
||||
|
||||
def _sendMouseEvent(ev, x, y, button):
|
||||
mouseEvent = Quartz.CGEventCreateMouseEvent(None, ev, (x, y), button)
|
||||
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
|
||||
|
||||
|
||||
def _dragTo(x, y, button):
|
||||
if button == 'left':
|
||||
_sendMouseEvent(Quartz.kCGEventLeftMouseDragged , x, y, Quartz.kCGMouseButtonLeft)
|
||||
elif button == 'middle':
|
||||
_sendMouseEvent(Quartz.kCGEventOtherMouseDragged , x, y, Quartz.kCGMouseButtonCenter)
|
||||
elif button == 'right':
|
||||
_sendMouseEvent(Quartz.kCGEventRightMouseDragged , x, y, Quartz.kCGMouseButtonRight)
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
time.sleep(0.01) # needed to allow OS time to catch up.
|
||||
|
||||
def _moveTo(x, y):
|
||||
_sendMouseEvent(Quartz.kCGEventMouseMoved, x, y, 0)
|
||||
time.sleep(0.01) # needed to allow OS time to catch up.
|
||||
|
@ -0,0 +1,586 @@
|
||||
# Windows implementation of PyAutoGUI functions.
|
||||
# BSD license
|
||||
# Al Sweigart al@inventwithpython.com
|
||||
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
import pyautogui
|
||||
|
||||
import sys
|
||||
if sys.platform != 'win32':
|
||||
raise Exception('The pyautogui_win module should only be loaded on a Windows system.')
|
||||
|
||||
|
||||
# Fixes the scaling issues where PyAutoGUI was reporting the wrong resolution:
|
||||
try:
|
||||
ctypes.windll.user32.SetProcessDPIAware()
|
||||
except AttributeError:
|
||||
pass # Windows XP doesn't support this, so just do nothing.
|
||||
|
||||
|
||||
"""
|
||||
A lot of this code is probably repeated from win32 extensions module, but I didn't want to have that dependency.
|
||||
|
||||
Note: According to http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
|
||||
the ctypes.windll.user32.mouse_event() function has been superceded by SendInput.
|
||||
|
||||
SendInput() is documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
|
||||
|
||||
UPDATE: SendInput() doesn't seem to be working for me. I've switched back to mouse_event()."""
|
||||
|
||||
|
||||
# Event codes to be passed to the mouse_event() win32 function.
|
||||
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273(v=vs.85).aspx
|
||||
MOUSEEVENTF_LEFTDOWN = 0x0002
|
||||
MOUSEEVENTF_LEFTUP = 0x0004
|
||||
MOUSEEVENTF_LEFTCLICK = MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP
|
||||
MOUSEEVENTF_RIGHTDOWN = 0x0008
|
||||
MOUSEEVENTF_RIGHTUP = 0x0010
|
||||
MOUSEEVENTF_RIGHTCLICK = MOUSEEVENTF_RIGHTDOWN + MOUSEEVENTF_RIGHTUP
|
||||
MOUSEEVENTF_MIDDLEDOWN = 0x0020
|
||||
MOUSEEVENTF_MIDDLEUP = 0x0040
|
||||
MOUSEEVENTF_MIDDLECLICK = MOUSEEVENTF_MIDDLEDOWN + MOUSEEVENTF_MIDDLEUP
|
||||
|
||||
MOUSEEVENTF_WHEEL = 0x0800
|
||||
MOUSEEVENTF_HWHEEL = 0x01000
|
||||
|
||||
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx
|
||||
KEYEVENTF_KEYUP = 0x0002
|
||||
|
||||
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
|
||||
INPUT_MOUSE = 0
|
||||
INPUT_KEYBOARD = 1
|
||||
|
||||
|
||||
# This ctypes structure is for a Win32 POINT structure,
|
||||
# which is documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805(v=vs.85).aspx
|
||||
# The POINT structure is used by GetCursorPos().
|
||||
class POINT(ctypes.Structure):
|
||||
_fields_ = [("x", ctypes.c_long),
|
||||
("y", ctypes.c_long)]
|
||||
|
||||
# These ctypes structures are for Win32 INPUT, MOUSEINPUT, KEYBDINPUT, and HARDWAREINPUT structures,
|
||||
# used by SendInput and documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
|
||||
# Thanks to BSH for this StackOverflow answer: https://stackoverflow.com/questions/18566289/how-would-you-recreate-this-windows-api-structure-with-ctypes
|
||||
class MOUSEINPUT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('dx', ctypes.wintypes.LONG),
|
||||
('dy', ctypes.wintypes.LONG),
|
||||
('mouseData', ctypes.wintypes.DWORD),
|
||||
('dwFlags', ctypes.wintypes.DWORD),
|
||||
('time', ctypes.wintypes.DWORD),
|
||||
('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
|
||||
]
|
||||
|
||||
class KEYBDINPUT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('wVk', ctypes.wintypes.WORD),
|
||||
('wScan', ctypes.wintypes.WORD),
|
||||
('dwFlags', ctypes.wintypes.DWORD),
|
||||
('time', ctypes.wintypes.DWORD),
|
||||
('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
|
||||
]
|
||||
|
||||
class HARDWAREINPUT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('uMsg', ctypes.wintypes.DWORD),
|
||||
('wParamL', ctypes.wintypes.WORD),
|
||||
('wParamH', ctypes.wintypes.DWORD)
|
||||
]
|
||||
|
||||
class INPUT(ctypes.Structure):
|
||||
class _I(ctypes.Union):
|
||||
_fields_ = [
|
||||
('mi', MOUSEINPUT),
|
||||
('ki', KEYBDINPUT),
|
||||
('hi', HARDWAREINPUT),
|
||||
]
|
||||
|
||||
_anonymous_ = ('i', )
|
||||
_fields_ = [
|
||||
('type', ctypes.wintypes.DWORD),
|
||||
('i', _I),
|
||||
]
|
||||
# End of the SendInput win32 data structures.
|
||||
|
||||
|
||||
|
||||
""" Keyboard key mapping for pyautogui:
|
||||
Documented at http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
|
||||
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
|
||||
keyUp(), or press() into the code used for the OS-specific keyboard function.
|
||||
|
||||
They should always be lowercase, and the same keys should be used across all OSes."""
|
||||
keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
|
||||
keyboardMapping.update({
|
||||
'backspace': 0x08, # VK_BACK
|
||||
'\b': 0x08, # VK_BACK
|
||||
'super': 0x5B, #VK_LWIN
|
||||
'tab': 0x09, # VK_TAB
|
||||
'\t': 0x09, # VK_TAB
|
||||
'clear': 0x0c, # VK_CLEAR
|
||||
'enter': 0x0d, # VK_RETURN
|
||||
'\n': 0x0d, # VK_RETURN
|
||||
'return': 0x0d, # VK_RETURN
|
||||
'shift': 0x10, # VK_SHIFT
|
||||
'ctrl': 0x11, # VK_CONTROL
|
||||
'alt': 0x12, # VK_MENU
|
||||
'pause': 0x13, # VK_PAUSE
|
||||
'capslock': 0x14, # VK_CAPITAL
|
||||
'kana': 0x15, # VK_KANA
|
||||
'hanguel': 0x15, # VK_HANGUEL
|
||||
'hangul': 0x15, # VK_HANGUL
|
||||
'junja': 0x17, # VK_JUNJA
|
||||
'final': 0x18, # VK_FINAL
|
||||
'hanja': 0x19, # VK_HANJA
|
||||
'kanji': 0x19, # VK_KANJI
|
||||
'esc': 0x1b, # VK_ESCAPE
|
||||
'escape': 0x1b, # VK_ESCAPE
|
||||
'convert': 0x1c, # VK_CONVERT
|
||||
'nonconvert': 0x1d, # VK_NONCONVERT
|
||||
'accept': 0x1e, # VK_ACCEPT
|
||||
'modechange': 0x1f, # VK_MODECHANGE
|
||||
' ': 0x20, # VK_SPACE
|
||||
'space': 0x20,
|
||||
'pgup': 0x21, # VK_PRIOR
|
||||
'pgdn': 0x22, # VK_NEXT
|
||||
'pageup': 0x21, # VK_PRIOR
|
||||
'pagedown': 0x22, # VK_NEXT
|
||||
'end': 0x23, # VK_END
|
||||
'home': 0x24, # VK_HOME
|
||||
'left': 0x25, # VK_LEFT
|
||||
'up': 0x26, # VK_UP
|
||||
'right': 0x27, # VK_RIGHT
|
||||
'down': 0x28, # VK_DOWN
|
||||
'select': 0x29, # VK_SELECT
|
||||
'print': 0x2a, # VK_PRINT
|
||||
'execute': 0x2b, # VK_EXECUTE
|
||||
'prtsc': 0x2c, # VK_SNAPSHOT
|
||||
'prtscr': 0x2c, # VK_SNAPSHOT
|
||||
'prntscrn': 0x2c, # VK_SNAPSHOT
|
||||
'printscreen': 0x2c, # VK_SNAPSHOT
|
||||
'insert': 0x2d, # VK_INSERT
|
||||
'del': 0x2e, # VK_DELETE
|
||||
'delete': 0x2e, # VK_DELETE
|
||||
'help': 0x2f, # VK_HELP
|
||||
'win': 0x5b, # VK_LWIN
|
||||
'winleft': 0x5b, # VK_LWIN
|
||||
'winright': 0x5c, # VK_RWIN
|
||||
'apps': 0x5d, # VK_APPS
|
||||
'sleep': 0x5f, # VK_SLEEP
|
||||
'num0': 0x60, # VK_NUMPAD0
|
||||
'num1': 0x61, # VK_NUMPAD1
|
||||
'num2': 0x62, # VK_NUMPAD2
|
||||
'num3': 0x63, # VK_NUMPAD3
|
||||
'num4': 0x64, # VK_NUMPAD4
|
||||
'num5': 0x65, # VK_NUMPAD5
|
||||
'num6': 0x66, # VK_NUMPAD6
|
||||
'num7': 0x67, # VK_NUMPAD7
|
||||
'num8': 0x68, # VK_NUMPAD8
|
||||
'num9': 0x69, # VK_NUMPAD9
|
||||
'multiply': 0x6a, # VK_MULTIPLY ??? Is this the numpad *?
|
||||
'add': 0x6b, # VK_ADD ??? Is this the numpad +?
|
||||
'separator': 0x6c, # VK_SEPARATOR ??? Is this the numpad enter?
|
||||
'subtract': 0x6d, # VK_SUBTRACT ??? Is this the numpad -?
|
||||
'decimal': 0x6e, # VK_DECIMAL
|
||||
'divide': 0x6f, # VK_DIVIDE
|
||||
'f1': 0x70, # VK_F1
|
||||
'f2': 0x71, # VK_F2
|
||||
'f3': 0x72, # VK_F3
|
||||
'f4': 0x73, # VK_F4
|
||||
'f5': 0x74, # VK_F5
|
||||
'f6': 0x75, # VK_F6
|
||||
'f7': 0x76, # VK_F7
|
||||
'f8': 0x77, # VK_F8
|
||||
'f9': 0x78, # VK_F9
|
||||
'f10': 0x79, # VK_F10
|
||||
'f11': 0x7a, # VK_F11
|
||||
'f12': 0x7b, # VK_F12
|
||||
'f13': 0x7c, # VK_F13
|
||||
'f14': 0x7d, # VK_F14
|
||||
'f15': 0x7e, # VK_F15
|
||||
'f16': 0x7f, # VK_F16
|
||||
'f17': 0x80, # VK_F17
|
||||
'f18': 0x81, # VK_F18
|
||||
'f19': 0x82, # VK_F19
|
||||
'f20': 0x83, # VK_F20
|
||||
'f21': 0x84, # VK_F21
|
||||
'f22': 0x85, # VK_F22
|
||||
'f23': 0x86, # VK_F23
|
||||
'f24': 0x87, # VK_F24
|
||||
'numlock': 0x90, # VK_NUMLOCK
|
||||
'scrolllock': 0x91, # VK_SCROLL
|
||||
'shiftleft': 0xa0, # VK_LSHIFT
|
||||
'shiftright': 0xa1, # VK_RSHIFT
|
||||
'ctrlleft': 0xa2, # VK_LCONTROL
|
||||
'ctrlright': 0xa3, # VK_RCONTROL
|
||||
'altleft': 0xa4, # VK_LMENU
|
||||
'altright': 0xa5, # VK_RMENU
|
||||
'browserback': 0xa6, # VK_BROWSER_BACK
|
||||
'browserforward': 0xa7, # VK_BROWSER_FORWARD
|
||||
'browserrefresh': 0xa8, # VK_BROWSER_REFRESH
|
||||
'browserstop': 0xa9, # VK_BROWSER_STOP
|
||||
'browsersearch': 0xaa, # VK_BROWSER_SEARCH
|
||||
'browserfavorites': 0xab, # VK_BROWSER_FAVORITES
|
||||
'browserhome': 0xac, # VK_BROWSER_HOME
|
||||
'volumemute': 0xad, # VK_VOLUME_MUTE
|
||||
'volumedown': 0xae, # VK_VOLUME_DOWN
|
||||
'volumeup': 0xaf, # VK_VOLUME_UP
|
||||
'nexttrack': 0xb0, # VK_MEDIA_NEXT_TRACK
|
||||
'prevtrack': 0xb1, # VK_MEDIA_PREV_TRACK
|
||||
'stop': 0xb2, # VK_MEDIA_STOP
|
||||
'playpause': 0xb3, # VK_MEDIA_PLAY_PAUSE
|
||||
'launchmail': 0xb4, # VK_LAUNCH_MAIL
|
||||
'launchmediaselect': 0xb5, # VK_LAUNCH_MEDIA_SELECT
|
||||
'launchapp1': 0xb6, # VK_LAUNCH_APP1
|
||||
'launchapp2': 0xb7, # VK_LAUNCH_APP2
|
||||
#';': 0xba, # VK_OEM_1
|
||||
#'+': 0xbb, # VK_OEM_PLUS
|
||||
#',': 0xbc, # VK_OEM_COMMA
|
||||
#'-': 0xbd, # VK_OEM_MINUS
|
||||
#'.': 0xbe, # VK_OEM_PERIOD
|
||||
#'/': 0xbf, # VK_OEM_2
|
||||
#'~': 0xc0, # VK_OEM_3
|
||||
#'[': 0xdb, # VK_OEM_4
|
||||
#'|': 0xdc, # VK_OEM_5
|
||||
#']': 0xdd, # VK_OEM_6
|
||||
#"'": 0xde, # VK_OEM_7
|
||||
#'': 0xdf, # VK_OEM_8
|
||||
#'': 0xe7, # VK_PACKET
|
||||
#'': 0xf6, # VK_ATTN
|
||||
#'': 0xf7, # VK_CRSEL
|
||||
#'': 0xf8, # VK_EXSEL
|
||||
#'': 0xf9, # VK_EREOF
|
||||
#'': 0xfa, # VK_PLAY
|
||||
#'': 0xfb, # VK_ZOOM
|
||||
#'': 0xfc, # VK_NONAME
|
||||
#'': 0xfd, # VK_PA1
|
||||
#'': 0xfe, # VK_OEM_CLEAR
|
||||
})
|
||||
|
||||
# Populate the basic printable ascii characters.
|
||||
for c in range(32, 128):
|
||||
keyboardMapping[chr(c)] = ctypes.windll.user32.VkKeyScanA(ctypes.wintypes.WCHAR(chr(c)))
|
||||
|
||||
|
||||
def _keyDown(key):
|
||||
"""Performs a keyboard key press without the release. This will put that
|
||||
key in a held down state.
|
||||
|
||||
NOTE: For some reason, this does not seem to cause key repeats like would
|
||||
happen if a keyboard key was held down on a text field.
|
||||
|
||||
Args:
|
||||
key (str): The key to be pressed down. The valid names are listed in
|
||||
pyautogui.KEY_NAMES.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
needsShift = pyautogui.isShiftCharacter(key)
|
||||
|
||||
"""
|
||||
# OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
|
||||
if key in keyboardMapping.keys():
|
||||
vkCode = keyboardMapping[key]
|
||||
elif len(key) == 1:
|
||||
# note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
|
||||
vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
|
||||
if vkCode == -1:
|
||||
raise ValueError('There is no VK code for key "%s"' % (key))
|
||||
if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
|
||||
vkCode -= 0x100
|
||||
needsShift = True
|
||||
"""
|
||||
mods, vkCode = divmod(keyboardMapping[key], 0x100)
|
||||
|
||||
for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
|
||||
(mods & 1 or needsShift, 0x10)]: #HANKAKU not suported! mods & 8
|
||||
if apply_mod:
|
||||
ctypes.windll.user32.keybd_event(vk_mod, 0, 0, 0) #
|
||||
ctypes.windll.user32.keybd_event(vkCode, 0, 0, 0)
|
||||
for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
|
||||
(mods & 4, 0x12)]: #HANKAKU not suported! mods & 8
|
||||
if apply_mod:
|
||||
ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
|
||||
|
||||
|
||||
def _keyUp(key):
|
||||
"""Performs a keyboard key release (without the press down beforehand).
|
||||
|
||||
Args:
|
||||
key (str): The key to be released up. The valid names are listed in
|
||||
pyautogui.KEY_NAMES.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
needsShift = pyautogui.isShiftCharacter(key)
|
||||
"""
|
||||
# OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
|
||||
if key in keyboardMapping.keys():
|
||||
vkCode = keyboardMapping[key]
|
||||
elif len(key) == 1:
|
||||
# note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
|
||||
vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
|
||||
if vkCode == -1:
|
||||
raise ValueError('There is no VK code for key "%s"' % (key))
|
||||
if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
|
||||
vkCode -= 0x100
|
||||
needsShift = True
|
||||
"""
|
||||
mods, vkCode = divmod(keyboardMapping[key], 0x100)
|
||||
|
||||
for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
|
||||
(mods & 1 or needsShift, 0x10)]: #HANKAKU not suported! mods & 8
|
||||
if apply_mod:
|
||||
ctypes.windll.user32.keybd_event(vk_mod, 0, 0, 0) #
|
||||
ctypes.windll.user32.keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0)
|
||||
for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
|
||||
(mods & 4, 0x12)]: #HANKAKU not suported! mods & 8
|
||||
if apply_mod:
|
||||
ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
|
||||
|
||||
|
||||
def _position():
|
||||
"""Returns the current xy coordinates of the mouse cursor as a two-integer
|
||||
tuple by calling the GetCursorPos() win32 function.
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of the current xy coordinates of the mouse cursor.
|
||||
"""
|
||||
|
||||
cursor = POINT()
|
||||
ctypes.windll.user32.GetCursorPos(ctypes.byref(cursor))
|
||||
return (cursor.x, cursor.y)
|
||||
|
||||
|
||||
def _size():
|
||||
"""Returns the width and height of the screen as a two-integer tuple.
|
||||
|
||||
Returns:
|
||||
(width, height) tuple of the screen size, in pixels.
|
||||
"""
|
||||
return (ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1))
|
||||
|
||||
|
||||
def _moveTo(x, y):
|
||||
"""Send the mouse move event to Windows by calling SetCursorPos() win32
|
||||
function.
|
||||
|
||||
Args:
|
||||
button (str): The mouse button, either 'left', 'middle', or 'right'
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
ctypes.windll.user32.SetCursorPos(x, y)
|
||||
|
||||
|
||||
def _mouseDown(x, y, button):
|
||||
"""Send the mouse down event to Windows by calling the mouse_event() win32
|
||||
function.
|
||||
|
||||
Args:
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
button (str): The mouse button, either 'left', 'middle', or 'right'
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if button == 'left':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_LEFTDOWN, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'middle':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_MIDDLEDOWN, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'right':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_RIGHTDOWN, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
|
||||
def _mouseUp(x, y, button):
|
||||
"""Send the mouse up event to Windows by calling the mouse_event() win32
|
||||
function.
|
||||
|
||||
Args:
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
button (str): The mouse button, either 'left', 'middle', or 'right'
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if button == 'left':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_LEFTUP, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'middle':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_MIDDLEUP, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'right':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_RIGHTUP, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
|
||||
def _click(x, y, button):
|
||||
"""Send the mouse click event to Windows by calling the mouse_event() win32
|
||||
function.
|
||||
|
||||
Args:
|
||||
button (str): The mouse button, either 'left', 'middle', or 'right'
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if button == 'left':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_LEFTCLICK, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'middle':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_MIDDLECLICK, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
elif button == 'right':
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_RIGHTCLICK, x, y)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
else:
|
||||
assert False, "button argument not in ('left', 'middle', 'right')"
|
||||
|
||||
|
||||
def _sendMouseEvent(ev, x, y, dwData=0):
|
||||
"""The helper function that actually makes the call to the mouse_event()
|
||||
win32 function.
|
||||
|
||||
Args:
|
||||
ev (int): The win32 code for the mouse event. Use one of the MOUSEEVENTF_*
|
||||
constants for this argument.
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
dwData (int): The argument for mouse_event()'s dwData parameter. So far
|
||||
this is only used by mouse scrolling.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
assert x != None and y != None, 'x and y cannot be set to None'
|
||||
# TODO: ARG! For some reason, SendInput isn't working for mouse events. I'm switching to using the older mouse_event win32 function.
|
||||
#mouseStruct = MOUSEINPUT()
|
||||
#mouseStruct.dx = x
|
||||
#mouseStruct.dy = y
|
||||
#mouseStruct.mouseData = ev
|
||||
#mouseStruct.time = 0
|
||||
#mouseStruct.dwExtraInfo = ctypes.pointer(ctypes.c_ulong(0)) # according to https://stackoverflow.com/questions/13564851/generate-keyboard-events I can just set this. I don't really care about this value.
|
||||
#inputStruct = INPUT()
|
||||
#inputStruct.mi = mouseStruct
|
||||
#inputStruct.type = INPUT_MOUSE
|
||||
#ctypes.windll.user32.SendInput(1, ctypes.pointer(inputStruct), ctypes.sizeof(inputStruct))
|
||||
|
||||
width, height = _size()
|
||||
convertedX = 65536 * x // width + 1
|
||||
convertedY = 65536 * y // height + 1
|
||||
ctypes.windll.user32.mouse_event(ev, ctypes.c_long(convertedX), ctypes.c_long(convertedY), dwData, 0)
|
||||
|
||||
# TODO: Too many false positives with this code: See: https://github.com/asweigart/pyautogui/issues/108
|
||||
#if ctypes.windll.kernel32.GetLastError() != 0:
|
||||
# raise ctypes.WinError()
|
||||
|
||||
|
||||
def _scroll(clicks, x=None, y=None):
|
||||
"""Send the mouse vertical scroll event to Windows by calling the
|
||||
mouse_event() win32 function.
|
||||
|
||||
Args:
|
||||
clicks (int): The amount of scrolling to do. A positive value is the mouse
|
||||
wheel moving forward (scrolling up), a negative value is backwards (down).
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
startx, starty = _position()
|
||||
width, height = _size()
|
||||
|
||||
if x is None:
|
||||
x = startx
|
||||
else:
|
||||
if x < 0:
|
||||
x = 0
|
||||
elif x >= width:
|
||||
x = width - 1
|
||||
if y is None:
|
||||
y = starty
|
||||
else:
|
||||
if y < 0:
|
||||
y = 0
|
||||
elif y >= height:
|
||||
y = height - 1
|
||||
|
||||
try:
|
||||
_sendMouseEvent(MOUSEEVENTF_WHEEL, x, y, dwData=clicks)
|
||||
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
|
||||
pass
|
||||
|
||||
|
||||
def _hscroll(clicks, x, y):
|
||||
"""Send the mouse horizontal scroll event to Windows by calling the
|
||||
mouse_event() win32 function.
|
||||
|
||||
Args:
|
||||
clicks (int): The amount of scrolling to do. A positive value is the mouse
|
||||
wheel moving right, a negative value is moving left.
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
return _scroll(clicks, x, y)
|
||||
|
||||
|
||||
def _vscroll(clicks, x, y):
|
||||
"""A wrapper for _scroll(), which does vertical scrolling.
|
||||
|
||||
Args:
|
||||
clicks (int): The amount of scrolling to do. A positive value is the mouse
|
||||
wheel moving forward (scrolling up), a negative value is backwards (down).
|
||||
x (int): The x position of the mouse event.
|
||||
y (int): The y position of the mouse event.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
return _scroll(clicks, x, y)
|
||||
|
@ -0,0 +1,298 @@
|
||||
# NOTE - It is a known issue that the keyboard-related functions don't work on Ubuntu VMs in Virtualbox.
|
||||
|
||||
import pyautogui
|
||||
import sys
|
||||
import os
|
||||
|
||||
from Xlib.display import Display
|
||||
from Xlib import X
|
||||
from Xlib.ext.xtest import fake_input
|
||||
import Xlib.XK
|
||||
|
||||
BUTTON_NAME_MAPPING = {'left': 1, 'middle': 2, 'right': 3, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}
|
||||
|
||||
|
||||
if sys.platform in ('java', 'darwin', 'win32'):
|
||||
raise Exception('The pyautogui_x11 module should only be loaded on a Unix system that supports X11.')
|
||||
|
||||
#from pyautogui import *
|
||||
|
||||
"""
|
||||
Much of this code is based on information gleaned from Paul Barton's PyKeyboard in PyUserInput from 2013, itself derived from Akkana Peck's pykey in 2008 ( http://www.shallowsky.com/software/crikey/pykey-0.1 ), itself derived from her "Crikey" lib.
|
||||
"""
|
||||
|
||||
def _position():
|
||||
"""Returns the current xy coordinates of the mouse cursor as a two-integer
|
||||
tuple.
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of the current xy coordinates of the mouse cursor.
|
||||
"""
|
||||
coord = _display.screen().root.query_pointer()._data
|
||||
return coord["root_x"], coord["root_y"]
|
||||
|
||||
|
||||
def _size():
|
||||
return _display.screen().width_in_pixels, _display.screen().height_in_pixels
|
||||
|
||||
|
||||
|
||||
def _vscroll(clicks, x=None, y=None):
|
||||
clicks = int(clicks)
|
||||
if clicks == 0:
|
||||
return
|
||||
elif clicks > 0:
|
||||
button = 4 # scroll up
|
||||
else:
|
||||
button = 5 # scroll down
|
||||
|
||||
for i in range(abs(clicks)):
|
||||
_click(x, y, button=button)
|
||||
|
||||
|
||||
def _hscroll(clicks, x=None, y=None):
|
||||
clicks = int(clicks)
|
||||
if clicks == 0:
|
||||
return
|
||||
elif clicks > 0:
|
||||
button = 7 # scroll right
|
||||
else:
|
||||
button = 6 # scroll left
|
||||
|
||||
for i in range(abs(clicks)):
|
||||
_click(x, y, button=button)
|
||||
|
||||
|
||||
def _scroll(clicks, x=None, y=None):
|
||||
return _vscroll(clicks, x, y)
|
||||
|
||||
|
||||
def _click(x, y, button):
|
||||
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
|
||||
button = BUTTON_NAME_MAPPING[button]
|
||||
|
||||
_mouseDown(x, y, button)
|
||||
_mouseUp(x, y, button)
|
||||
|
||||
|
||||
def _moveTo(x, y):
|
||||
fake_input(_display, X.MotionNotify, x=x, y=y)
|
||||
_display.sync()
|
||||
|
||||
|
||||
def _mouseDown(x, y, button):
|
||||
_moveTo(x, y)
|
||||
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
|
||||
button = BUTTON_NAME_MAPPING[button]
|
||||
fake_input(_display, X.ButtonPress, button)
|
||||
_display.sync()
|
||||
|
||||
|
||||
def _mouseUp(x, y, button):
|
||||
_moveTo(x, y)
|
||||
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
|
||||
button = BUTTON_NAME_MAPPING[button]
|
||||
fake_input(_display, X.ButtonRelease, button)
|
||||
_display.sync()
|
||||
|
||||
|
||||
def _keyDown(key):
|
||||
"""Performs a keyboard key press without the release. This will put that
|
||||
key in a held down state.
|
||||
|
||||
NOTE: For some reason, this does not seem to cause key repeats like would
|
||||
happen if a keyboard key was held down on a text field.
|
||||
|
||||
Args:
|
||||
key (str): The key to be pressed down. The valid names are listed in
|
||||
pyautogui.KEY_NAMES.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
if type(key) == int:
|
||||
fake_input(_display, X.KeyPress, key)
|
||||
_display.sync()
|
||||
return
|
||||
|
||||
needsShift = pyautogui.isShiftCharacter(key)
|
||||
if needsShift:
|
||||
fake_input(_display, X.KeyPress, keyboardMapping['shift'])
|
||||
|
||||
fake_input(_display, X.KeyPress, keyboardMapping[key])
|
||||
|
||||
if needsShift:
|
||||
fake_input(_display, X.KeyRelease, keyboardMapping['shift'])
|
||||
_display.sync()
|
||||
|
||||
|
||||
def _keyUp(key):
|
||||
"""Performs a keyboard key release (without the press down beforehand).
|
||||
|
||||
Args:
|
||||
key (str): The key to be released up. The valid names are listed in
|
||||
pyautogui.KEY_NAMES.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
"""
|
||||
Release a given character key. Also works with character keycodes as
|
||||
integers, but not keysyms.
|
||||
"""
|
||||
if key not in keyboardMapping or keyboardMapping[key] is None:
|
||||
return
|
||||
|
||||
if type(key) == int:
|
||||
keycode = key
|
||||
else:
|
||||
keycode = keyboardMapping[key]
|
||||
|
||||
fake_input(_display, X.KeyRelease, keycode)
|
||||
_display.sync()
|
||||
|
||||
|
||||
# Taken from PyKeyboard's ctor function.
|
||||
_display = Display(os.environ['DISPLAY'])
|
||||
|
||||
|
||||
""" Information for keyboardMapping derived from PyKeyboard's special_key_assignment() function.
|
||||
|
||||
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
|
||||
keyUp(), or press() into the code used for the OS-specific keyboard function.
|
||||
|
||||
They should always be lowercase, and the same keys should be used across all OSes."""
|
||||
keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
|
||||
keyboardMapping.update({
|
||||
'backspace': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('BackSpace')),
|
||||
'\b': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('BackSpace')),
|
||||
'tab': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Tab')),
|
||||
'enter': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
|
||||
'return': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
|
||||
'shift': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_L')),
|
||||
'ctrl': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_L')),
|
||||
'alt': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_L')),
|
||||
'pause': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Pause')),
|
||||
'capslock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Caps_Lock')),
|
||||
'esc': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
|
||||
'escape': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
|
||||
'pgup': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Up')),
|
||||
'pgdn': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Down')),
|
||||
'pageup': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Up')),
|
||||
'pagedown': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Down')),
|
||||
'end': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('End')),
|
||||
'home': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Home')),
|
||||
'left': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Left')),
|
||||
'up': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Up')),
|
||||
'right': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Right')),
|
||||
'down': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Down')),
|
||||
'select': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Select')),
|
||||
'print': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
|
||||
'execute': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Execute')),
|
||||
'prtsc': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
|
||||
'prtscr': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
|
||||
'prntscrn': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
|
||||
'printscreen': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
|
||||
'insert': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Insert')),
|
||||
'del': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Delete')),
|
||||
'delete': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Delete')),
|
||||
'help': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Help')),
|
||||
'winleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_L')),
|
||||
'winright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_R')),
|
||||
'apps': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_L')),
|
||||
'num0': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_0')),
|
||||
'num1': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_1')),
|
||||
'num2': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_2')),
|
||||
'num3': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_3')),
|
||||
'num4': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_4')),
|
||||
'num5': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_5')),
|
||||
'num6': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_6')),
|
||||
'num7': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_7')),
|
||||
'num8': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_8')),
|
||||
'num9': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_9')),
|
||||
'multiply': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Multiply')),
|
||||
'add': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Add')),
|
||||
'separator': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Separator')),
|
||||
'subtract': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Subtract')),
|
||||
'decimal': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Decimal')),
|
||||
'divide': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Divide')),
|
||||
'f1': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F1')),
|
||||
'f2': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F2')),
|
||||
'f3': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F3')),
|
||||
'f4': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F4')),
|
||||
'f5': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F5')),
|
||||
'f6': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F6')),
|
||||
'f7': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F7')),
|
||||
'f8': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F8')),
|
||||
'f9': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F9')),
|
||||
'f10': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F10')),
|
||||
'f11': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F11')),
|
||||
'f12': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F12')),
|
||||
'f13': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F13')),
|
||||
'f14': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F14')),
|
||||
'f15': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F15')),
|
||||
'f16': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F16')),
|
||||
'f17': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F17')),
|
||||
'f18': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F18')),
|
||||
'f19': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F19')),
|
||||
'f20': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F20')),
|
||||
'f21': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F21')),
|
||||
'f22': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F22')),
|
||||
'f23': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F23')),
|
||||
'f24': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F24')),
|
||||
'numlock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Num_Lock')),
|
||||
'scrolllock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Scroll_Lock')),
|
||||
'shiftleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_L')),
|
||||
'shiftright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_R')),
|
||||
'ctrlleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_L')),
|
||||
'ctrlright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_R')),
|
||||
'altleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_L')),
|
||||
'altright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_R')),
|
||||
# These are added because unlike a-zA-Z0-9, the single characters do not have a
|
||||
' ': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('space')),
|
||||
'space': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('space')),
|
||||
'\t': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Tab')),
|
||||
'\n': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')), # for some reason this needs to be cr, not lf
|
||||
'\r': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
|
||||
'\e': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
|
||||
'!': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('exclam')),
|
||||
'#': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('numbersign')),
|
||||
'%': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('percent')),
|
||||
'$': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('dollar')),
|
||||
'&': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('ampersand')),
|
||||
'"': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('quotedbl')),
|
||||
"'": _display.keysym_to_keycode(Xlib.XK.string_to_keysym('apostrophe')),
|
||||
'(': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('parenleft')),
|
||||
')': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('parenright')),
|
||||
'*': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asterisk')),
|
||||
'=': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('equal')),
|
||||
'+': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('plus')),
|
||||
',': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('comma')),
|
||||
'-': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('minus')),
|
||||
'.': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('period')),
|
||||
'/': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('slash')),
|
||||
':': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('colon')),
|
||||
';': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('semicolon')),
|
||||
'<': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('less')),
|
||||
'>': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('greater')),
|
||||
'?': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('question')),
|
||||
'@': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('at')),
|
||||
'[': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bracketleft')),
|
||||
']': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bracketright')),
|
||||
'\\': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('backslash')),
|
||||
'^': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asciicircum')),
|
||||
'_': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('underscore')),
|
||||
'`': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('grave')),
|
||||
'{': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('braceleft')),
|
||||
'|': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bar')),
|
||||
'}': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('braceright')),
|
||||
'~': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asciitilde')),
|
||||
})
|
||||
|
||||
# Trading memory for time" populate winKB so we don't have to call VkKeyScanA each time.
|
||||
for c in """abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890""":
|
||||
keyboardMapping[c] = _display.keysym_to_keycode(Xlib.XK.string_to_keysym(c))
|
@ -0,0 +1,117 @@
|
||||
# Window-handling features of PyAutoGUI for win_32
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
|
||||
import sys
|
||||
if sys.platform != 'win32':
|
||||
raise Exception('The _window_win module should only be loaded on a Windows system.')
|
||||
|
||||
SetWindowPos = ctypes.windll.user32.SetWindowPos
|
||||
# Flags for SetWindowPos:
|
||||
SWP_NOMOVE = ctypes.c_uint(0x0002)
|
||||
SWP_NOSIZE = ctypes.c_uint(0x0001)
|
||||
|
||||
ShowWindow = ctypes.windll.user32.ShowWindow
|
||||
# Flags for ShowWindow:
|
||||
SW_MAXIMIZE = 3
|
||||
SW_MINIMIZE = 6
|
||||
SW_RESTORE = 9
|
||||
|
||||
SwitchToThisWindow = ctypes.windll.user32.SwitchToThisWindow
|
||||
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
|
||||
CloseWindow = ctypes.windll.user32.CloseWindow
|
||||
GetWindowRect = ctypes.windll.user32.GetWindowRect
|
||||
|
||||
EnumWindows = ctypes.windll.user32.EnumWindows
|
||||
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
|
||||
GetWindowText = ctypes.windll.user32.GetWindowTextW
|
||||
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
||||
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
|
||||
|
||||
|
||||
class _Rect(ctypes.Structure):
|
||||
_fields_ = [('left', ctypes.c_long),
|
||||
('top', ctypes.c_long),
|
||||
('right', ctypes.c_long),
|
||||
('bottom', ctypes.c_long)]
|
||||
|
||||
|
||||
class Window(object):
|
||||
|
||||
def __init__(self, hwnd):
|
||||
self._hwnd = hwnd # Window handle
|
||||
|
||||
def set_position(self, x, y, width, height):
|
||||
"""Set window top-left corner position and size"""
|
||||
SetWindowPos(self._hwnd, None, x, y, width, height, ctypes.c_uint(0))
|
||||
|
||||
def move(self, x, y):
|
||||
"""Move window top-left corner to position"""
|
||||
SetWindowPos(self._hwnd, None, x, y, 0, 0, SWP_NOSIZE)
|
||||
|
||||
def resize(self, width, height):
|
||||
"""Change window size"""
|
||||
SetWindowPos(self._hwnd, None, 0, 0, width, height, SWP_NOMOVE)
|
||||
|
||||
def maximize(self):
|
||||
ShowWindow(self._hwnd, SW_MAXIMIZE)
|
||||
|
||||
def set_foreground(self):
|
||||
SetForegroundWindow(self._hwnd)
|
||||
|
||||
def minimize(self):
|
||||
ShowWindow(self._hwnd, SW_MINIMIZE)
|
||||
|
||||
def restore(self):
|
||||
ShowWindow(self._hwnd, SW_RESTORE)
|
||||
|
||||
def close(self):
|
||||
CloseWindow(self._hwnd)
|
||||
|
||||
def get_position(self):
|
||||
"""Returns tuple of 4 numbers: (x, y)s of top-left and bottom-right corners"""
|
||||
rect = _Rect()
|
||||
GetWindowRect(self._hwnd, ctypes.pointer(rect))
|
||||
return rect.left, rect.top, rect.right, rect.bottom
|
||||
|
||||
# def moveRel(self, x=0, y=0): # moves relative to the x, y of top-left corner of the window
|
||||
# pass
|
||||
# def clickRel(self, x=0, y=0, clicks=1, interval=0.0, button='left'):
|
||||
# click relative to the x, y of top-left corner of the window
|
||||
# pass
|
||||
|
||||
|
||||
def getWindows(): #https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/
|
||||
"""Return dict: {'window title' : window handle} for all visible windows"""
|
||||
titles = {}
|
||||
|
||||
def foreach_window(hwnd, lparam):
|
||||
if IsWindowVisible(hwnd):
|
||||
length = GetWindowTextLength(hwnd)
|
||||
buff = ctypes.create_unicode_buffer(length + 1)
|
||||
GetWindowText(hwnd, buff, length + 1)
|
||||
titles[buff.value] = hwnd
|
||||
return True
|
||||
EnumWindows(EnumWindowsProc(foreach_window), 0)
|
||||
|
||||
return titles
|
||||
|
||||
def getWindow(title, exact=False):
|
||||
"""Return Window object if 'title' or its part found in visible windows titles, else return None
|
||||
|
||||
Return only 1 window found first
|
||||
Args:
|
||||
title: unicode string
|
||||
exact (bool): True if search only exact match
|
||||
"""
|
||||
titles = getWindows()
|
||||
hwnd = titles.get(title, None)
|
||||
if not hwnd and not exact:
|
||||
for k, v in titles.items():
|
||||
if title in k:
|
||||
hwnd = v
|
||||
break
|
||||
if hwnd:
|
||||
return Window(hwnd)
|
||||
else:
|
||||
return None
|
@ -0,0 +1,217 @@
|
||||
# Screenshot-related features of PyAutoGUI
|
||||
|
||||
"""
|
||||
So, apparently Pillow support on Ubuntu 64-bit has several additional steps since it doesn't have JPEG/PNG support out of the box. Description here:
|
||||
|
||||
https://stackoverflow.com/questions/7648200/pip-install-pil-e-tickets-1-no-jpeg-png-support
|
||||
http://ubuntuforums.org/showthread.php?t=1751455
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
|
||||
RUNNING_PYTHON_2 = sys.version_info[0] == 2
|
||||
|
||||
scrotExists = False
|
||||
maimExists = False
|
||||
try:
|
||||
if sys.platform not in ('java', 'darwin', 'win32'):
|
||||
whichProc = subprocess.Popen(['which', 'scrot'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
scrotExists = whichProc.wait() == 0
|
||||
except:
|
||||
# if there is no "which" program to find scrot, then assume there is no scrot.
|
||||
pass
|
||||
|
||||
try:
|
||||
if sys.platform not in ('java', 'darwin', 'win32'):
|
||||
whichProc = subprocess.Popen(['which', 'maim'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
maimExists = whichProc.wait() == 0
|
||||
except:
|
||||
# if there is no "which" program to find maim, then assume there is no maim.
|
||||
pass
|
||||
|
||||
def locateAll(needleImage, haystackImage, grayscale=False, limit=None):
|
||||
needleFileObj = None
|
||||
haystackFileObj = None
|
||||
if isinstance(needleImage, str):
|
||||
# 'image' is a filename, load the Image object
|
||||
needleFileObj = open(needleImage, 'rb')
|
||||
needleImage = Image.open(needleFileObj)
|
||||
if isinstance(haystackImage, str):
|
||||
# 'image' is a filename, load the Image object
|
||||
haystackFileObj = open(haystackImage, 'rb')
|
||||
haystackImage = Image.open(haystackFileObj)
|
||||
|
||||
|
||||
if grayscale:
|
||||
needleImage = ImageOps.grayscale(needleImage)
|
||||
haystackImage = ImageOps.grayscale(haystackImage)
|
||||
|
||||
needleWidth, needleHeight = needleImage.size
|
||||
haystackWidth, haystackHeight = haystackImage.size
|
||||
|
||||
needleImageData = tuple(needleImage.getdata()) # TODO - rename to needleImageData??
|
||||
haystackImageData = tuple(haystackImage.getdata())
|
||||
|
||||
needleImageRows = [needleImageData[y * needleWidth:(y+1) * needleWidth] for y in range(needleHeight)] # LEFT OFF - check this
|
||||
needleImageFirstRow = needleImageRows[0]
|
||||
|
||||
assert len(needleImageFirstRow) == needleWidth
|
||||
assert [len(row) for row in needleImageRows] == [needleWidth] * needleHeight
|
||||
|
||||
numMatchesFound = 0
|
||||
|
||||
for y in range(haystackHeight):
|
||||
for matchx in _kmp(needleImageFirstRow, haystackImageData[y * haystackWidth:(y+1) * haystackWidth]):
|
||||
foundMatch = True
|
||||
for searchy in range(1, needleHeight):
|
||||
haystackStart = (searchy + y) * haystackWidth + matchx
|
||||
if needleImageData[searchy * needleWidth:(searchy+1) * needleWidth] != haystackImageData[haystackStart:haystackStart + needleWidth]:
|
||||
foundMatch = False
|
||||
break
|
||||
if foundMatch:
|
||||
# Match found, report the x, y, width, height of where the matching region is in haystack.
|
||||
numMatchesFound += 1
|
||||
yield (matchx, y, needleWidth, needleHeight)
|
||||
if limit is not None and numMatchesFound >= limit:
|
||||
# Limit has been reached. Close file handles.
|
||||
if needleFileObj is not None:
|
||||
needleFileObj.close()
|
||||
if haystackFileObj is not None:
|
||||
haystackFileObj.close()
|
||||
|
||||
|
||||
# There was no limit or the limit wasn't reached, but close the file handles anyway.
|
||||
if needleFileObj is not None:
|
||||
needleFileObj.close()
|
||||
if haystackFileObj is not None:
|
||||
haystackFileObj.close()
|
||||
|
||||
|
||||
def locate(needleImage, haystackImage, grayscale=False):
|
||||
# Note: The gymnastics in this function is because we want to make sure to exhaust the iterator so that the needle and haystack files are closed in locateAll.
|
||||
points = tuple(locateAll(needleImage, haystackImage, grayscale, 1))
|
||||
if len(points) > 0:
|
||||
return points[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def locateOnScreen(image, grayscale=False,region=None):
|
||||
screenshotIm = screenshot(region=region)
|
||||
retVal = locate(image, screenshotIm, grayscale)
|
||||
if 'fp' in dir(screenshotIm) and screenshotIm.fp is not None:
|
||||
screenshotIm.fp.close() # Screenshots on Windows won't have an fp since they came from ImageGrab, not a file.
|
||||
return retVal
|
||||
|
||||
|
||||
def locateAllOnScreen(image, grayscale=False, limit=None, region=None):
|
||||
screenshotIm = screenshot(region=region)
|
||||
retVal = locateAll(image, screenshotIm, grayscale, limit)
|
||||
if 'fp' in dir(screenshotIm) and screenshotIm.fp is not None:
|
||||
screenshotIm.fp.close() # Screenshots on Windows won't have an fp since they came from ImageGrab, not a file.
|
||||
return retVal
|
||||
|
||||
|
||||
def locateCenterOnScreen(image, grayscale=False, region=None):
|
||||
return center(locateOnScreen(image, grayscale, region))
|
||||
|
||||
|
||||
def _screenshot_win32(imageFilename=None):
|
||||
im = ImageGrab.grab()
|
||||
if imageFilename is not None:
|
||||
im.save(imageFilename)
|
||||
return im
|
||||
|
||||
|
||||
def _screenshot_osx(imageFilename=None):
|
||||
if imageFilename is None:
|
||||
tmpFilename = 'screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
|
||||
else:
|
||||
tmpFilename = imageFilename
|
||||
subprocess.call(['screencapture', '-x', tmpFilename])
|
||||
im = Image.open(tmpFilename)
|
||||
if imageFilename is None:
|
||||
os.unlink(tmpFilename)
|
||||
return im
|
||||
|
||||
|
||||
def _screenshot_linux(imageFilename=None, region=None):
|
||||
if not scrotExists:
|
||||
raise NotImplementedError('"scrot" must be installed to use screenshot functions in Linux. Run: sudo apt-get install scrot')
|
||||
if imageFilename is None:
|
||||
tmpFilename = '.screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
|
||||
else:
|
||||
tmpFilename = imageFilename
|
||||
if scrotExists:
|
||||
if not region:
|
||||
subprocess.call(['scrot', tmpFilename])
|
||||
else:
|
||||
if not maimExists:
|
||||
raise NotImplementedError('"maim" must be installed to use screenshot functions with region in Linux. Run: sudo apt-get install maim')
|
||||
left,top,width,height = [str(x) for x in region]
|
||||
subprocess.call(['maim','-x',left,'-y',top,'-w',width,'-h',height, tmpFilename])
|
||||
im = Image.open(tmpFilename)
|
||||
if imageFilename is None:
|
||||
os.unlink(tmpFilename)
|
||||
return im
|
||||
|
||||
else:
|
||||
raise Exception('The scrot program must be installed to take a screenshot with PyAutoGUI on Linux. Run: sudo apt-get install scrot')
|
||||
|
||||
|
||||
|
||||
def _kmp(needle, haystack): # Knuth-Morris-Pratt search algorithm implementation (to be used by screen capture)
|
||||
# build table of shift amounts
|
||||
shifts = [1] * (len(needle) + 1)
|
||||
shift = 1
|
||||
for pos in range(len(needle)):
|
||||
while shift <= pos and needle[pos] != needle[pos-shift]:
|
||||
shift += shifts[pos-shift]
|
||||
shifts[pos+1] = shift
|
||||
|
||||
# do the actual search
|
||||
startPos = 0
|
||||
matchLen = 0
|
||||
for c in haystack:
|
||||
while matchLen == len(needle) or \
|
||||
matchLen >= 0 and needle[matchLen] != c:
|
||||
startPos += shifts[matchLen]
|
||||
matchLen -= shifts[matchLen]
|
||||
matchLen += 1
|
||||
if matchLen == len(needle):
|
||||
yield startPos
|
||||
|
||||
|
||||
def center(coords):
|
||||
return (coords[0] + int(coords[2] / 2), coords[1] + int(coords[3] / 2))
|
||||
|
||||
|
||||
def pixelMatchesColor(x, y, expectedRGBColor, tolerance=0):
|
||||
r, g, b = screenshot().getpixel((x, y))
|
||||
exR, exG, exB = expectedRGBColor
|
||||
|
||||
return (abs(r - exR) <= tolerance) and (abs(g - exG) <= tolerance) and (abs(b - exB) <= tolerance)
|
||||
|
||||
|
||||
def pixel(x, y):
|
||||
return screenshot().getpixel((x, y))
|
||||
|
||||
|
||||
# set the screenshot() function based on the platform running this module
|
||||
if sys.platform.startswith('java'):
|
||||
raise NotImplementedError('Jython is not yet supported by PyAutoGUI.')
|
||||
elif sys.platform == 'darwin':
|
||||
screenshot = _screenshot_osx
|
||||
elif sys.platform == 'win32':
|
||||
screenshot = _screenshot_win32
|
||||
from PIL import ImageGrab
|
||||
else:
|
||||
screenshot = _screenshot_linux
|
||||
|
||||
|
||||
grab = screenshot # for compatibility with Pillow/PIL's ImageGrab module.
|
@ -0,0 +1,9 @@
|
||||
@property
|
||||
def foo():
|
||||
return _foo
|
||||
|
||||
@foo.setter
|
||||
def foo(value):
|
||||
_foo = value
|
||||
|
||||
_foo = 'foo'
|
@ -0,0 +1,36 @@
|
||||
import pytweening
|
||||
|
||||
|
||||
# This is just left here for backwards compatibility. I'll be deprecating this in favor of pyautogui.linear, pyautogui.easeInQuad, etc.
|
||||
getPointOnLine = pytweening.getPointOnLine
|
||||
linear = pytweening.linear
|
||||
easeInQuad = pytweening.easeInQuad
|
||||
easeOutQuad = pytweening.easeOutQuad
|
||||
easeInOutQuad = pytweening.easeInOutQuad
|
||||
easeInCubic = pytweening.easeInCubic
|
||||
easeOutCubic = pytweening.easeOutCubic
|
||||
easeInOutCubic = pytweening.easeInOutCubic
|
||||
easeInQuart = pytweening.easeInQuart
|
||||
easeOutQuart = pytweening.easeOutQuart
|
||||
easeInOutQuart = pytweening.easeInOutQuart
|
||||
easeInQuint = pytweening.easeInQuint
|
||||
easeOutQuint = pytweening.easeOutQuint
|
||||
easeInOutQuint = pytweening.easeInOutQuint
|
||||
easeInSine = pytweening.easeInSine
|
||||
easeOutSine = pytweening.easeOutSine
|
||||
easeInOutSine = pytweening.easeInOutSine
|
||||
easeInExpo = pytweening.easeInExpo
|
||||
easeOutExpo = pytweening.easeOutExpo
|
||||
easeInOutExpo = pytweening.easeInOutExpo
|
||||
easeInCirc = pytweening.easeInCirc
|
||||
easeOutCirc = pytweening.easeOutCirc
|
||||
easeInOutCirc = pytweening.easeInOutCirc
|
||||
easeInElastic = pytweening.easeInElastic
|
||||
easeOutElastic = pytweening.easeOutElastic
|
||||
easeInOutElastic = pytweening.easeInOutElastic
|
||||
easeInBack = pytweening.easeInBack
|
||||
easeOutBack = pytweening.easeOutBack
|
||||
easeInOutBack = pytweening.easeInOutBack
|
||||
easeInBounce = pytweening.easeInBounce
|
||||
easeOutBounce = pytweening.easeOutBounce
|
||||
easeInOutBounce = pytweening.easeInOutBounce
|
@ -0,0 +1,50 @@
|
||||
# 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.5'
|
||||
|
||||
import sys
|
||||
import collections
|
||||
|
||||
|
||||
class PyGetWindowException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def pointInRect(x, y, left, top, width, height):
|
||||
return left < x < left + width and top < y < top + height
|
||||
|
||||
|
||||
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
|
||||
elif sys.platform == 'win32':
|
||||
from ._pygetwindow_win import Win32Window, getActiveWindow, 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')
|
||||
|
||||
|
||||
# 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')
|
@ -0,0 +1,388 @@
|
||||
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
|
@ -0,0 +1,563 @@
|
||||
import ctypes
|
||||
import pyrect
|
||||
from ctypes import wintypes # We can't use ctypes.wintypes, we must import wintypes this way.
|
||||
|
||||
import pygetwindow
|
||||
|
||||
NULL = 0 # Used to match the Win32 API value of "null".
|
||||
|
||||
# These FORMAT_MESSAGE_ constants are used for FormatMesage() and are
|
||||
# documented at https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-formatmessage#parameters
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
|
||||
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
|
||||
|
||||
# These SW_ constants are used for ShowWindow() and are documented at
|
||||
# https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-showwindow#parameters
|
||||
SW_MINIMIZE = 6
|
||||
SW_MAXIMIZE = 3
|
||||
SW_RESTORE = 9
|
||||
|
||||
# SetWindowPos constants:
|
||||
HWND_TOP = 0
|
||||
|
||||
# Window Message constants:
|
||||
WM_CLOSE = 0x0010
|
||||
|
||||
# This ctypes structure is for a Win32 POINT structure,
|
||||
# which is documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805(v=vs.85).aspx
|
||||
# The POINT structure is used by GetCursorPos().
|
||||
class POINT(ctypes.Structure):
|
||||
_fields_ = [("x", ctypes.c_long),
|
||||
("y", ctypes.c_long)]
|
||||
|
||||
enumWindows = ctypes.windll.user32.EnumWindows
|
||||
enumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
|
||||
getWindowText = ctypes.windll.user32.GetWindowTextW
|
||||
getWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
||||
isWindowVisible = ctypes.windll.user32.IsWindowVisible
|
||||
|
||||
|
||||
class RECT(ctypes.Structure):
|
||||
"""A nice wrapper of the RECT structure.
|
||||
|
||||
Microsoft Documentation:
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx
|
||||
"""
|
||||
_fields_ = [('left', ctypes.c_long),
|
||||
('top', ctypes.c_long),
|
||||
('right', ctypes.c_long),
|
||||
('bottom', ctypes.c_long)]
|
||||
|
||||
|
||||
def _getAllTitles():
|
||||
# This code taken from https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/
|
||||
# A correction to this code (for enumWindowsProc) is here: http://makble.com/the-story-of-lpclong
|
||||
titles = []
|
||||
def foreach_window(hWnd, lParam):
|
||||
if isWindowVisible(hWnd):
|
||||
length = getWindowTextLength(hWnd)
|
||||
buff = ctypes.create_unicode_buffer(length + 1)
|
||||
getWindowText(hWnd, buff, length + 1)
|
||||
titles.append((hWnd, buff.value))
|
||||
return True
|
||||
enumWindows(enumWindowsProc(foreach_window), 0)
|
||||
|
||||
return titles
|
||||
|
||||
|
||||
def _formatMessage(errorCode):
|
||||
"""A nice wrapper for FormatMessageW(). TODO
|
||||
|
||||
Microsoft Documentation:
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-formatmessagew
|
||||
|
||||
Additional information:
|
||||
https://stackoverflow.com/questions/18905702/python-ctypes-and-mutable-buffers
|
||||
https://stackoverflow.com/questions/455434/how-should-i-use-formatmessage-properly-in-c
|
||||
"""
|
||||
lpBuffer = wintypes.LPWSTR()
|
||||
|
||||
ctypes.windll.kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
errorCode,
|
||||
0, # dwLanguageId
|
||||
ctypes.cast(ctypes.byref(lpBuffer), wintypes.LPWSTR),
|
||||
0, # nSize
|
||||
NULL)
|
||||
msg = lpBuffer.value.rstrip()
|
||||
ctypes.windll.kernel32.LocalFree(lpBuffer) # Free the memory allocated for the error message's buffer.
|
||||
return msg
|
||||
|
||||
|
||||
def _raiseWithLastError():
|
||||
"""A helper function that raises PyGetWindowException using the error
|
||||
information from GetLastError() and FormatMessage()."""
|
||||
errorCode = ctypes.windll.kernel32.GetLastError()
|
||||
raise pygetwindow.PyGetWindowException('Error code from Windows: %s - %s' % (errorCode, _formatMessage(errorCode)))
|
||||
|
||||
|
||||
def _getWindowRect(hWnd):
|
||||
"""A nice wrapper for GetWindowRect(). TODO
|
||||
|
||||
Syntax:
|
||||
BOOL GetWindowRect(
|
||||
HWND hWnd,
|
||||
LPRECT lpRect
|
||||
);
|
||||
|
||||
Microsoft Documentation:
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowrect
|
||||
"""
|
||||
rect = RECT()
|
||||
result = ctypes.windll.user32.GetWindowRect(hWnd, ctypes.byref(rect))
|
||||
if result != 0:
|
||||
return pygetwindow.Rect(rect.left, rect.top, rect.right, rect.bottom)
|
||||
else:
|
||||
_raiseWithLastError()
|
||||
|
||||
|
||||
def _getWindowText(hWnd):
|
||||
"""A wrapper for the GetWindowTextW() win api. TODO
|
||||
|
||||
Syntax:
|
||||
int GetWindowTextW(
|
||||
HWND hWnd,
|
||||
LPWSTR lpString,
|
||||
int nMaxCount
|
||||
);
|
||||
|
||||
int GetWindowTextLengthW(
|
||||
HWND hWnd
|
||||
);
|
||||
|
||||
Microsoft Documentation:
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextw
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextlengthw
|
||||
"""
|
||||
textLenInCharacters = ctypes.windll.user32.GetWindowTextLengthW(hWnd)
|
||||
stringBuffer = ctypes.create_unicode_buffer(textLenInCharacters + 1) # +1 for the \0 at the end of the null-terminated string.
|
||||
ctypes.windll.user32.GetWindowTextW(hWnd, stringBuffer, textLenInCharacters + 1)
|
||||
|
||||
# TODO it's ambiguous if an error happened or the title text is just empty. Look into this later.
|
||||
return stringBuffer.value
|
||||
|
||||
|
||||
def getActiveWindow():
|
||||
"""Returns a Window object of the currently active Window."""
|
||||
hWnd = ctypes.windll.user32.GetForegroundWindow()
|
||||
if hWnd == 0:
|
||||
# TODO - raise error instead
|
||||
return None # Note that this function doesn't use GetLastError().
|
||||
else:
|
||||
return Win32Window(hWnd)
|
||||
|
||||
|
||||
def getWindowsAt(x, y):
|
||||
"""Returns a list of Window objects
|
||||
|
||||
Args:
|
||||
x (int, optional): The x position of the window(s).
|
||||
y (int, optional): The y position of the window(s)."""
|
||||
windowsAtXY = []
|
||||
for window in getAllWindows():
|
||||
if pygetwindow.pointInRect(x, y, window.left, window.top, window.width, window.height):
|
||||
windowsAtXY.append(window)
|
||||
return windowsAtXY
|
||||
|
||||
|
||||
def getWindowsWithTitle(title):
|
||||
"""Returns a list of Window objects that substring match the title.
|
||||
"""
|
||||
hWndsAndTitles = _getAllTitles()
|
||||
windowObjs = []
|
||||
for hWnd, winTitle in hWndsAndTitles:
|
||||
if title.upper() in winTitle.upper(): # do a case-insensitive match
|
||||
windowObjs.append(Win32Window(hWnd))
|
||||
return windowObjs
|
||||
|
||||
|
||||
def getAllTitles():
|
||||
"""Returns a list of strings of window titles for all visible windows.
|
||||
"""
|
||||
return [window.title for window in getAllWindows()]
|
||||
|
||||
|
||||
def getAllWindows():
|
||||
"""Returns a list of Window objects for all visible windows.
|
||||
"""
|
||||
windowObjs = []
|
||||
def foreach_window(hWnd, lParam):
|
||||
if ctypes.windll.user32.IsWindowVisible(hWnd) != 0:
|
||||
windowObjs.append(Win32Window(hWnd))
|
||||
return True
|
||||
enumWindows(enumWindowsProc(foreach_window), 0)
|
||||
|
||||
return windowObjs
|
||||
|
||||
|
||||
class Win32Window():
|
||||
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 activate(self):
|
||||
"""Activate 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 isActive(self):
|
||||
"""Returns True if the window is currently the active, foreground window."""
|
||||
return getActiveWindow() == 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
|
||||
|
||||
|
||||
def cursor():
|
||||
"""Returns the current xy coordinates of the mouse cursor as a two-integer
|
||||
tuple by calling the GetCursorPos() win32 function.
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of the current xy coordinates of the mouse cursor.
|
||||
"""
|
||||
|
||||
cursor = POINT()
|
||||
ctypes.windll.user32.GetCursorPos(ctypes.byref(cursor))
|
||||
return pygetwindow.Point(x=cursor.x, y=cursor.y)
|
||||
|
||||
|
||||
def resolution():
|
||||
"""Returns the width and height of the screen as a two-integer tuple.
|
||||
|
||||
Returns:
|
||||
(width, height) tuple of the screen size, in pixels.
|
||||
"""
|
||||
return pygetwindow.Size(width=ctypes.windll.user32.GetSystemMetrics(0), height=ctypes.windll.user32.GetSystemMetrics(1))
|
||||
|
||||
'''
|
||||
def displayWindowsUnderMouse(xOffset=0, yOffset=0):
|
||||
"""This function is meant to be run from the command line. It will
|
||||
automatically display the location and RGB of the mouse cursor."""
|
||||
print('Press Ctrl-C to quit.')
|
||||
if xOffset != 0 or yOffset != 0:
|
||||
print('xOffset: %s yOffset: %s' % (xOffset, yOffset))
|
||||
resolution = size()
|
||||
try:
|
||||
while True:
|
||||
# Get and print the mouse coordinates.
|
||||
x, y = position()
|
||||
positionStr = 'X: ' + str(x - xOffset).rjust(4) + ' Y: ' + str(y - yOffset).rjust(4)
|
||||
|
||||
# TODO - display windows under the mouse
|
||||
|
||||
sys.stdout.write(positionStr)
|
||||
sys.stdout.write('\b' * len(positionStr))
|
||||
sys.stdout.flush()
|
||||
except KeyboardInterrupt:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
'''
|
@ -0,0 +1 @@
|
||||
bar = 42
|
@ -0,0 +1,389 @@
|
||||
# PyMsgBox - A simple, cross-platform, pure Python module for JavaScript-like message boxes.
|
||||
# Al Sweigart al@inventwithpython.com
|
||||
|
||||
# Modified BSD License
|
||||
# Derived from Stephen Raymond Ferg's EasyGui http://easygui.sourceforge.net/
|
||||
|
||||
"""
|
||||
The four functions in PyMsgBox:
|
||||
|
||||
- alert(text='', title='', button='OK')
|
||||
|
||||
Displays a simple message box with text and a single OK button. Returns the text of the button clicked on.
|
||||
|
||||
- confirm(text='', title='', buttons=['OK', 'Cancel'])
|
||||
|
||||
Displays a message box with OK and Cancel buttons. Number and text of buttons can be customized. Returns the text of the button clicked on.
|
||||
|
||||
- prompt(text='', title='' , default='')
|
||||
|
||||
Displays a message box with text input, and OK & Cancel buttons. Returns the text entered, or None if Cancel was clicked.
|
||||
|
||||
- password(text='', title='', default='', mask='*')
|
||||
|
||||
Displays a message box with text input, and OK & Cancel buttons. Typed characters appear as *. Returns the text entered, or None if Cancel was clicked.
|
||||
"""
|
||||
|
||||
"""
|
||||
TODO Roadmap:
|
||||
- Be able to specify a custom icon in the message box.
|
||||
- Be able to place the message box at an arbitrary position (including on multi screen layouts)
|
||||
- Add mouse clicks to unit testing.
|
||||
- progress() function to display a progress bar
|
||||
- Maybe other types of dialog: open, save, file/folder picker, etc.
|
||||
"""
|
||||
|
||||
__version__ = '1.0.6'
|
||||
|
||||
import sys
|
||||
RUNNING_PYTHON_2 = sys.version_info[0] == 2
|
||||
|
||||
# Because PyAutoGUI requires PyMsgBox but might be installed on systems
|
||||
# without tkinter, we don't want a lack of tkinter to cause installation
|
||||
# to fail. So exceptions won't be raised until the PyMsgBox functions
|
||||
# are actually called.
|
||||
TKINTER_IMPORT_SUCCEEDED = True
|
||||
|
||||
try:
|
||||
if RUNNING_PYTHON_2:
|
||||
import Tkinter as tk
|
||||
else:
|
||||
import tkinter as tk
|
||||
|
||||
rootWindowPosition = '+300+200'
|
||||
|
||||
if tk.TkVersion < 8.0 :
|
||||
raise RuntimeError('You are running Tk version: ' + str(tk.TkVersion) + 'You must be using Tk version 8.0 or greater to use PyMsgBox.')
|
||||
|
||||
except ImportError:
|
||||
TKINTER_IMPORT_SUCCEEDED = False
|
||||
|
||||
|
||||
|
||||
PROPORTIONAL_FONT_FAMILY = ('MS', 'Sans', 'Serif')
|
||||
MONOSPACE_FONT_FAMILY = ('Courier')
|
||||
|
||||
PROPORTIONAL_FONT_SIZE = 10
|
||||
MONOSPACE_FONT_SIZE = 9 #a little smaller, because it it more legible at a smaller size
|
||||
TEXT_ENTRY_FONT_SIZE = 12 # a little larger makes it easier to see
|
||||
|
||||
|
||||
STANDARD_SELECTION_EVENTS = ['Return', 'Button-1', 'space']
|
||||
|
||||
# constants for strings: (for internationalization, change these)
|
||||
OK_TEXT = 'OK'
|
||||
CANCEL_TEXT = 'Cancel'
|
||||
TIMEOUT_TEXT = 'Timeout'
|
||||
|
||||
# Initialize some global variables that will be reset later
|
||||
__choiceboxMultipleSelect = None
|
||||
__widgetTexts = None
|
||||
__replyButtonText = None
|
||||
__choiceboxResults = None
|
||||
__firstWidget = None
|
||||
__enterboxText = None
|
||||
__enterboxDefaultText=''
|
||||
__multenterboxText = ''
|
||||
choiceboxChoices = None
|
||||
choiceboxWidget = None
|
||||
entryWidget = None
|
||||
boxRoot = None
|
||||
buttonsFrame = None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def alert(text='', title='', button=OK_TEXT, root=None, timeout=None):
|
||||
"""Displays a simple message box with text and a single OK button. Returns the text of the button clicked on."""
|
||||
assert TKINTER_IMPORT_SUCCEEDED, 'Tkinter is required for pymsgbox'
|
||||
return _buttonbox(msg=text, title=title, choices=[str(button)], root=root, timeout=timeout)
|
||||
|
||||
|
||||
def confirm(text='', title='', buttons=[OK_TEXT, CANCEL_TEXT], root=None, timeout=None):
|
||||
"""Displays a message box with OK and Cancel buttons. Number and text of buttons can be customized. Returns the text of the button clicked on."""
|
||||
assert TKINTER_IMPORT_SUCCEEDED, 'Tkinter is required for pymsgbox'
|
||||
return _buttonbox(msg=text, title=title, choices=[str(b) for b in buttons], root=root, timeout=timeout)
|
||||
|
||||
|
||||
def prompt(text='', title='' , default='', root=None, timeout=None):
|
||||
"""Displays a message box with text input, and OK & Cancel buttons. Returns the text entered, or None if Cancel was clicked."""
|
||||
assert TKINTER_IMPORT_SUCCEEDED, 'Tkinter is required for pymsgbox'
|
||||
return __fillablebox(text, title, default=default, mask=None,root=root, timeout=timeout)
|
||||
|
||||
|
||||
def password(text='', title='', default='', mask='*', root=None, timeout=None):
|
||||
"""Displays a message box with text input, and OK & Cancel buttons. Typed characters appear as *. Returns the text entered, or None if Cancel was clicked."""
|
||||
assert TKINTER_IMPORT_SUCCEEDED, 'Tkinter is required for pymsgbox'
|
||||
return __fillablebox(text, title, default, mask=mask, root=root, timeout=timeout)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import pymsgbox.native as native # This needs to be after the above functions so that the unimplmeneted native functions can default back to the above functions.
|
||||
native # dummy line just to make lint stop complaining about the previous line
|
||||
|
||||
def timeoutBoxRoot():
|
||||
global boxRoot, __replyButtonText, __enterboxText
|
||||
boxRoot.destroy()
|
||||
__replyButtonText = TIMEOUT_TEXT
|
||||
__enterboxText = TIMEOUT_TEXT
|
||||
|
||||
|
||||
def _buttonbox(msg, title, choices, root=None, timeout=None):
|
||||
"""
|
||||
Display a msg, a title, and a set of buttons.
|
||||
The buttons are defined by the members of the choices list.
|
||||
Return the text of the button that the user selected.
|
||||
|
||||
@arg msg: the msg to be displayed.
|
||||
@arg title: the window title
|
||||
@arg choices: a list or tuple of the choices to be displayed
|
||||
"""
|
||||
global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
|
||||
|
||||
|
||||
# Initialize __replyButtonText to the first choice.
|
||||
# This is what will be used if the window is closed by the close button.
|
||||
__replyButtonText = choices[0]
|
||||
|
||||
if root:
|
||||
root.withdraw()
|
||||
boxRoot = tk.Toplevel(master=root)
|
||||
boxRoot.withdraw()
|
||||
else:
|
||||
boxRoot = tk.Tk()
|
||||
boxRoot.withdraw()
|
||||
|
||||
boxRoot.title(title)
|
||||
boxRoot.iconname('Dialog')
|
||||
boxRoot.geometry(rootWindowPosition)
|
||||
boxRoot.minsize(400, 100)
|
||||
|
||||
# ------------- define the messageFrame ---------------------------------
|
||||
messageFrame = tk.Frame(master=boxRoot)
|
||||
messageFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
# ------------- define the buttonsFrame ---------------------------------
|
||||
buttonsFrame = tk.Frame(master=boxRoot)
|
||||
buttonsFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
# -------------------- place the widgets in the frames -----------------------
|
||||
messageWidget = tk.Message(messageFrame, text=msg, width=400)
|
||||
messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY, PROPORTIONAL_FONT_SIZE))
|
||||
messageWidget.pack(side=tk.TOP, expand=tk.YES, fill=tk.X, padx='3m', pady='3m')
|
||||
|
||||
__put_buttons_in_buttonframe(choices)
|
||||
|
||||
# -------------- the action begins -----------
|
||||
# put the focus on the first button
|
||||
__firstWidget.focus_force()
|
||||
|
||||
boxRoot.deiconify()
|
||||
if timeout is not None:
|
||||
boxRoot.after(timeout, timeoutBoxRoot)
|
||||
boxRoot.mainloop()
|
||||
try:
|
||||
boxRoot.destroy()
|
||||
except tk.TclError:
|
||||
if __replyButtonText != TIMEOUT_TEXT:
|
||||
__replyButtonText = None
|
||||
|
||||
if root: root.deiconify()
|
||||
return __replyButtonText
|
||||
|
||||
|
||||
def __put_buttons_in_buttonframe(choices):
|
||||
"""Put the buttons in the buttons frame"""
|
||||
global __widgetTexts, __firstWidget, buttonsFrame
|
||||
|
||||
__firstWidget = None
|
||||
__widgetTexts = {}
|
||||
|
||||
i = 0
|
||||
|
||||
for buttonText in choices:
|
||||
tempButton = tk.Button(buttonsFrame, takefocus=1, text=buttonText)
|
||||
_bindArrows(tempButton)
|
||||
tempButton.pack(expand=tk.YES, side=tk.LEFT, padx='1m', pady='1m', ipadx='2m', ipady='1m')
|
||||
|
||||
# remember the text associated with this widget
|
||||
__widgetTexts[tempButton] = buttonText
|
||||
|
||||
# remember the first widget, so we can put the focus there
|
||||
if i == 0:
|
||||
__firstWidget = tempButton
|
||||
i = 1
|
||||
|
||||
# for the commandButton, bind activation events to the activation event handler
|
||||
commandButton = tempButton
|
||||
handler = __buttonEvent
|
||||
for selectionEvent in STANDARD_SELECTION_EVENTS:
|
||||
commandButton.bind('<%s>' % selectionEvent, handler)
|
||||
|
||||
if CANCEL_TEXT in choices:
|
||||
commandButton.bind('<Escape>', __cancelButtonEvent)
|
||||
|
||||
|
||||
def _bindArrows(widget, skipArrowKeys=False):
|
||||
widget.bind('<Down>', _tabRight)
|
||||
widget.bind('<Up>' , _tabLeft)
|
||||
|
||||
if not skipArrowKeys:
|
||||
widget.bind('<Right>',_tabRight)
|
||||
widget.bind('<Left>' , _tabLeft)
|
||||
|
||||
def _tabRight(event):
|
||||
boxRoot.event_generate('<Tab>')
|
||||
|
||||
def _tabLeft(event):
|
||||
boxRoot.event_generate('<Shift-Tab>')
|
||||
|
||||
|
||||
def __buttonEvent(event):
|
||||
"""
|
||||
Handle an event that is generated by a person clicking a button.
|
||||
"""
|
||||
global boxRoot, __widgetTexts, __replyButtonText
|
||||
__replyButtonText = __widgetTexts[event.widget]
|
||||
boxRoot.quit() # quit the main loop
|
||||
|
||||
def __cancelButtonEvent(event):
|
||||
"""Handle pressing Esc by clicking the Cancel button."""
|
||||
global boxRoot, __widgetTexts, __replyButtonText
|
||||
__replyButtonText = CANCEL_TEXT
|
||||
boxRoot.quit()
|
||||
|
||||
|
||||
def __fillablebox(msg, title='', default='', mask=None, root=None, timeout=None):
|
||||
"""
|
||||
Show a box in which a user can enter some text.
|
||||
You may optionally specify some default text, which will appear in the
|
||||
enterbox when it is displayed.
|
||||
Returns the text that the user entered, or None if he cancels the operation.
|
||||
"""
|
||||
|
||||
global boxRoot, __enterboxText, __enterboxDefaultText
|
||||
global cancelButton, entryWidget, okButton
|
||||
|
||||
if title == None:
|
||||
title == ''
|
||||
if default == None:
|
||||
default = ''
|
||||
__enterboxDefaultText = default
|
||||
__enterboxText = __enterboxDefaultText
|
||||
|
||||
if root:
|
||||
root.withdraw()
|
||||
boxRoot = tk.Toplevel(master=root)
|
||||
boxRoot.withdraw()
|
||||
else:
|
||||
boxRoot = tk.Tk()
|
||||
boxRoot.withdraw()
|
||||
|
||||
boxRoot.title(title)
|
||||
boxRoot.iconname('Dialog')
|
||||
boxRoot.geometry(rootWindowPosition)
|
||||
boxRoot.bind('<Escape>', __enterboxCancel)
|
||||
|
||||
# ------------- define the messageFrame ---------------------------------
|
||||
messageFrame = tk.Frame(master=boxRoot)
|
||||
messageFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
# ------------- define the buttonsFrame ---------------------------------
|
||||
buttonsFrame = tk.Frame(master=boxRoot)
|
||||
buttonsFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
|
||||
# ------------- define the entryFrame ---------------------------------
|
||||
entryFrame = tk.Frame(master=boxRoot)
|
||||
entryFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
# ------------- define the buttonsFrame ---------------------------------
|
||||
buttonsFrame = tk.Frame(master=boxRoot)
|
||||
buttonsFrame.pack(side=tk.TOP, fill=tk.BOTH)
|
||||
|
||||
#-------------------- the msg widget ----------------------------
|
||||
messageWidget = tk.Message(messageFrame, width='4.5i', text=msg)
|
||||
messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY, PROPORTIONAL_FONT_SIZE))
|
||||
messageWidget.pack(side=tk.RIGHT, expand=1, fill=tk.BOTH, padx='3m', pady='3m')
|
||||
|
||||
# --------- entryWidget ----------------------------------------------
|
||||
entryWidget = tk.Entry(entryFrame, width=40)
|
||||
_bindArrows(entryWidget, skipArrowKeys=True)
|
||||
entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY, TEXT_ENTRY_FONT_SIZE))
|
||||
if mask:
|
||||
entryWidget.configure(show=mask)
|
||||
entryWidget.pack(side=tk.LEFT, padx='3m')
|
||||
entryWidget.bind('<Return>', __enterboxGetText)
|
||||
entryWidget.bind('<Escape>', __enterboxCancel)
|
||||
|
||||
# put text into the entryWidget and have it pre-highlighted
|
||||
if __enterboxDefaultText != '':
|
||||
entryWidget.insert(0,__enterboxDefaultText)
|
||||
entryWidget.select_range(0, tk.END)
|
||||
|
||||
# ------------------ ok button -------------------------------
|
||||
okButton = tk.Button(buttonsFrame, takefocus=1, text=OK_TEXT)
|
||||
_bindArrows(okButton)
|
||||
okButton.pack(expand=1, side=tk.LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
|
||||
|
||||
# for the commandButton, bind activation events to the activation event handler
|
||||
commandButton = okButton
|
||||
handler = __enterboxGetText
|
||||
for selectionEvent in STANDARD_SELECTION_EVENTS:
|
||||
commandButton.bind('<%s>' % selectionEvent, handler)
|
||||
|
||||
|
||||
# ------------------ cancel button -------------------------------
|
||||
cancelButton = tk.Button(buttonsFrame, takefocus=1, text=CANCEL_TEXT)
|
||||
_bindArrows(cancelButton)
|
||||
cancelButton.pack(expand=1, side=tk.RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
|
||||
|
||||
# for the commandButton, bind activation events to the activation event handler
|
||||
commandButton = cancelButton
|
||||
handler = __enterboxCancel
|
||||
for selectionEvent in STANDARD_SELECTION_EVENTS:
|
||||
commandButton.bind('<%s>' % selectionEvent, handler)
|
||||
|
||||
# ------------------- time for action! -----------------
|
||||
entryWidget.focus_force() # put the focus on the entryWidget
|
||||
boxRoot.deiconify()
|
||||
if timeout is not None:
|
||||
boxRoot.after(timeout, timeoutBoxRoot)
|
||||
boxRoot.mainloop() # run it!
|
||||
|
||||
# -------- after the run has completed ----------------------------------
|
||||
if root: root.deiconify()
|
||||
try:
|
||||
boxRoot.destroy() # button_click didn't destroy boxRoot, so we do it now
|
||||
except tk.TclError:
|
||||
if __enterboxText != TIMEOUT_TEXT:
|
||||
return None
|
||||
|
||||
return __enterboxText
|
||||
|
||||
|
||||
def __enterboxGetText(event):
|
||||
global __enterboxText
|
||||
|
||||
__enterboxText = entryWidget.get()
|
||||
boxRoot.quit()
|
||||
|
||||
|
||||
def __enterboxRestore(event):
|
||||
global entryWidget
|
||||
|
||||
entryWidget.delete(0,len(entryWidget.get()))
|
||||
entryWidget.insert(0, __enterboxDefaultText)
|
||||
|
||||
|
||||
def __enterboxCancel(event):
|
||||
global __enterboxText
|
||||
|
||||
__enterboxText = None
|
||||
boxRoot.quit()
|
||||
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
# The Windows native message box.
|
||||
|
||||
import sys
|
||||
import ctypes # An included library with Python install.
|
||||
|
||||
|
||||
|
||||
|
||||
MB_OK = 0x0
|
||||
MB_OKCANCEL = 0x1
|
||||
MB_ABORTRETRYIGNORE = 0x2
|
||||
MB_YESNOCANCEL = 0x3
|
||||
MB_YESNO = 0x4
|
||||
MB_RETRYCANCEL = 0x5
|
||||
|
||||
MB_ICONHAND = MB_ICONSTOP = MB_ICONERRPR = 0x10
|
||||
MB_ICONQUESTION = 0x20
|
||||
MB_ICONEXCLAIMATION = 0x30
|
||||
MB_ICONASTERISK = MB_ICONINFOMRAITON = 0x40
|
||||
|
||||
MB_DEFAULTBUTTON1 = 0x0
|
||||
MB_DEFAULTBUTTON2 = 0x100
|
||||
MB_DEFAULTBUTTON3 = 0x200
|
||||
MB_DEFAULTBUTTON4 = 0x300
|
||||
|
||||
MB_SETFOREGROUND = 0x10000
|
||||
MB_TOPMOST = 0x40000
|
||||
|
||||
runningOnPython2 = sys.version_info[0] == 2
|
||||
|
||||
|
||||
|
||||
if runningOnPython2:
|
||||
messageBoxFunc = ctypes.windll.user32.MessageBoxA
|
||||
else: # Python 3 functions.
|
||||
messageBoxFunc = ctypes.windll.user32.MessageBoxW
|
||||
|
||||
|
||||
def alert(text='', title='', button='OK'):
|
||||
"""Displays a simple message box with text and a single OK button. Returns the text of the button clicked on."""
|
||||
messageBoxFunc(0, text, title, MB_OK | MB_SETFOREGROUND | MB_TOPMOST)
|
||||
return button
|
||||
|
||||
def confirm(text='', title='', buttons=['OK', 'Cancel']):
|
||||
"""Displays a message box with OK and Cancel buttons. Number and text of buttons can be customized. Returns the text of the button clicked on."""
|
||||
retVal = messageBoxFunc(0, text, title, MB_OKCANCEL | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST)
|
||||
if retVal == 1 or len(buttons) == 1:
|
||||
return buttons[0]
|
||||
elif retVal == 2:
|
||||
return buttons[1]
|
||||
else:
|
||||
assert False, 'Unexpected return value from MessageBox: %s' % (retVal)
|
||||
|
||||
|
||||
|
||||
'''
|
||||
def prompt(text='', title='' , default=''):
|
||||
"""Displays a message box with text input, and OK & Cancel buttons. Returns the text entered, or None if Cancel was clicked."""
|
||||
pass
|
||||
|
||||
def password(text='', title='', default='', mask='*'):
|
||||
"""Displays a message box with text input, and OK & Cancel buttons. Typed characters appear as *. Returns the text entered, or None if Cancel was clicked."""
|
||||
pass
|
||||
|
||||
'''
|
@ -0,0 +1,27 @@
|
||||
# These functions use the operating system's native message box calls.
|
||||
|
||||
import sys
|
||||
|
||||
# default back to the original functions if no native functions exist.
|
||||
import pymsgbox
|
||||
alert = pymsgbox.alert
|
||||
confirm = pymsgbox.confirm
|
||||
prompt = pymsgbox.prompt
|
||||
password = pymsgbox.password
|
||||
|
||||
|
||||
# The platformModule is where we reference the platform-specific functions.
|
||||
if sys.platform.startswith('java'):
|
||||
import pymsgbox._native_java as platformModule
|
||||
elif sys.platform == 'darwin':
|
||||
import pymsgbox._native_osx as platformModule
|
||||
elif sys.platform == 'win32':
|
||||
import pymsgbox._native_win as platformModule
|
||||
alert = platformModule.alert
|
||||
confirm = platformModule.confirm
|
||||
else:
|
||||
import pymsgbox._native_x11 as platformModule
|
||||
|
||||
platformModule # this line used to silence the linting tool. Will be removed once implementation is done
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,500 @@
|
||||
# PyScreeze
|
||||
# by Al Sweigart
|
||||
# https://github.com/asweigart/pyscreeze
|
||||
# BSD license
|
||||
|
||||
"""
|
||||
So, apparently Pillow support on Ubuntu 64-bit has several additional steps since it doesn't have JPEG/PNG support out of the box. Description here:
|
||||
|
||||
https://stackoverflow.com/questions/7648200/pip-install-pil-e-tickets-1-no-jpeg-png-support
|
||||
http://ubuntuforums.org/showthread.php?t=1751455
|
||||
"""
|
||||
|
||||
__version__ = '0.1.21'
|
||||
|
||||
import collections
|
||||
import datetime
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import errno
|
||||
try:
|
||||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
except ImportError:
|
||||
pass
|
||||
from contextlib import contextmanager
|
||||
|
||||
try:
|
||||
import cv2, numpy
|
||||
useOpenCV = True
|
||||
RUNNING_CV_2 = cv2.__version__[0] < '3'
|
||||
except ImportError:
|
||||
useOpenCV = False
|
||||
|
||||
RUNNING_PYTHON_2 = sys.version_info[0] == 2
|
||||
if useOpenCV:
|
||||
if RUNNING_CV_2:
|
||||
LOAD_COLOR = cv2.CV_LOAD_IMAGE_COLOR
|
||||
LOAD_GRAYSCALE = cv2.CV_LOAD_IMAGE_GRAYSCALE
|
||||
else:
|
||||
LOAD_COLOR = cv2.IMREAD_COLOR
|
||||
LOAD_GRAYSCALE = cv2.IMREAD_GRAYSCALE
|
||||
|
||||
|
||||
GRAYSCALE_DEFAULT = False
|
||||
|
||||
# For version 0.1.19 I changed it so that ImageNotFoundException was raised
|
||||
# instead of returning None. In hindsight, this change came too late, so I'm
|
||||
# changing it back to returning None. But I'm also including this option for
|
||||
# folks who would rather have it raise an exception.
|
||||
USE_IMAGE_NOT_FOUND_EXCEPTION = False
|
||||
|
||||
scrotExists = False
|
||||
try:
|
||||
if sys.platform not in ('java', 'darwin', 'win32'):
|
||||
whichProc = subprocess.Popen(
|
||||
['which', 'scrot'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
scrotExists = whichProc.wait() == 0
|
||||
except OSError as ex:
|
||||
if ex.errno == errno.ENOENT:
|
||||
# if there is no "which" program to find scrot, then assume there
|
||||
# is no scrot.
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from ctypes import windll
|
||||
|
||||
# win32 DC(DeviceContext) Manager
|
||||
@contextmanager
|
||||
def __win32_openDC(hWnd):
|
||||
hDC = windll.user32.GetDC(hWnd)
|
||||
if hDC == 0: #NULL
|
||||
raise WindowsError("windll.user32.GetDC failed : return NULL")
|
||||
try:
|
||||
yield hDC
|
||||
finally:
|
||||
if windll.user32.ReleaseDC(hWnd, hDC) == 0:
|
||||
raise WindowsError("windll.user32.ReleaseDC failed : return 0")
|
||||
|
||||
Box = collections.namedtuple('Box', 'left top width height')
|
||||
Point = collections.namedtuple('Point', 'x y')
|
||||
RGB = collections.namedtuple('RGB', 'red green blue')
|
||||
|
||||
class ImageNotFoundException(Exception):
|
||||
pass # This is an exception class raised when the locate functions fail.
|
||||
|
||||
|
||||
def _load_cv2(img, grayscale=None):
|
||||
# load images if given filename, or convert as needed to opencv
|
||||
# Alpha layer just causes failures at this point, so flatten to RGB.
|
||||
# RGBA: load with -1 * cv2.CV_LOAD_IMAGE_COLOR to preserve alpha
|
||||
# to matchTemplate, need template and image to be the same wrt having alpha
|
||||
|
||||
if grayscale is None:
|
||||
grayscale = GRAYSCALE_DEFAULT
|
||||
if isinstance(img, str):
|
||||
# The function imread loads an image from the specified file and
|
||||
# returns it. If the image cannot be read (because of missing
|
||||
# file, improper permissions, unsupported or invalid format),
|
||||
# the function returns an empty matrix
|
||||
# http://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html
|
||||
if grayscale:
|
||||
img_cv = cv2.imread(img, LOAD_GRAYSCALE)
|
||||
else:
|
||||
img_cv = cv2.imread(img, LOAD_COLOR)
|
||||
if img_cv is None:
|
||||
raise IOError("Failed to read %s because file is missing, "
|
||||
"has improper permissions, or is an "
|
||||
"unsupported or invalid format" % img)
|
||||
elif isinstance(img, numpy.ndarray):
|
||||
# don't try to convert an already-gray image to gray
|
||||
if grayscale and len(img.shape) == 3: # and img.shape[2] == 3:
|
||||
img_cv = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
img_cv = img
|
||||
elif hasattr(img, 'convert'):
|
||||
# assume its a PIL.Image, convert to cv format
|
||||
img_array = numpy.array(img.convert('RGB'))
|
||||
img_cv = img_array[:, :, ::-1].copy() # -1 does RGB -> BGR
|
||||
if grayscale:
|
||||
img_cv = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
raise TypeError('expected an image filename, OpenCV numpy array, or PIL image')
|
||||
return img_cv
|
||||
|
||||
|
||||
def _locateAll_opencv(needleImage, haystackImage, grayscale=None, limit=10000, region=None, step=1,
|
||||
confidence=0.999):
|
||||
""" faster but more memory-intensive than pure python
|
||||
step 2 skips every other row and column = ~3x faster but prone to miss;
|
||||
to compensate, the algorithm automatically reduces the confidence
|
||||
threshold by 5% (which helps but will not avoid all misses).
|
||||
limitations:
|
||||
- OpenCV 3.x & python 3.x not tested
|
||||
- RGBA images are treated as RBG (ignores alpha channel)
|
||||
"""
|
||||
if grayscale is None:
|
||||
grayscale = GRAYSCALE_DEFAULT
|
||||
|
||||
confidence = float(confidence)
|
||||
|
||||
needleImage = _load_cv2(needleImage, grayscale)
|
||||
needleHeight, needleWidth = needleImage.shape[:2]
|
||||
haystackImage = _load_cv2(haystackImage, grayscale)
|
||||
|
||||
if region:
|
||||
haystackImage = haystackImage[region[1]:region[1]+region[3],
|
||||
region[0]:region[0]+region[2]]
|
||||
else:
|
||||
region = (0, 0) # full image; these values used in the yield statement
|
||||
if (haystackImage.shape[0] < needleImage.shape[0] or
|
||||
haystackImage.shape[1] < needleImage.shape[1]):
|
||||
# avoid semi-cryptic OpenCV error below if bad size
|
||||
raise ValueError('needle dimension(s) exceed the haystack image or region dimensions')
|
||||
|
||||
if step == 2:
|
||||
confidence *= 0.95
|
||||
needleImage = needleImage[::step, ::step]
|
||||
haystackImage = haystackImage[::step, ::step]
|
||||
else:
|
||||
step = 1
|
||||
|
||||
# get all matches at once, credit: https://stackoverflow.com/questions/7670112/finding-a-subimage-inside-a-numpy-image/9253805#9253805
|
||||
result = cv2.matchTemplate(haystackImage, needleImage, cv2.TM_CCOEFF_NORMED)
|
||||
match_indices = numpy.arange(result.size)[(result > confidence).flatten()]
|
||||
matches = numpy.unravel_index(match_indices[:limit], result.shape)
|
||||
|
||||
if len(matches[0]) == 0:
|
||||
if USE_IMAGE_NOT_FOUND_EXCEPTION:
|
||||
raise ImageNotFoundException('Could not locate the image (highest confidence = %.3f)' % result.max())
|
||||
else:
|
||||
return
|
||||
|
||||
# use a generator for API consistency:
|
||||
matchx = matches[1] * step + region[0] # vectorized
|
||||
matchy = matches[0] * step + region[1]
|
||||
for x, y in zip(matchx, matchy):
|
||||
yield Box(x, y, needleWidth, needleHeight)
|
||||
|
||||
|
||||
def _locateAll_python(needleImage, haystackImage, grayscale=None, limit=None, region=None, step=1):
|
||||
# setup all the arguments
|
||||
if grayscale is None:
|
||||
grayscale = GRAYSCALE_DEFAULT
|
||||
|
||||
needleFileObj = None
|
||||
if isinstance(needleImage, str):
|
||||
# 'image' is a filename, load the Image object
|
||||
needleFileObj = open(needleImage, 'rb')
|
||||
needleImage = Image.open(needleFileObj)
|
||||
|
||||
haystackFileObj = None
|
||||
if isinstance(haystackImage, str):
|
||||
# 'image' is a filename, load the Image object
|
||||
haystackFileObj = open(haystackImage, 'rb')
|
||||
haystackImage = Image.open(haystackFileObj)
|
||||
|
||||
if region is not None:
|
||||
haystackImage = haystackImage.crop((region[0], region[1], region[0] + region[2], region[1] + region[3]))
|
||||
else:
|
||||
region = (0, 0) # set to 0 because the code always accounts for a region
|
||||
|
||||
if grayscale: # if grayscale mode is on, convert the needle and haystack images to grayscale
|
||||
needleImage = ImageOps.grayscale(needleImage)
|
||||
haystackImage = ImageOps.grayscale(haystackImage)
|
||||
else:
|
||||
# if not using grayscale, make sure we are comparing RGB images, not RGBA images.
|
||||
if needleImage.mode == 'RGBA':
|
||||
needleImage = needleImage.convert('RGB')
|
||||
if haystackImage.mode == 'RGBA':
|
||||
haystackImage = haystackImage.convert('RGB')
|
||||
|
||||
# setup some constants we'll be using in this function
|
||||
needleWidth, needleHeight = needleImage.size
|
||||
haystackWidth, haystackHeight = haystackImage.size
|
||||
|
||||
needleImageData = tuple(needleImage.getdata())
|
||||
haystackImageData = tuple(haystackImage.getdata())
|
||||
|
||||
needleImageRows = [needleImageData[y * needleWidth:(y+1) * needleWidth] for y in range(needleHeight)] # LEFT OFF - check this
|
||||
needleImageFirstRow = needleImageRows[0]
|
||||
|
||||
assert len(needleImageFirstRow) == needleWidth, 'For some reason, the calculated width of first row of the needle image is not the same as the width of the image.'
|
||||
assert [len(row) for row in needleImageRows] == [needleWidth] * needleHeight, 'For some reason, the needleImageRows aren\'t the same size as the original image.'
|
||||
|
||||
numMatchesFound = 0
|
||||
|
||||
# NOTE: After running tests/benchmarks.py on the following code, it seem that having a step
|
||||
# value greater than 1 does not give *any* significant performance improvements.
|
||||
# Since using a step higher than 1 makes for less accurate matches, it will be
|
||||
# set to 1.
|
||||
step = 1 # hard-code step as 1 until a way to improve it can be figured out.
|
||||
|
||||
if step == 1:
|
||||
firstFindFunc = _kmp
|
||||
else:
|
||||
firstFindFunc = _steppingFind
|
||||
|
||||
|
||||
for y in range(haystackHeight): # start at the leftmost column
|
||||
for matchx in firstFindFunc(needleImageFirstRow, haystackImageData[y * haystackWidth:(y+1) * haystackWidth], step):
|
||||
foundMatch = True
|
||||
for searchy in range(1, needleHeight, step):
|
||||
haystackStart = (searchy + y) * haystackWidth + matchx
|
||||
if needleImageData[searchy * needleWidth:(searchy+1) * needleWidth] != haystackImageData[haystackStart:haystackStart + needleWidth]:
|
||||
foundMatch = False
|
||||
break
|
||||
if foundMatch:
|
||||
# Match found, report the x, y, width, height of where the matching region is in haystack.
|
||||
numMatchesFound += 1
|
||||
yield Box(matchx + region[0], y + region[1], needleWidth, needleHeight)
|
||||
if limit is not None and numMatchesFound >= limit:
|
||||
# Limit has been reached. Close file handles.
|
||||
if needleFileObj is not None:
|
||||
needleFileObj.close()
|
||||
if haystackFileObj is not None:
|
||||
haystackFileObj.close()
|
||||
return
|
||||
|
||||
|
||||
# There was no limit or the limit wasn't reached, but close the file handles anyway.
|
||||
if needleFileObj is not None:
|
||||
needleFileObj.close()
|
||||
if haystackFileObj is not None:
|
||||
haystackFileObj.close()
|
||||
|
||||
if numMatchesFound == 0:
|
||||
if USE_IMAGE_NOT_FOUND_EXCEPTION:
|
||||
raise ImageNotFoundException('Could not locate the image.')
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
def locate(needleImage, haystackImage, **kwargs):
|
||||
# Note: The gymnastics in this function is because we want to make sure to exhaust the iterator so that the needle and haystack files are closed in locateAll.
|
||||
kwargs['limit'] = 1
|
||||
points = tuple(locateAll(needleImage, haystackImage, **kwargs))
|
||||
if len(points) > 0:
|
||||
return points[0]
|
||||
else:
|
||||
if USE_IMAGE_NOT_FOUND_EXCEPTION:
|
||||
raise ImageNotFoundException('Could not locate the image.')
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def locateOnScreen(image, minSearchTime=0, **kwargs):
|
||||
"""minSearchTime - amount of time in seconds to repeat taking
|
||||
screenshots and trying to locate a match. The default of 0 performs
|
||||
a single search.
|
||||
"""
|
||||
start = time.time()
|
||||
while True:
|
||||
try:
|
||||
screenshotIm = screenshot(region=None) # the locateAll() function must handle cropping to return accurate coordinates, so don't pass a region here.
|
||||
retVal = locate(image, screenshotIm, **kwargs)
|
||||
try:
|
||||
screenshotIm.fp.close()
|
||||
except AttributeError:
|
||||
# Screenshots on Windows won't have an fp since they came from
|
||||
# ImageGrab, not a file. Screenshots on Linux will have fp set
|
||||
# to None since the file has been unlinked
|
||||
pass
|
||||
if retVal or time.time() - start > minSearchTime:
|
||||
return retVal
|
||||
except ImageNotFoundException:
|
||||
if time.time() - start > minSearchTime:
|
||||
if USE_IMAGE_NOT_FOUND_EXCEPTION:
|
||||
raise
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def locateAllOnScreen(image, **kwargs):
|
||||
screenshotIm = screenshot(region=None) # the locateAll() function must handle cropping to return accurate coordinates, so don't pass a region here.
|
||||
retVal = locateAll(image, screenshotIm, **kwargs)
|
||||
try:
|
||||
screenshotIm.fp.close()
|
||||
except AttributeError:
|
||||
# Screenshots on Windows won't have an fp since they came from
|
||||
# ImageGrab, not a file. Screenshots on Linux will have fp set
|
||||
# to None since the file has been unlinked
|
||||
pass
|
||||
return retVal
|
||||
|
||||
|
||||
def locateCenterOnScreen(image, **kwargs):
|
||||
coords = locateOnScreen(image, **kwargs)
|
||||
return center(coords)
|
||||
|
||||
|
||||
def showRegionOnScreen(region, outlineColor='red', filename='_showRegionOnScreen.png'):
|
||||
from PIL import ImageDraw # this is the only function that needs this, and it's rarely called
|
||||
screenshotIm = screenshot()
|
||||
draw = ImageDraw.Draw(screenshotIm)
|
||||
region = (region[0], region[1], region[2] + region[0], region[3] + region[1]) # convert from (left, top, right, bottom) to (left, top, width, height)
|
||||
draw.rectangle(region, outline=outlineColor)
|
||||
screenshotIm.save(filename)
|
||||
|
||||
|
||||
def _screenshot_win32(imageFilename=None, region=None):
|
||||
try:
|
||||
im = ImageGrab.grab()
|
||||
except NameError:
|
||||
raise ImportError('Pillow module must be installed to use screenshot functions on Windows.')
|
||||
if region is not None:
|
||||
assert len(region) == 4, 'region argument must be a tuple of four ints'
|
||||
region = [int(x) for x in region]
|
||||
im = im.crop((region[0], region[1], region[2] + region[0], region[3] + region[1]))
|
||||
if imageFilename is not None:
|
||||
im.save(imageFilename)
|
||||
return im
|
||||
|
||||
|
||||
def _screenshot_osx(imageFilename=None, region=None):
|
||||
if imageFilename is None:
|
||||
tmpFilename = 'screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
|
||||
else:
|
||||
tmpFilename = imageFilename
|
||||
subprocess.call(['screencapture', '-x', tmpFilename])
|
||||
im = Image.open(tmpFilename)
|
||||
|
||||
if region is not None:
|
||||
assert len(region) == 4, 'region argument must be a tuple of four ints'
|
||||
region = [int(x) for x in region]
|
||||
im = im.crop((region[0], region[1], region[2] + region[0], region[3] + region[1]))
|
||||
os.unlink(tmpFilename) # delete image of entire screen to save cropped version
|
||||
im.save(tmpFilename)
|
||||
else:
|
||||
# force loading before unlinking, Image.open() is lazy
|
||||
im.load()
|
||||
|
||||
if imageFilename is None:
|
||||
os.unlink(tmpFilename)
|
||||
return im
|
||||
|
||||
|
||||
def _screenshot_linux(imageFilename=None, region=None):
|
||||
if not scrotExists:
|
||||
raise NotImplementedError('"scrot" must be installed to use screenshot functions in Linux. Run: sudo apt-get install scrot')
|
||||
if imageFilename is None:
|
||||
tmpFilename = '.screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
|
||||
else:
|
||||
tmpFilename = imageFilename
|
||||
if scrotExists:
|
||||
subprocess.call(['scrot', tmpFilename])
|
||||
im = Image.open(tmpFilename)
|
||||
|
||||
if region is not None:
|
||||
assert len(region) == 4, 'region argument must be a tuple of four ints'
|
||||
region = [int(x) for x in region]
|
||||
im = im.crop((region[0], region[1], region[2] + region[0], region[3] + region[1]))
|
||||
os.unlink(tmpFilename) # delete image of entire screen to save cropped version
|
||||
im.save(tmpFilename)
|
||||
else:
|
||||
# force loading before unlinking, Image.open() is lazy
|
||||
im.load()
|
||||
|
||||
if imageFilename is None:
|
||||
os.unlink(tmpFilename)
|
||||
return im
|
||||
else:
|
||||
raise Exception('The scrot program must be installed to take a screenshot with PyScreeze on Linux. Run: sudo apt-get install scrot')
|
||||
|
||||
|
||||
|
||||
def _kmp(needle, haystack, _dummy): # Knuth-Morris-Pratt search algorithm implementation (to be used by screen capture)
|
||||
# build table of shift amounts
|
||||
shifts = [1] * (len(needle) + 1)
|
||||
shift = 1
|
||||
for pos in range(len(needle)):
|
||||
while shift <= pos and needle[pos] != needle[pos-shift]:
|
||||
shift += shifts[pos-shift]
|
||||
shifts[pos+1] = shift
|
||||
|
||||
# do the actual search
|
||||
startPos = 0
|
||||
matchLen = 0
|
||||
for c in haystack:
|
||||
while matchLen == len(needle) or \
|
||||
matchLen >= 0 and needle[matchLen] != c:
|
||||
startPos += shifts[matchLen]
|
||||
matchLen -= shifts[matchLen]
|
||||
matchLen += 1
|
||||
if matchLen == len(needle):
|
||||
yield startPos
|
||||
|
||||
|
||||
def _steppingFind(needle, haystack, step):
|
||||
for startPos in range(0, len(haystack) - len(needle) + 1):
|
||||
foundMatch = True
|
||||
for pos in range(0, len(needle), step):
|
||||
if haystack[startPos + pos] != needle[pos]:
|
||||
foundMatch = False
|
||||
break
|
||||
if foundMatch:
|
||||
yield startPos
|
||||
|
||||
|
||||
def center(coords):
|
||||
return Point(coords[0] + int(coords[2] / 2), coords[1] + int(coords[3] / 2))
|
||||
|
||||
|
||||
def pixelMatchesColor(x, y, expectedRGBColor, tolerance=0):
|
||||
pix = pixel(x, y)
|
||||
if len(pix) == 3 or len(expectedRGBColor) == 3: #RGB mode
|
||||
r, g, b = pix[:3]
|
||||
exR, exG, exB = expectedRGBColor[:3]
|
||||
return (abs(r - exR) <= tolerance) and (abs(g - exG) <= tolerance) and (abs(b - exB) <= tolerance)
|
||||
elif len(pix) == 4 and len(expectedRGBColor) == 4: #RGBA mode
|
||||
r, g, b, a = pix
|
||||
exR, exG, exB, exA = expectedRGBColor
|
||||
return (abs(r - exR) <= tolerance) and (abs(g - exG) <= tolerance) and (abs(b - exB) <= tolerance) and (abs(a - exA) <= tolerance)
|
||||
else:
|
||||
assert False, 'Color mode was expected to be length 3 (RGB) or 4 (RGBA), but pixel is length %s and expectedRGBColor is length %s' % (len(pix), len(expectedRGBColor))
|
||||
|
||||
def pixel(x, y):
|
||||
if sys.platform == 'win32':
|
||||
# On Windows, calling GetDC() and GetPixel() is twice as fast as using our screenshot() function.
|
||||
with __win32_openDC(0) as hdc: # handle will be released automatically
|
||||
color = windll.gdi32.GetPixel(hdc, x, y)
|
||||
if color < 0:
|
||||
raise WindowsError("windll.gdi32.GetPixel faild : return {}".format(color))
|
||||
# color is in the format 0xbbggrr https://msdn.microsoft.com/en-us/library/windows/desktop/dd183449(v=vs.85).aspx
|
||||
bbggrr = "{:0>6x}".format(color) # bbggrr => 'bbggrr' (hex)
|
||||
b, g, r = (int(bbggrr[i:i+2], 16) for i in range(0, 6, 2))
|
||||
return (r, g, b)
|
||||
else:
|
||||
# Need to select only the first three values of the color in
|
||||
# case the returned pixel has an alpha channel
|
||||
return RGB(*(screenshot().getpixel((x, y))[:3]))
|
||||
|
||||
|
||||
# set the screenshot() function based on the platform running this module
|
||||
if sys.platform.startswith('java'):
|
||||
raise NotImplementedError('Jython is not yet supported by PyScreeze.')
|
||||
elif sys.platform == 'darwin':
|
||||
screenshot = _screenshot_osx
|
||||
elif sys.platform == 'win32':
|
||||
screenshot = _screenshot_win32
|
||||
try:
|
||||
from PIL import ImageGrab
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
screenshot = _screenshot_linux
|
||||
|
||||
grab = screenshot # for compatibility with Pillow/PIL's ImageGrab module.
|
||||
|
||||
# set the locateAll function to use opencv if possible; python 3 needs opencv 3.0+
|
||||
if useOpenCV:
|
||||
locateAll = _locateAll_opencv
|
||||
if not RUNNING_PYTHON_2 and cv2.__version__ < '3':
|
||||
locateAll = _locateAll_python
|
||||
else:
|
||||
locateAll = _locateAll_python
|
@ -0,0 +1,594 @@
|
||||
from __future__ import division
|
||||
|
||||
import math
|
||||
|
||||
__version__ = '1.0.3'
|
||||
|
||||
|
||||
# from http://www.roguebasin.com/index.php?title=Bresenham%27s_Line_Algorithm#Python
|
||||
def getLine(x1, y1, x2, y2):
|
||||
"""Returns a list of (x, y) tuples of every point on a line between
|
||||
(x1, y1) and (x2, y2). The x and y values inside the tuple are integers.
|
||||
|
||||
Line generated with the Bresenham algorithm.
|
||||
|
||||
Args:
|
||||
x1 (int, float): The x coordinate of the line's start point.
|
||||
y1 (int, float): The y coordinate of the line's start point.
|
||||
x2 (int, float): The x coordinate of the line's end point.
|
||||
y2 (int, float): The y coordiante of the line's end point.
|
||||
|
||||
Returns:
|
||||
[(x1, y1), (x2, y2), (x3, y3), ...]
|
||||
|
||||
Example:
|
||||
>>> getLine(0, 0, 6, 6)
|
||||
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]
|
||||
>>> getLine(0, 0, 3, 6)
|
||||
[(0, 0), (0, 1), (1, 2), (1, 3), (2, 4), (2, 5), (3, 6)]
|
||||
>>> getLine(3, 3, -3, -3)
|
||||
[(3, 3), (2, 2), (1, 1), (0, 0), (-1, -1), (-2, -2), (-3, -3)]
|
||||
"""
|
||||
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
|
||||
points = []
|
||||
issteep = abs(y2-y1) > abs(x2-x1)
|
||||
if issteep:
|
||||
x1, y1 = y1, x1
|
||||
x2, y2 = y2, x2
|
||||
rev = False
|
||||
if x1 > x2:
|
||||
x1, x2 = x2, x1
|
||||
y1, y2 = y2, y1
|
||||
rev = True
|
||||
deltax = x2 - x1
|
||||
deltay = abs(y2-y1)
|
||||
error = int(deltax / 2)
|
||||
y = y1
|
||||
ystep = None
|
||||
if y1 < y2:
|
||||
ystep = 1
|
||||
else:
|
||||
ystep = -1
|
||||
for x in range(x1, x2 + 1):
|
||||
if issteep:
|
||||
points.append((y, x))
|
||||
else:
|
||||
points.append((x, y))
|
||||
error -= deltay
|
||||
if error < 0:
|
||||
y += ystep
|
||||
error += deltax
|
||||
# Reverse the list if the coordinates were reversed
|
||||
if rev:
|
||||
points.reverse()
|
||||
return points
|
||||
|
||||
|
||||
def getPointOnLine(x1, y1, x2, y2, n):
|
||||
"""Returns the (x, y) tuple of the point that has progressed a proportion
|
||||
n along the line defined by the two x, y coordinates.
|
||||
|
||||
Args:
|
||||
x1 (int, float): The x coordinate of the line's start point.
|
||||
y1 (int, float): The y coordinate of the line's start point.
|
||||
x2 (int, float): The x coordinate of the line's end point.
|
||||
y2 (int, float): The y coordiante of the line's end point.
|
||||
n (float): Progress along the line. 0.0 is the start point, 1.0 is the end point. 0.5 is the midpoint. This value can be less than 0.0 or greater than 1.0.
|
||||
|
||||
Returns:
|
||||
Tuple of floats for the x, y coordinate of the point.
|
||||
|
||||
Example:
|
||||
>>> getPointOnLine(0, 0, 6, 6, 0)
|
||||
(0, 0)
|
||||
>>> getPointOnLine(0, 0, 6, 6, 1)
|
||||
(6, 6)
|
||||
>>> getPointOnLine(0, 0, 6, 6, 0.5)
|
||||
(3.0, 3.0)
|
||||
>>> getPointOnLine(0, 0, 6, 6, 0.75)
|
||||
(4.5, 4.5)
|
||||
>>> getPointOnLine(3, 3, -3, -3, 0.5)
|
||||
(0.0, 0.0)
|
||||
>>> getPointOnLine(3, 3, -3, -3, 0.25)
|
||||
(1.5, 1.5)
|
||||
>>> getPointOnLine(3, 3, -3, -3, 0.75)
|
||||
(-1.5, -1.5)
|
||||
"""
|
||||
x = ((x2 - x1) * n) + x1
|
||||
y = ((y2 - y1) * n) + y1
|
||||
return (x, y)
|
||||
|
||||
|
||||
def _checkRange(n):
|
||||
"""Raises ValueError if the argument is not between 0.0 and 1.0.
|
||||
"""
|
||||
if not 0.0 <= n <= 1.0:
|
||||
raise ValueError('Argument must be between 0.0 and 1.0.')
|
||||
|
||||
|
||||
def linear(n):
|
||||
"""A linear tween function
|
||||
|
||||
Example:
|
||||
>>> linear(0.0)
|
||||
0.0
|
||||
>>> linear(0.2)
|
||||
0.2
|
||||
>>> linear(0.4)
|
||||
0.4
|
||||
>>> linear(0.6)
|
||||
0.6
|
||||
>>> linear(0.8)
|
||||
0.8
|
||||
>>> linear(1.0)
|
||||
1.0
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n
|
||||
|
||||
|
||||
def easeInQuad(n):
|
||||
"""A quadratic tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n**2
|
||||
|
||||
|
||||
def easeOutQuad(n):
|
||||
"""A quadratic tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return -n * (n-2)
|
||||
|
||||
|
||||
def easeInOutQuad(n):
|
||||
"""A quadratic tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n < 0.5:
|
||||
return 2 * n**2
|
||||
else:
|
||||
n = n * 2 - 1
|
||||
return -0.5 * (n*(n-2) - 1)
|
||||
|
||||
|
||||
def easeInCubic(n):
|
||||
"""A cubic tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n**3
|
||||
|
||||
|
||||
def easeOutCubic(n):
|
||||
"""A cubic tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n - 1
|
||||
return n**3 + 1
|
||||
|
||||
|
||||
def easeInOutCubic(n):
|
||||
"""A cubic tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = 2 * n
|
||||
if n < 1:
|
||||
return 0.5 * n**3
|
||||
else:
|
||||
n = n - 2
|
||||
return 0.5 * (n**3 + 2)
|
||||
|
||||
|
||||
def easeInQuart(n):
|
||||
"""A quartic tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n**4
|
||||
|
||||
|
||||
def easeOutQuart(n):
|
||||
"""A quartic tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n - 1
|
||||
return -(n**4 - 1)
|
||||
|
||||
|
||||
def easeInOutQuart(n):
|
||||
"""A quartic tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = 2 * n
|
||||
if n < 1:
|
||||
return 0.5 * n**4
|
||||
else:
|
||||
n = n - 2
|
||||
return -0.5 * (n**4 - 2)
|
||||
|
||||
|
||||
def easeInQuint(n):
|
||||
"""A quintic tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n**5
|
||||
|
||||
|
||||
def easeOutQuint(n):
|
||||
"""A quintic tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n - 1
|
||||
return n**5 + 1
|
||||
|
||||
|
||||
def easeInOutQuint(n):
|
||||
"""A quintic tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = 2 * n
|
||||
if n < 1:
|
||||
return 0.5 * n**5
|
||||
else:
|
||||
n = n - 2
|
||||
return 0.5 * (n**5 + 2)
|
||||
|
||||
|
||||
def easeInSine(n):
|
||||
"""A sinusoidal tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return -1 * math.cos(n * math.pi / 2) + 1
|
||||
|
||||
|
||||
def easeOutSine(n):
|
||||
"""A sinusoidal tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return math.sin(n * math.pi / 2)
|
||||
|
||||
|
||||
def easeInOutSine(n):
|
||||
"""A sinusoidal tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return -0.5 * (math.cos(math.pi * n) - 1)
|
||||
|
||||
|
||||
def easeInExpo(n):
|
||||
"""An exponential tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n == 0:
|
||||
return 0
|
||||
else:
|
||||
return 2**(10 * (n - 1))
|
||||
|
||||
|
||||
def easeOutExpo(n):
|
||||
"""An exponential tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n == 1:
|
||||
return 1
|
||||
else:
|
||||
return -(2 ** (-10 * n)) + 1
|
||||
|
||||
|
||||
def easeInOutExpo(n):
|
||||
"""An exponential tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n == 0:
|
||||
return 0
|
||||
elif n == 1:
|
||||
return 1
|
||||
else:
|
||||
n = n * 2
|
||||
if n < 1:
|
||||
return 0.5 * 2**(10 * (n - 1))
|
||||
else:
|
||||
n -= 1
|
||||
# 0.5 * (-() + 2)
|
||||
return 0.5 * (-1 * (2 ** (-10 * n)) + 2)
|
||||
|
||||
|
||||
def easeInCirc(n):
|
||||
"""A circular tween function that begins slow and then accelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return -1 * (math.sqrt(1 - n * n) - 1)
|
||||
|
||||
|
||||
def easeOutCirc(n):
|
||||
"""A circular tween function that begins fast and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n -= 1
|
||||
return math.sqrt(1 - (n * n))
|
||||
|
||||
|
||||
def easeInOutCirc(n):
|
||||
"""A circular tween function that accelerates, reaches the midpoint, and then decelerates.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n * 2
|
||||
if n < 1:
|
||||
return -0.5 * (math.sqrt(1 - n**2) - 1)
|
||||
else:
|
||||
n = n - 2
|
||||
return 0.5 * (math.sqrt(1 - n**2) + 1)
|
||||
|
||||
|
||||
def easeInElastic(n, amplitude=1, period=0.3):
|
||||
"""An elastic tween function that begins with an increasing wobble and then snaps into the destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return 1 - easeOutElastic(1-n, amplitude=amplitude, period=period)
|
||||
|
||||
|
||||
def easeOutElastic(n, amplitude=1, period=0.3):
|
||||
"""An elastic tween function that overshoots the destination and then "rubber bands" into the destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
|
||||
if amplitude < 1:
|
||||
amplitude = 1
|
||||
s = period / 4
|
||||
else:
|
||||
s = period / (2 * math.pi) * math.asin(1 / amplitude)
|
||||
|
||||
return amplitude * 2**(-10*n) * math.sin((n-s)*(2*math.pi / period)) + 1
|
||||
|
||||
|
||||
def easeInOutElastic(n, amplitude=1, period=0.5):
|
||||
"""An elastic tween function wobbles towards the midpoint.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n *= 2
|
||||
if n < 1:
|
||||
return easeInElastic(n, amplitude=amplitude, period=period) / 2
|
||||
else:
|
||||
return easeOutElastic(n-1, amplitude=amplitude, period=period) / 2 + 0.5
|
||||
|
||||
|
||||
def easeInBack(n, s=1.70158):
|
||||
"""A tween function that backs up first at the start and then goes to the destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return n * n * ((s + 1) * n - s)
|
||||
|
||||
|
||||
def easeOutBack(n, s=1.70158):
|
||||
"""A tween function that overshoots the destination a little and then backs into the destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n - 1
|
||||
return n * n * ((s + 1) * n + s) + 1
|
||||
|
||||
|
||||
def easeInOutBack(n, s=1.70158):
|
||||
"""A "back-in" tween function that overshoots both the start and destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
n = n * 2
|
||||
if n < 1:
|
||||
s *= 1.525
|
||||
return 0.5 * (n * n * ((s + 1) * n - s))
|
||||
else:
|
||||
n -= 2
|
||||
s *= 1.525
|
||||
return 0.5 * (n * n * ((s + 1) * n + s) + 2)
|
||||
|
||||
|
||||
def easeInBounce(n):
|
||||
"""A bouncing tween function that begins bouncing and then jumps to the destination.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
return 1 - easeOutBounce(1 - n)
|
||||
|
||||
|
||||
def easeOutBounce(n):
|
||||
"""A bouncing tween function that hits the destination and then bounces to rest.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n < (1/2.75):
|
||||
return 7.5625 * n * n
|
||||
elif n < (2/2.75):
|
||||
n -= (1.5/2.75)
|
||||
return 7.5625 * n * n + 0.75
|
||||
elif n < (2.5/2.75):
|
||||
n -= (2.25/2.75)
|
||||
return 7.5625 * n * n + 0.9375
|
||||
else:
|
||||
n -= (2.65/2.75)
|
||||
return 7.5625 * n * n + 0.984375
|
||||
|
||||
|
||||
def easeInOutBounce(n):
|
||||
"""A bouncing tween function that bounces at the start and end.
|
||||
|
||||
Args:
|
||||
n (float): The time progress, starting at 0.0 and ending at 1.0.
|
||||
|
||||
Returns:
|
||||
(float) The line progress, starting at 0.0 and ending at 1.0. Suitable for passing to getPointOnLine().
|
||||
"""
|
||||
_checkRange(n)
|
||||
if n < 0.5:
|
||||
return easeInBounce(n * 2) * 0.5
|
||||
else:
|
||||
return easeOutBounce(n * 2 - 1) * 0.5 + 0.5
|
@ -0,0 +1,3 @@
|
||||
from comtypes.gen import _944DE083_8FB8_45CF_BCB7_C477ACB2F897_0_1_0
|
||||
globals().update(_944DE083_8FB8_45CF_BCB7_C477ACB2F897_0_1_0.__dict__)
|
||||
__name__ = 'comtypes.gen.UIAutomationClient'
|
@ -0,0 +1,396 @@
|
||||
# -*- coding: mbcs -*-
|
||||
typelib_path = 'C:\\Windows\\System32\\stdole2.tlb'
|
||||
_lcid = 0 # change this if required
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import VARIANT_BOOL
|
||||
FONTITALIC = VARIANT_BOOL
|
||||
from comtypes import IUnknown
|
||||
OLE_COLOR = c_ulong
|
||||
FONTUNDERSCORE = VARIANT_BOOL
|
||||
from comtypes.automation import IDispatch
|
||||
from comtypes import GUID
|
||||
from comtypes import BSTR
|
||||
from comtypes import dispid
|
||||
from comtypes import DISPMETHOD, DISPPROPERTY, helpstring
|
||||
FONTSTRIKETHROUGH = VARIANT_BOOL
|
||||
OLE_XPOS_PIXELS = c_int
|
||||
OLE_HANDLE = c_int
|
||||
OLE_XSIZE_HIMETRIC = c_int
|
||||
OLE_YSIZE_HIMETRIC = c_int
|
||||
OLE_XPOS_HIMETRIC = c_int
|
||||
OLE_YPOS_HIMETRIC = c_int
|
||||
from ctypes import HRESULT
|
||||
from comtypes import helpstring
|
||||
from comtypes import COMMETHOD
|
||||
from comtypes import CoClass
|
||||
FONTNAME = BSTR
|
||||
from comtypes import GUID
|
||||
FONTSIZE = c_longlong
|
||||
OLE_YPOS_PIXELS = c_int
|
||||
OLE_XSIZE_PIXELS = c_int
|
||||
from comtypes.automation import DISPPARAMS
|
||||
OLE_YSIZE_PIXELS = c_int
|
||||
OLE_XPOS_CONTAINER = c_float
|
||||
from comtypes.automation import IEnumVARIANT
|
||||
OLE_YPOS_CONTAINER = c_float
|
||||
OLE_XSIZE_CONTAINER = c_float
|
||||
OLE_YSIZE_CONTAINER = c_float
|
||||
from comtypes.automation import EXCEPINFO
|
||||
OLE_OPTEXCLUSIVE = VARIANT_BOOL
|
||||
OLE_CANCELBOOL = VARIANT_BOOL
|
||||
OLE_ENABLEDEFAULTBOOL = VARIANT_BOOL
|
||||
FONTBOLD = VARIANT_BOOL
|
||||
|
||||
|
||||
class FontEvents(IDispatch):
|
||||
_case_insensitive_ = True
|
||||
'Event interface for the Font object'
|
||||
_iid_ = GUID('{4EF6100A-AF88-11D0-9846-00C04FC29993}')
|
||||
_idlflags_ = ['hidden']
|
||||
_methods_ = []
|
||||
FontEvents._disp_methods_ = [
|
||||
DISPMETHOD([dispid(9)], None, 'FontChanged',
|
||||
( ['in'], BSTR, 'PropertyName' )),
|
||||
]
|
||||
|
||||
# values for enumeration 'LoadPictureConstants'
|
||||
Default = 0
|
||||
Monochrome = 1
|
||||
VgaColor = 2
|
||||
Color = 4
|
||||
LoadPictureConstants = c_int # enum
|
||||
class Picture(IDispatch):
|
||||
_case_insensitive_ = True
|
||||
_iid_ = GUID('{7BF80981-BF32-101A-8BBB-00AA00300CAB}')
|
||||
_idlflags_ = []
|
||||
_methods_ = []
|
||||
Picture._disp_methods_ = [
|
||||
DISPPROPERTY([dispid(0), 'readonly'], OLE_HANDLE, 'Handle'),
|
||||
DISPPROPERTY([dispid(2)], OLE_HANDLE, 'hPal'),
|
||||
DISPPROPERTY([dispid(3), 'readonly'], c_short, 'Type'),
|
||||
DISPPROPERTY([dispid(4), 'readonly'], OLE_XSIZE_HIMETRIC, 'Width'),
|
||||
DISPPROPERTY([dispid(5), 'readonly'], OLE_YSIZE_HIMETRIC, 'Height'),
|
||||
DISPMETHOD([dispid(6)], None, 'Render',
|
||||
( [], c_int, 'hdc' ),
|
||||
( [], c_int, 'x' ),
|
||||
( [], c_int, 'y' ),
|
||||
( [], c_int, 'cx' ),
|
||||
( [], c_int, 'cy' ),
|
||||
( [], OLE_XPOS_HIMETRIC, 'xSrc' ),
|
||||
( [], OLE_YPOS_HIMETRIC, 'ySrc' ),
|
||||
( [], OLE_XSIZE_HIMETRIC, 'cxSrc' ),
|
||||
( [], OLE_YSIZE_HIMETRIC, 'cySrc' ),
|
||||
( [], c_void_p, 'prcWBounds' )),
|
||||
]
|
||||
IPictureDisp = Picture
|
||||
class IFont(IUnknown):
|
||||
_case_insensitive_ = True
|
||||
'Font Object'
|
||||
_iid_ = GUID('{BEF6E002-A874-101A-8BBA-00AA00300CAB}')
|
||||
_idlflags_ = ['hidden']
|
||||
IFont._methods_ = [
|
||||
COMMETHOD(['propget'], HRESULT, 'Name',
|
||||
( ['out', 'retval'], POINTER(BSTR), 'pname' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Name',
|
||||
( ['in'], BSTR, 'pname' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Size',
|
||||
( ['out', 'retval'], POINTER(c_longlong), 'psize' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Size',
|
||||
( ['in'], c_longlong, 'psize' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Bold',
|
||||
( ['out', 'retval'], POINTER(VARIANT_BOOL), 'pbold' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Bold',
|
||||
( ['in'], VARIANT_BOOL, 'pbold' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Italic',
|
||||
( ['out', 'retval'], POINTER(VARIANT_BOOL), 'pitalic' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Italic',
|
||||
( ['in'], VARIANT_BOOL, 'pitalic' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Underline',
|
||||
( ['out', 'retval'], POINTER(VARIANT_BOOL), 'punderline' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Underline',
|
||||
( ['in'], VARIANT_BOOL, 'punderline' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Strikethrough',
|
||||
( ['out', 'retval'], POINTER(VARIANT_BOOL), 'pstrikethrough' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Strikethrough',
|
||||
( ['in'], VARIANT_BOOL, 'pstrikethrough' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Weight',
|
||||
( ['out', 'retval'], POINTER(c_short), 'pweight' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Weight',
|
||||
( ['in'], c_short, 'pweight' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Charset',
|
||||
( ['out', 'retval'], POINTER(c_short), 'pcharset' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'Charset',
|
||||
( ['in'], c_short, 'pcharset' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'hFont',
|
||||
( ['out', 'retval'], POINTER(OLE_HANDLE), 'phfont' )),
|
||||
COMMETHOD([], HRESULT, 'Clone',
|
||||
( ['out'], POINTER(POINTER(IFont)), 'ppfont' )),
|
||||
COMMETHOD([], HRESULT, 'IsEqual',
|
||||
( ['in'], POINTER(IFont), 'pfontOther' )),
|
||||
COMMETHOD([], HRESULT, 'SetRatio',
|
||||
( ['in'], c_int, 'cyLogical' ),
|
||||
( ['in'], c_int, 'cyHimetric' )),
|
||||
COMMETHOD([], HRESULT, 'AddRefHfont',
|
||||
( ['in'], OLE_HANDLE, 'hFont' )),
|
||||
COMMETHOD([], HRESULT, 'ReleaseHfont',
|
||||
( ['in'], OLE_HANDLE, 'hFont' )),
|
||||
]
|
||||
################################################################
|
||||
## code template for IFont implementation
|
||||
##class IFont_Impl(object):
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pname
|
||||
## def _set(self, pname):
|
||||
## '-no docstring-'
|
||||
## Name = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return psize
|
||||
## def _set(self, psize):
|
||||
## '-no docstring-'
|
||||
## Size = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pbold
|
||||
## def _set(self, pbold):
|
||||
## '-no docstring-'
|
||||
## Bold = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pitalic
|
||||
## def _set(self, pitalic):
|
||||
## '-no docstring-'
|
||||
## Italic = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return punderline
|
||||
## def _set(self, punderline):
|
||||
## '-no docstring-'
|
||||
## Underline = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pstrikethrough
|
||||
## def _set(self, pstrikethrough):
|
||||
## '-no docstring-'
|
||||
## Strikethrough = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pweight
|
||||
## def _set(self, pweight):
|
||||
## '-no docstring-'
|
||||
## Weight = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pcharset
|
||||
## def _set(self, pcharset):
|
||||
## '-no docstring-'
|
||||
## Charset = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## @property
|
||||
## def hFont(self):
|
||||
## '-no docstring-'
|
||||
## #return phfont
|
||||
##
|
||||
## def Clone(self):
|
||||
## '-no docstring-'
|
||||
## #return ppfont
|
||||
##
|
||||
## def IsEqual(self, pfontOther):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
## def SetRatio(self, cyLogical, cyHimetric):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
## def AddRefHfont(self, hFont):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
## def ReleaseHfont(self, hFont):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
|
||||
class StdPicture(CoClass):
|
||||
_reg_clsid_ = GUID('{0BE35204-8F91-11CE-9DE3-00AA004BB851}')
|
||||
_idlflags_ = []
|
||||
_typelib_path_ = typelib_path
|
||||
_reg_typelib_ = ('{00020430-0000-0000-C000-000000000046}', 2, 0)
|
||||
class IPicture(IUnknown):
|
||||
_case_insensitive_ = True
|
||||
'Picture Object'
|
||||
_iid_ = GUID('{7BF80980-BF32-101A-8BBB-00AA00300CAB}')
|
||||
_idlflags_ = ['hidden']
|
||||
StdPicture._com_interfaces_ = [Picture, IPicture]
|
||||
|
||||
class Library(object):
|
||||
'OLE Automation'
|
||||
name = 'stdole'
|
||||
_reg_typelib_ = ('{00020430-0000-0000-C000-000000000046}', 2, 0)
|
||||
|
||||
IFontEventsDisp = FontEvents
|
||||
class StdFont(CoClass):
|
||||
_reg_clsid_ = GUID('{0BE35203-8F91-11CE-9DE3-00AA004BB851}')
|
||||
_idlflags_ = []
|
||||
_typelib_path_ = typelib_path
|
||||
_reg_typelib_ = ('{00020430-0000-0000-C000-000000000046}', 2, 0)
|
||||
class Font(IDispatch):
|
||||
_case_insensitive_ = True
|
||||
_iid_ = GUID('{BEF6E003-A874-101A-8BBA-00AA00300CAB}')
|
||||
_idlflags_ = []
|
||||
_methods_ = []
|
||||
StdFont._com_interfaces_ = [Font, IFont]
|
||||
StdFont._outgoing_interfaces_ = [FontEvents]
|
||||
|
||||
IPicture._methods_ = [
|
||||
COMMETHOD(['propget'], HRESULT, 'Handle',
|
||||
( ['out', 'retval'], POINTER(OLE_HANDLE), 'phandle' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'hPal',
|
||||
( ['out', 'retval'], POINTER(OLE_HANDLE), 'phpal' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Type',
|
||||
( ['out', 'retval'], POINTER(c_short), 'ptype' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Width',
|
||||
( ['out', 'retval'], POINTER(OLE_XSIZE_HIMETRIC), 'pwidth' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Height',
|
||||
( ['out', 'retval'], POINTER(OLE_YSIZE_HIMETRIC), 'pheight' )),
|
||||
COMMETHOD([], HRESULT, 'Render',
|
||||
( ['in'], c_int, 'hdc' ),
|
||||
( ['in'], c_int, 'x' ),
|
||||
( ['in'], c_int, 'y' ),
|
||||
( ['in'], c_int, 'cx' ),
|
||||
( ['in'], c_int, 'cy' ),
|
||||
( ['in'], OLE_XPOS_HIMETRIC, 'xSrc' ),
|
||||
( ['in'], OLE_YPOS_HIMETRIC, 'ySrc' ),
|
||||
( ['in'], OLE_XSIZE_HIMETRIC, 'cxSrc' ),
|
||||
( ['in'], OLE_YSIZE_HIMETRIC, 'cySrc' ),
|
||||
( ['in'], c_void_p, 'prcWBounds' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'hPal',
|
||||
( ['in'], OLE_HANDLE, 'phpal' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'CurDC',
|
||||
( ['out', 'retval'], POINTER(c_int), 'phdcOut' )),
|
||||
COMMETHOD([], HRESULT, 'SelectPicture',
|
||||
( ['in'], c_int, 'hdcIn' ),
|
||||
( ['out'], POINTER(c_int), 'phdcOut' ),
|
||||
( ['out'], POINTER(OLE_HANDLE), 'phbmpOut' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'KeepOriginalFormat',
|
||||
( ['out', 'retval'], POINTER(VARIANT_BOOL), 'pfkeep' )),
|
||||
COMMETHOD(['propput'], HRESULT, 'KeepOriginalFormat',
|
||||
( ['in'], VARIANT_BOOL, 'pfkeep' )),
|
||||
COMMETHOD([], HRESULT, 'PictureChanged'),
|
||||
COMMETHOD([], HRESULT, 'SaveAsFile',
|
||||
( ['in'], c_void_p, 'pstm' ),
|
||||
( ['in'], VARIANT_BOOL, 'fSaveMemCopy' ),
|
||||
( ['out'], POINTER(c_int), 'pcbSize' )),
|
||||
COMMETHOD(['propget'], HRESULT, 'Attributes',
|
||||
( ['out', 'retval'], POINTER(c_int), 'pdwAttr' )),
|
||||
COMMETHOD([], HRESULT, 'SetHdc',
|
||||
( ['in'], OLE_HANDLE, 'hdc' )),
|
||||
]
|
||||
################################################################
|
||||
## code template for IPicture implementation
|
||||
##class IPicture_Impl(object):
|
||||
## @property
|
||||
## def Handle(self):
|
||||
## '-no docstring-'
|
||||
## #return phandle
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return phpal
|
||||
## def _set(self, phpal):
|
||||
## '-no docstring-'
|
||||
## hPal = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## @property
|
||||
## def Type(self):
|
||||
## '-no docstring-'
|
||||
## #return ptype
|
||||
##
|
||||
## @property
|
||||
## def Width(self):
|
||||
## '-no docstring-'
|
||||
## #return pwidth
|
||||
##
|
||||
## @property
|
||||
## def Height(self):
|
||||
## '-no docstring-'
|
||||
## #return pheight
|
||||
##
|
||||
## def Render(self, hdc, x, y, cx, cy, xSrc, ySrc, cxSrc, cySrc, prcWBounds):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
## @property
|
||||
## def CurDC(self):
|
||||
## '-no docstring-'
|
||||
## #return phdcOut
|
||||
##
|
||||
## def SelectPicture(self, hdcIn):
|
||||
## '-no docstring-'
|
||||
## #return phdcOut, phbmpOut
|
||||
##
|
||||
## def _get(self):
|
||||
## '-no docstring-'
|
||||
## #return pfkeep
|
||||
## def _set(self, pfkeep):
|
||||
## '-no docstring-'
|
||||
## KeepOriginalFormat = property(_get, _set, doc = _set.__doc__)
|
||||
##
|
||||
## def PictureChanged(self):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
## def SaveAsFile(self, pstm, fSaveMemCopy):
|
||||
## '-no docstring-'
|
||||
## #return pcbSize
|
||||
##
|
||||
## @property
|
||||
## def Attributes(self):
|
||||
## '-no docstring-'
|
||||
## #return pdwAttr
|
||||
##
|
||||
## def SetHdc(self, hdc):
|
||||
## '-no docstring-'
|
||||
## #return
|
||||
##
|
||||
|
||||
|
||||
# values for enumeration 'OLE_TRISTATE'
|
||||
Unchecked = 0
|
||||
Checked = 1
|
||||
Gray = 2
|
||||
OLE_TRISTATE = c_int # enum
|
||||
Font._disp_methods_ = [
|
||||
DISPPROPERTY([dispid(0)], BSTR, 'Name'),
|
||||
DISPPROPERTY([dispid(2)], c_longlong, 'Size'),
|
||||
DISPPROPERTY([dispid(3)], VARIANT_BOOL, 'Bold'),
|
||||
DISPPROPERTY([dispid(4)], VARIANT_BOOL, 'Italic'),
|
||||
DISPPROPERTY([dispid(5)], VARIANT_BOOL, 'Underline'),
|
||||
DISPPROPERTY([dispid(6)], VARIANT_BOOL, 'Strikethrough'),
|
||||
DISPPROPERTY([dispid(7)], c_short, 'Weight'),
|
||||
DISPPROPERTY([dispid(8)], c_short, 'Charset'),
|
||||
]
|
||||
IFontDisp = Font
|
||||
__all__ = [ 'OLE_TRISTATE', 'FONTITALIC', 'OLE_OPTEXCLUSIVE', 'Font',
|
||||
'OLE_ENABLEDEFAULTBOOL', 'OLE_YPOS_CONTAINER',
|
||||
'OLE_XPOS_HIMETRIC', 'OLE_XSIZE_CONTAINER', 'IPictureDisp',
|
||||
'OLE_YSIZE_CONTAINER', 'OLE_XPOS_CONTAINER',
|
||||
'OLE_CANCELBOOL', 'LoadPictureConstants', 'Color',
|
||||
'FONTBOLD', 'OLE_HANDLE', 'Checked', 'Default', 'FONTSIZE',
|
||||
'StdPicture', 'StdFont', 'FontEvents', 'FONTSTRIKETHROUGH',
|
||||
'IPicture', 'Unchecked', 'OLE_XSIZE_HIMETRIC', 'IFont',
|
||||
'Monochrome', 'OLE_COLOR', 'Gray', 'OLE_YPOS_HIMETRIC',
|
||||
'OLE_YPOS_PIXELS', 'OLE_YSIZE_PIXELS', 'OLE_XPOS_PIXELS',
|
||||
'IFontEventsDisp', 'IFontDisp', 'FONTUNDERSCORE',
|
||||
'OLE_XSIZE_PIXELS', 'VgaColor', 'Picture',
|
||||
'OLE_YSIZE_HIMETRIC', 'FONTNAME']
|
||||
from comtypes import _check_version; _check_version('')
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
# comtypes.gen package, directory for generated files.
|
@ -0,0 +1,3 @@
|
||||
from comtypes.gen import _00020430_0000_0000_C000_000000000046_0_2_0
|
||||
globals().update(_00020430_0000_0000_C000_000000000046_0_2_0.__dict__)
|
||||
__name__ = 'comtypes.gen.stdole'
|
Loading…
Reference in new issue