Skip to content

Commit

Permalink
Bug fixes + update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
SKaplanOfficial committed Jan 8, 2024
1 parent 85cb265 commit 4dc6df9
Show file tree
Hide file tree
Showing 32 changed files with 1,857 additions and 46 deletions.
451 changes: 451 additions & 0 deletions PyXA/Additions/Devices.py

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions PyXA/Additions/Speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def voices(self) -> list[str]:
for x in ls
]

def speak(self, path: Union[str, XABase.XAPath, None] = None):
def speak(self, path: Union[str, XABase.XAPath, None, list[str]] = None):
"""Speaks the provided message using the desired voice, volume, and speaking rate.
:param path: The path to a .AIFF file to output sound to, defaults to None
Expand Down Expand Up @@ -287,10 +287,16 @@ def speak(self, path: Union[str, XABase.XAPath, None] = None):
.. versionadded:: 0.0.9
"""
if isinstance(self.message, list):
self.message = "\n".join(self.message)

if self.message.strip() == "":
return

# Get the selected voice by name
voice = None
for v in AppKit.NSSpeechSynthesizer.availableVoices():
if self.voice.lower() in v.lower():
if self.voice is not None and self.voice.lower() in v.lower():
voice = v

# Set up speech synthesis object
Expand Down
12 changes: 9 additions & 3 deletions PyXA/XABase.py
Original file line number Diff line number Diff line change
Expand Up @@ -8292,11 +8292,13 @@ def beep(self):
"""
).run()

def play(self) -> "XASound":
def play(self, new_thread = False) -> "XASound":
"""Plays the sound from the beginning.
Audio playback runs in a separate thread. For the sound the play properly, you must keep the main thread alive over the duration of the desired playback.
:param new_thread: Whether to play the sound in a new thread, defaults to False
:type new_thread: bool, optional
:return: A reference to this sound object.
:rtype: XASound
Expand All @@ -8319,12 +8321,16 @@ def play_sound(self):
)
self.__audio_engine.startAndReturnError_(None)
self.__player_node.play()
while self.__player_node.isPlaying():
start_date = AppKit.NSDate.date()
while AppKit.NSDate.date().timeIntervalSinceDate_(start_date) < self.duration:
AppKit.NSRunLoop.currentRunLoop().runUntilDate_(
datetime.now() + timedelta(seconds=0.1)
)

self._spawn_thread(play_sound, [self])
if new_thread:
self._spawn_thread(play_sound, [self])
else:
play_sound(self)
return self

def pause(self) -> "XASound":
Expand Down
1 change: 1 addition & 0 deletions PyXA/apps/Bike.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class XABikeApplication(XABaseScriptable.XASBApplication, XACanOpenPath):
"""

class ObjectType(Enum):
"""Types of objects that can be created with :func:`XABikeApplication.make`."""
WINDOW = "window"
DOCUMENT = "document"
ROW = "row"
Expand Down
588 changes: 588 additions & 0 deletions docs/_modules/PyXA/Additions/Devices.html

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions docs/_modules/PyXA/Additions/Speech.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyXA.Additions.Speech &mdash; PyXA 0.2.2 documentation</title>
<title>PyXA.Additions.Speech &mdash; PyXA 0.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=80d5e7a1" />
<link rel="stylesheet" type="text/css" href="../../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../../_static/graphviz.css?v=eafc0fe6" />
Expand All @@ -15,7 +15,7 @@

<script src="../../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../../_static/documentation_options.js?v=000c92bf"></script>
<script src="../../../_static/documentation_options.js?v=a47416a6"></script>
<script src="../../../_static/doctools.js?v=888ff710"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/js/theme.js"></script>
Expand Down Expand Up @@ -359,7 +359,7 @@ <h1>Source code for PyXA.Additions.Speech</h1><div class="highlight"><pre>

<div class="viewcode-block" id="XASpeech.speak">
<a class="viewcode-back" href="../../../reference/additions/speech.html#PyXA.Additions.Speech.XASpeech.speak">[docs]</a>
<span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">XABase</span><span class="o">.</span><span class="n">XAPath</span><span class="p">,</span> <span class="kc">None</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">XABase</span><span class="o">.</span><span class="n">XAPath</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Speaks the provided message using the desired voice, volume, and speaking rate.</span>

<span class="sd"> :param path: The path to a .AIFF file to output sound to, defaults to None</span>
Expand Down Expand Up @@ -389,10 +389,16 @@ <h1>Source code for PyXA.Additions.Speech</h1><div class="highlight"><pre>

<span class="sd"> .. versionadded:: 0.0.9</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">message</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">message</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">message</span><span class="p">)</span>

<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;&quot;</span><span class="p">:</span>
<span class="k">return</span>

<span class="c1"># Get the selected voice by name</span>
<span class="n">voice</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">AppKit</span><span class="o">.</span><span class="n">NSSpeechSynthesizer</span><span class="o">.</span><span class="n">availableVoices</span><span class="p">():</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">voice</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">v</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">voice</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">voice</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">v</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
<span class="n">voice</span> <span class="o">=</span> <span class="n">v</span>

<span class="c1"># Set up speech synthesis object</span>
Expand Down
16 changes: 11 additions & 5 deletions docs/_modules/PyXA/XABase.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyXA.XABase &mdash; PyXA 0.2.2 documentation</title>
<title>PyXA.XABase &mdash; PyXA 0.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=80d5e7a1" />
<link rel="stylesheet" type="text/css" href="../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../_static/graphviz.css?v=eafc0fe6" />
Expand All @@ -15,7 +15,7 @@

<script src="../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../_static/documentation_options.js?v=000c92bf"></script>
<script src="../../_static/documentation_options.js?v=a47416a6"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/js/theme.js"></script>
Expand Down Expand Up @@ -9930,11 +9930,13 @@ <h1>Source code for PyXA.XABase</h1><div class="highlight"><pre>

<div class="viewcode-block" id="XASound.play">
<a class="viewcode-back" href="../../reference/xabase.html#PyXA.XABase.XASound.play">[docs]</a>
<span class="k">def</span> <span class="nf">play</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;XASound&quot;</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">play</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new_thread</span> <span class="o">=</span> <span class="kc">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;XASound&quot;</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Plays the sound from the beginning.</span>

<span class="sd"> Audio playback runs in a separate thread. For the sound the play properly, you must keep the main thread alive over the duration of the desired playback.</span>

<span class="sd"> :param new_thread: Whether to play the sound in a new thread, defaults to False</span>
<span class="sd"> :type new_thread: bool, optional</span>
<span class="sd"> :return: A reference to this sound object.</span>
<span class="sd"> :rtype: XASound</span>

Expand All @@ -9957,12 +9959,16 @@ <h1>Source code for PyXA.XABase</h1><div class="highlight"><pre>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__audio_engine</span><span class="o">.</span><span class="n">startAndReturnError_</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__player_node</span><span class="o">.</span><span class="n">play</span><span class="p">()</span>
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">__player_node</span><span class="o">.</span><span class="n">isPlaying</span><span class="p">():</span>
<span class="n">start_date</span> <span class="o">=</span> <span class="n">AppKit</span><span class="o">.</span><span class="n">NSDate</span><span class="o">.</span><span class="n">date</span><span class="p">()</span>
<span class="k">while</span> <span class="n">AppKit</span><span class="o">.</span><span class="n">NSDate</span><span class="o">.</span><span class="n">date</span><span class="p">()</span><span class="o">.</span><span class="n">timeIntervalSinceDate_</span><span class="p">(</span><span class="n">start_date</span><span class="p">)</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">duration</span><span class="p">:</span>
<span class="n">AppKit</span><span class="o">.</span><span class="n">NSRunLoop</span><span class="o">.</span><span class="n">currentRunLoop</span><span class="p">()</span><span class="o">.</span><span class="n">runUntilDate_</span><span class="p">(</span>
<span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">+</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>
<span class="p">)</span>

<span class="bp">self</span><span class="o">.</span><span class="n">_spawn_thread</span><span class="p">(</span><span class="n">play_sound</span><span class="p">,</span> <span class="p">[</span><span class="bp">self</span><span class="p">])</span>
<span class="k">if</span> <span class="n">new_thread</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_spawn_thread</span><span class="p">(</span><span class="n">play_sound</span><span class="p">,</span> <span class="p">[</span><span class="bp">self</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">play_sound</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span></div>


Expand Down
5 changes: 3 additions & 2 deletions docs/_modules/PyXA/apps/Bike.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyXA.apps.Bike &mdash; PyXA 0.2.2 documentation</title>
<title>PyXA.apps.Bike &mdash; PyXA 0.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=80d5e7a1" />
<link rel="stylesheet" type="text/css" href="../../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../../_static/graphviz.css?v=eafc0fe6" />
Expand All @@ -15,7 +15,7 @@

<script src="../../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../../_static/documentation_options.js?v=000c92bf"></script>
<script src="../../../_static/documentation_options.js?v=a47416a6"></script>
<script src="../../../_static/doctools.js?v=888ff710"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/js/theme.js"></script>
Expand Down Expand Up @@ -109,6 +109,7 @@ <h1>Source code for PyXA.apps.Bike</h1><div class="highlight"><pre>
<div class="viewcode-block" id="XABikeApplication.ObjectType">
<a class="viewcode-back" href="../../../reference/apps/bike.html#PyXA.apps.Bike.XABikeApplication.ObjectType">[docs]</a>
<span class="k">class</span> <span class="nc">ObjectType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Types of objects that can be created with :func:`XABikeApplication.make`.&quot;&quot;&quot;</span>
<span class="n">WINDOW</span> <span class="o">=</span> <span class="s2">&quot;window&quot;</span>
<span class="n">DOCUMENT</span> <span class="o">=</span> <span class="s2">&quot;document&quot;</span>
<span class="n">ROW</span> <span class="o">=</span> <span class="s2">&quot;row&quot;</span>
Expand Down
3 changes: 2 additions & 1 deletion docs/_modules/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
<div itemprop="articleBody">

<h1>All modules for which code is available</h1>
<ul><li><a href="PyXA/Additions/Learn.html">PyXA.Additions.Learn</a></li>
<ul><li><a href="PyXA/Additions/Devices.html">PyXA.Additions.Devices</a></li>
<li><a href="PyXA/Additions/Learn.html">PyXA.Additions.Learn</a></li>
<li><a href="PyXA/Additions/Speech.html">PyXA.Additions.Speech</a></li>
<li><a href="PyXA/Additions/UI.html">PyXA.Additions.UI</a></li>
<li><a href="PyXA/Additions/Utils.html">PyXA.Additions.Utils</a></li>
Expand Down
7 changes: 7 additions & 0 deletions docs/_sources/reference/additions/devices.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Devices Module
==============

.. automodule:: PyXA.Additions.Devices
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/_sources/reference/index.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ PyXA Additions
:maxdepth: 2

additions/learn
additions/devices
additions/speech
additions/ui
additions/utils
Expand Down
71 changes: 71 additions & 0 deletions docs/_sources/tutorial/devices.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
Device Interaction
==================

PyXA offers a _Devices_ addition that provides a simple interface to your device's hardware such as its camera, microphone, and screen.

Device Classes
--------------

The following device classes are available:

- :class:`~PyXA.Additions.Devices.XACamera` - for capturing photos and videos
- :class:`~PyXA.Additions.Devices.XAMicrophone` - for recording audio
- :class:`~PyXA.Additions.Devices.XAScreen` - for capturing screenshots and screen recordings

An overview of each class is provided below.

Camera
---------------------------

The :class:`~PyXA.Additions.Devices.XACamera` class provides two methods: one to capture photos (:func:`~PyXA.Additions.Devices.XACamera.capture`) and another to record videos (:func:`~PyXA.Additions.Devices.XACamera.record`). Both methods return an instance of the corresponding PyXA class—either :class:`~PyXA.XABase.XAImage` or :class:`~PyXA.XABase.XAVideo`—that can be used alongside other areas of PyXA.

For example, to capture a photo and save it to the desktop, you could use the following code:

.. code-block:: python
import PyXA
import os
cam = PyXA.XACamera()
img = cam.capture()
homedir = os.path.expanduser("~")
img.save(f"{homedir}/Desktop/test.png")
You can combine this with other PyXA features to create powerful automations. For example, you could use the :class:`~PyXA.Additions.Speech.XASpeech` class to speak any text detected in the image:

.. code-block:: python
import PyXA
cam = PyXA.XACamera()
img = cam.capture()
img_text = img.extract_text()
PyXA.XASpeech(img_text).speak()
Microphone
----------

The :class:`~PyXA.Additions.Devices.XAMicrophone` class provides a single method, :func:`~PyXA.Additions.Devices.XAMicrophone.record`, that records audio from the device's microphone and returns an instance of the :class:`~PyXA.XABase.XAAudio` class.

For example, to record 5 seconds of audio and play it back, you could use the following code:

.. code-block:: python
import PyXA
import os
mic = PyXA.XAMicrophone()
homedir = os.path.expanduser("~")
recording = mic.record(f"{homedir}/Downloads/test.wav", 5)
recording.play()
Screen
------

PyXA's :class:`~PyXA.Additions.Devices.XAScreen` class provides methods for capturing screenshots and screen recordings. Each method returns either a :class:`~PyXA.XABase.XAImage` or :class:`~PyXA.XABase.XAVideo` object. The available methods are:

- :func:`~PyXA.Additions.Devices.XAScreen.capture` - captures a screenshot
- :func:`~PyXA.Additions.Devices.XAScreen.capture_rect` - captures a screenshot of a specific area of the screen
- :func:`~PyXA.Additions.Devices.XAScreen.capture_window` - captures a screenshot of a specific window
- :func:`~PyXA.Additions.Devices.XAScreen.record` - records a screen recording
1 change: 1 addition & 0 deletions docs/_sources/tutorial/index.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Additional Tutorial Topics
ui_scripting
applescript
menubar
devices
extensions/web/rssfeed
appscript
tips_tricks
Expand Down
Loading

0 comments on commit 4dc6df9

Please sign in to comment.