mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-01 08:29:27 +02:00
Add docimages_oddments.py for the weirder cases.
This also adds an option NOT to bring Audacity to the top when screenshotting. That's because QuickPlay will disappear if you do -because you lose mouse capture. Also allow negative window IDs in the drag command.
This commit is contained in:
parent
4412f325dd
commit
2088e44f03
@ -13,3 +13,4 @@ exec( open("docimages_after.py" ).read() )
|
||||
exec( open("docimages_envelopes.py" ).read() )
|
||||
exec( open("docimages_cut_n_paste.py" ).read() )
|
||||
exec( open("docimages_clip_boundaries.py" ).read() )
|
||||
exec( open("docimages_oddments.py" ).read() )
|
||||
|
82
scripts/piped-work/docimages_oddments.py
Normal file
82
scripts/piped-work/docimages_oddments.py
Normal file
@ -0,0 +1,82 @@
|
||||
# docimages_oddments.py
|
||||
# Sends commands to get images for the manual.
|
||||
# Image oddments that don't fit the other categories.
|
||||
|
||||
# Make sure Audacity is running first and that mod-script-pipe is enabled
|
||||
# before running this script.
|
||||
|
||||
#load and run the common core.
|
||||
exec( open("docimages_core.py" ).read() )
|
||||
|
||||
import time
|
||||
|
||||
|
||||
# 11 2KHz tones, of decreasing amplitude.
|
||||
# With label track to annotate it.
|
||||
def makeStepper():
|
||||
makeWayForTracks()
|
||||
do( 'NewMonoTrack' )
|
||||
do( 'Select: Start=0 End=22')
|
||||
do( 'Silence' ) #Just so we can watch.
|
||||
do( 'FitInWindow')
|
||||
for i in range( 0, 11 ):
|
||||
do( 'Select: Start='+str(i*2)+' End='+str(i*2+2) )
|
||||
do( 'Chirp: StartFreq=2000 EndFreq=2000 StartAmp=' + str( (400**(0.1 * (10-i)))/400 )+' EndAmp=' + str( (400**(0.1 * (10-i) ))/400 ))
|
||||
do( 'Select: Start=0 End=22')
|
||||
do( 'Join' )
|
||||
do( 'FitInWindow')
|
||||
do( 'AddLabelTrack' )
|
||||
for i in range( 0, 11 ):
|
||||
do( 'Select: Start='+str(i*2)+' End='+str(i*2+2) )
|
||||
do( 'AddLabel' )
|
||||
do( 'SetLabel: Label=' + str(i)+' Selected=0 Text='+str( -(i*10) ))
|
||||
do( 'Select: Start=0 End=0')
|
||||
|
||||
|
||||
def spectro_image1and2() :
|
||||
loadStereoTracks(1)
|
||||
# A stereo track
|
||||
capture( 'Spectral001.png', 'First_Track' )
|
||||
# As spectrogram.
|
||||
do( 'SetTrack: Track=0 Display=Spectrogram')
|
||||
do( 'Select: Start=55 End=70 First=0 Last=1')
|
||||
capture( 'Spectral002.png', 'First_Track' )
|
||||
# Half spectrogram, half wave.
|
||||
do( 'SetTrack: Channel=1 Display=Waveform')
|
||||
capture( 'MixedMode.png', 'First_Track' )
|
||||
|
||||
def spectro_image3and4():
|
||||
makeStepper();
|
||||
# Stepper tone, viewed in dB.
|
||||
do( 'SetTrack: Scale=dB')
|
||||
capture( 'Spectral003.png', 'First_Two_Tracks' )
|
||||
# As spectrogram.
|
||||
do( 'SetTrack: Display=Spectrogram')
|
||||
capture( 'Spectral004.png', 'First_Two_Tracks' )
|
||||
|
||||
def oddments_imagesA():
|
||||
for name in ["Select","Envelope","Draw","Zoom","TimeShift","Multi"] :
|
||||
do( name + "Tool" )
|
||||
capture( name + "Tool.png" , 'Tools' );
|
||||
#A track is needed for the buttons to be active.
|
||||
loadMonoTracks(1)
|
||||
for id in range( 11000, 11006 ):
|
||||
do( "Drag: Id="+str( id) + " FromX=10 FromY=10" )
|
||||
capture( "Button" + str(id) +"Hover.png", "Transport" )
|
||||
do( "Drag: Id="+str( id) + " FromX=1000 FromY=10" )
|
||||
|
||||
|
||||
def oddments_imagesB():
|
||||
loadMonoTracks(1)
|
||||
#Bring window to top now
|
||||
capture( "Dummy.png", "Ruler" )
|
||||
#We hope nothing else gets focus before the next capture, so
|
||||
#that we actually get to see something!
|
||||
do( "Drag: Window=Timeline FromX=200 FromY=10 ToX=600 ToY=10" )
|
||||
time.sleep(3.0)
|
||||
#Disable bringing to top, so as not to destroy quick play.
|
||||
capture( "QuikPlay001.png", "First_Track_Plus ToTop=0" )
|
||||
|
||||
#oddments_imagesA()
|
||||
oddments_imagesB()
|
||||
|
296
scripts/piped-work/pipeclient.py
Normal file
296
scripts/piped-work/pipeclient.py
Normal file
@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Automate Audacity via mod-script-pipe.
|
||||
|
||||
Pipe Client may be used as a command-line script to send commands to
|
||||
Audacity via the mod-script-pipe interface, or loaded as a module.
|
||||
Requires Python 2.7 or later. Python 3 recommended.
|
||||
|
||||
======================
|
||||
Command Line Interface
|
||||
======================
|
||||
|
||||
usage: pipeclient.py [-h] [-t] [-s ] [-d]
|
||||
|
||||
Arguments
|
||||
---------
|
||||
-h,--help: optional
|
||||
show short help and exit
|
||||
-t, --timeout: float, optional
|
||||
timeout for reply in seconds (default: 10)
|
||||
-s, --show-time: bool, optional
|
||||
show command execution time (default: True)
|
||||
-d, --docs: optional
|
||||
show this documentation and exit
|
||||
|
||||
Example
|
||||
-------
|
||||
$ python3 pipeclient.py -t 20 -s False
|
||||
|
||||
Launches command line interface with 20 second time-out for
|
||||
returned message, and don't show the execution time.
|
||||
|
||||
When prompted, enter the command to send (not quoted), or 'Q' to quit.
|
||||
|
||||
$ Enter command or 'Q' to quit: GetInfo: Type=Tracks Format=LISP
|
||||
|
||||
============
|
||||
Module Usage
|
||||
============
|
||||
|
||||
Note that on a critical error (such as broken pipe), the module just exits.
|
||||
If a more graceful shutdown is required, replace the sys.exit()'s with
|
||||
exceptions.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
# Import the module:
|
||||
>>> import pipeclient
|
||||
|
||||
# Create a client instance:
|
||||
>>> client = pipeclient.PipeClient()
|
||||
|
||||
# Send a command:
|
||||
>>> client.write("Command", timer=True)
|
||||
|
||||
# Read the last reply:
|
||||
>>> print(client.read())
|
||||
|
||||
See Also
|
||||
--------
|
||||
PipeClient.write : Write a command to _write_pipe.
|
||||
PipeClient.read : Read Audacity's reply from pipe.
|
||||
|
||||
Copyright Steve Daulton 2018
|
||||
Released under terms of the GNU General Public License version 2:
|
||||
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html />
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import errno
|
||||
import argparse
|
||||
|
||||
|
||||
if sys.version_info[0] < 3 and sys.version_info[1] < 7:
|
||||
sys.exit('PipeClient Error: Python 2.7 or later required')
|
||||
|
||||
# Platform specific constants
|
||||
if sys.platform == 'win32':
|
||||
WRITE_NAME = '\\\\.\\pipe\\ToSrvPipe'
|
||||
READ_NAME = '\\\\.\\pipe\\FromSrvPipe'
|
||||
EOL = '\r\n\0'
|
||||
else:
|
||||
# Linux or Mac
|
||||
PIPE_BASE = '/tmp/audacity_script_pipe.'
|
||||
WRITE_NAME = PIPE_BASE + 'to.' + str(os.getuid())
|
||||
READ_NAME = PIPE_BASE + 'from.' + str(os.getuid())
|
||||
EOL = '\n'
|
||||
|
||||
|
||||
class PipeClient(object):
|
||||
"""Write / read client access to Audacity via named pipes.
|
||||
|
||||
Normally there should be just one instance of this class. If
|
||||
more instances are created, they all share the same state.
|
||||
|
||||
__init__ calls _write_thread_start() and _read_thread_start() on
|
||||
first instantiation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Attributes
|
||||
----------
|
||||
reader_pipe_broken : event object
|
||||
Set if pipe reader fails. Audacity may have crashed
|
||||
reply_ready : event object
|
||||
flag cleared when command sent and set when response received
|
||||
timer : bool
|
||||
When true, time the command execution (default False)
|
||||
reply : string
|
||||
message received when Audacity completes the command
|
||||
|
||||
See Also
|
||||
--------
|
||||
write : Write a command to _write_pipe.
|
||||
read : Read Audacity's reply from pipe.
|
||||
|
||||
"""
|
||||
|
||||
reader_pipe_broken = threading.Event()
|
||||
reply_ready = threading.Event()
|
||||
|
||||
_shared_state = {}
|
||||
|
||||
def __new__(cls, *p, **k):
|
||||
self = object.__new__(cls, *p, **k)
|
||||
self.__dict__ = cls._shared_state
|
||||
return self
|
||||
|
||||
def __init__(self):
|
||||
self.timer = False
|
||||
self._start_time = 0
|
||||
self._write_pipe = None
|
||||
self.reply = ''
|
||||
if not self._write_pipe:
|
||||
self._write_thread_start()
|
||||
self._read_thread_start()
|
||||
|
||||
def _write_thread_start(self):
|
||||
"""Start _write_pipe thread"""
|
||||
# Pipe is opened in a new thread so that we don't
|
||||
# freeze if Audacity is not running.
|
||||
write_thread = threading.Thread(target=self._write_pipe_open)
|
||||
write_thread.daemon = True
|
||||
write_thread.start()
|
||||
# Allow a little time for connection to be made.
|
||||
time.sleep(0.1)
|
||||
if not self._write_pipe:
|
||||
sys.exit('PipeClientError: Write pipe cannot be opened.')
|
||||
|
||||
def _write_pipe_open(self):
|
||||
"""Open _write_pipe."""
|
||||
self._write_pipe = open(WRITE_NAME, 'w')
|
||||
|
||||
def _read_thread_start(self):
|
||||
"""Start read_pipe thread."""
|
||||
read_thread = threading.Thread(target=self._reader)
|
||||
read_thread.daemon = True
|
||||
read_thread.start()
|
||||
|
||||
def write(self, command, timer=False):
|
||||
"""Write a command to _write_pipe.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
command : string
|
||||
The command to send to Audacity
|
||||
timer : bool, optional
|
||||
If true, time the execution of the command
|
||||
|
||||
Example
|
||||
-------
|
||||
write("GetInfo: Type=Labels", timer=True):
|
||||
|
||||
"""
|
||||
self.timer = timer
|
||||
print('Sending command:', command)
|
||||
self._write_pipe.write(command + EOL)
|
||||
# Check that read pipe is alive
|
||||
if PipeClient.reader_pipe_broken.isSet():
|
||||
sys.exit('PipeClient: Read-pipe error.')
|
||||
try:
|
||||
self._write_pipe.flush()
|
||||
if self.timer:
|
||||
self._start_time = time.time()
|
||||
self.reply = ''
|
||||
PipeClient.reply_ready.clear()
|
||||
except IOError as err:
|
||||
if err.errno == errno.EPIPE:
|
||||
sys.exit('PipeClient: Write-pipe error.')
|
||||
else:
|
||||
raise
|
||||
|
||||
def _reader(self):
|
||||
"""Read FIFO in worker thread."""
|
||||
# Thread will wait at this read until it connects.
|
||||
# Connection should occur as soon as _write_pipe has connected.
|
||||
read_pipe = open(READ_NAME, 'r')
|
||||
message = ''
|
||||
while True:
|
||||
line = read_pipe.readline()
|
||||
# Stop timer as soon as we get first line of response.
|
||||
stop_time = time.time()
|
||||
while line != EOL:
|
||||
message += line
|
||||
line = read_pipe.readline()
|
||||
if line == '':
|
||||
# No data in read_pipe indicates that the pipe is broken
|
||||
# (Audacity may have crashed).
|
||||
PipeClient.reader_pipe_broken.set()
|
||||
if self.timer:
|
||||
xtime = (stop_time - self._start_time) * 1000
|
||||
message += 'Execution time: {0:.2f}ms'.format(xtime)
|
||||
self.reply = message
|
||||
PipeClient.reply_ready.set()
|
||||
message = ''
|
||||
read_pipe.close()
|
||||
|
||||
def read(self):
|
||||
"""Read Audacity's reply from pipe.
|
||||
|
||||
Returns
|
||||
-------
|
||||
string
|
||||
The reply from the last command sent to Audacity, or null string
|
||||
if reply not received. Null string usually indicates that Audacity
|
||||
is still processing the last command.
|
||||
|
||||
"""
|
||||
if not PipeClient.reply_ready.isSet():
|
||||
return ''
|
||||
else:
|
||||
return self.reply
|
||||
|
||||
|
||||
def bool_from_string(strval):
|
||||
"""Return boolean value from string"""
|
||||
if strval.lower() in ('true', 't', '1', 'yes', 'y'):
|
||||
return True
|
||||
elif strval.lower() in ('false', 'f', '0', 'no', 'n'):
|
||||
return False
|
||||
else:
|
||||
raise argparse.ArgumentTypeError('Boolean value expected.')
|
||||
|
||||
def main():
|
||||
"""Interactive command-line for PipeClient"""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-t', '--timeout', type=float, metavar='', default=10,
|
||||
help="timeout for reply in seconds (default: 10")
|
||||
parser.add_argument('-s', '--show-time', metavar='True/False',
|
||||
nargs='?', type=bool_from_string,
|
||||
const='t', default='t', dest='show',
|
||||
help='show command execution time (default: True)')
|
||||
parser.add_argument('-d', '--docs', action='store_true',
|
||||
help='show documentation and exit')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.docs:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
|
||||
client = PipeClient()
|
||||
while True:
|
||||
reply = ''
|
||||
if sys.version_info[0] < 3:
|
||||
#pylint: disable=undefined-variable
|
||||
message = raw_input("\nEnter command or 'Q' to quit: ")
|
||||
else:
|
||||
message = input( #pylint: disable=bad-builtin
|
||||
"\nEnter command or 'Q' to quit: ")
|
||||
start = time.time()
|
||||
if message.upper() == 'Q':
|
||||
sys.exit(0)
|
||||
elif message == '':
|
||||
pass
|
||||
else:
|
||||
client.write(message, timer=args.show)
|
||||
while reply == '':
|
||||
time.sleep(0.1) # allow time for reply
|
||||
if time.time() - start > args.timeout:
|
||||
reply = 'PipeClient: Reply timed-out.'
|
||||
else:
|
||||
reply = client.read()
|
||||
print(reply)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -48,7 +48,7 @@ static const wxString kCoordTypeStrings[nCoordTypes] =
|
||||
|
||||
bool DragCommand::DefineParams( ShuttleParams & S ){
|
||||
wxArrayString coords( nCoordTypes, kCoordTypeStrings );
|
||||
S.OptionalN( bHasId ).Define( mId, wxT("Id"), 0.0, 11000.0, 1000000.0);
|
||||
S.OptionalN( bHasId ).Define( mId, wxT("Id"), 11000.0, -100000.0, 1000000.0);
|
||||
S.OptionalY( bHasWinName ).Define( mWinName, wxT("Window"), "Timeline");
|
||||
S.OptionalY( bHasFromX ).Define( mFromX, wxT("FromX"), 200.0, 0.0, 1000000.0);
|
||||
S.OptionalY( bHasFromY ).Define( mFromY, wxT("FromY"), 10.0, 0.0, 1000000.0);
|
||||
|
@ -139,9 +139,10 @@ static const wxString kBackgroundStrings[nBackgrounds] =
|
||||
bool ScreenshotCommand::DefineParams( ShuttleParams & S ){
|
||||
wxArrayString whats(nCaptureWhats, kCaptureWhatStrings);
|
||||
wxArrayString backs(nBackgrounds, kBackgroundStrings);
|
||||
S.Define( mPath, wxT("Path"), wxT(""));
|
||||
S.DefineEnum( mWhat, wxT("CaptureWhat"), wxT("Window"), whats );
|
||||
S.OptionalN(bHasBackground).DefineEnum( mBack, wxT("Background"), wxT("None"), backs );
|
||||
S.Define( mPath, wxT("Path"), wxT(""));
|
||||
S.DefineEnum( mWhat, wxT("CaptureWhat"), wxT("Window"), whats );
|
||||
S.OptionalN(bHasBackground).DefineEnum( mBack, wxT("Background"), wxT("None"), backs );
|
||||
S.OptionalN(bHasBringToTop).Define( mbBringToTop, wxT("ToTop"), true );
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -153,9 +154,10 @@ void ScreenshotCommand::PopulateOrExchange(ShuttleGui & S)
|
||||
|
||||
S.StartMultiColumn(2, wxALIGN_CENTER);
|
||||
{
|
||||
S.TieTextBox( _("Path:"), mPath);
|
||||
S.TieChoice( _("Capture What:"), mWhat, &whats);
|
||||
S.TieChoice( _("Background:"), mBack, &backs);
|
||||
S.TieTextBox( _("Path:"), mPath);
|
||||
S.TieChoice( _("Capture What:"), mWhat, &whats);
|
||||
S.TieChoice( _("Background:"), mBack, &backs);
|
||||
S.TieCheckBox( _("Bring To Top:"), mbBringToTop);
|
||||
}
|
||||
S.EndMultiColumn();
|
||||
}
|
||||
@ -247,16 +249,18 @@ bool ScreenshotCommand::Capture(
|
||||
int height = r.height;
|
||||
if( r.width == 0 )
|
||||
return false;
|
||||
if (window) {
|
||||
if (window->IsTopLevel()) {
|
||||
window->Raise();
|
||||
}
|
||||
else {
|
||||
wxGetTopLevelParent(window)->Raise();
|
||||
if (window ) {
|
||||
wxWindow * win = window;
|
||||
wxTopLevelWindow * top_win= nullptr;
|
||||
if( !window->IsTopLevel())
|
||||
win = wxGetTopLevelParent(window);
|
||||
top_win = dynamic_cast<wxTopLevelWindow*>( win );
|
||||
if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){
|
||||
win->Raise();
|
||||
Yield();
|
||||
}
|
||||
}
|
||||
|
||||
Yield();
|
||||
|
||||
int screenW, screenH;
|
||||
wxDisplaySize(&screenW, &screenH);
|
||||
|
@ -33,6 +33,7 @@ class CommandContext;
|
||||
class ScreenshotCommand : public AudacityCommand
|
||||
{
|
||||
public:
|
||||
ScreenshotCommand(){ mbBringToTop=true;};
|
||||
// CommandDefinitionInterface overrides
|
||||
wxString GetSymbol() override {return SCREENSHOT_PLUGIN_SYMBOL;};
|
||||
wxString GetDescription() override {return _("Takes screenshots.");};
|
||||
@ -46,7 +47,9 @@ private:
|
||||
wxString mWhat;
|
||||
wxString mBack;
|
||||
wxString mPath;
|
||||
bool mbBringToTop;
|
||||
bool bHasBackground;
|
||||
bool bHasBringToTop;
|
||||
friend class ScreenshotCommand;
|
||||
friend class ScreenFrame;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user