diff --git a/scripts/piped-work/docimages_all.py b/scripts/piped-work/docimages_all.py
index c68340c2b..06489d7a5 100644
--- a/scripts/piped-work/docimages_all.py
+++ b/scripts/piped-work/docimages_all.py
@@ -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() )
diff --git a/scripts/piped-work/docimages_oddments.py b/scripts/piped-work/docimages_oddments.py
new file mode 100644
index 000000000..e17ffcfec
--- /dev/null
+++ b/scripts/piped-work/docimages_oddments.py
@@ -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()
+
diff --git a/scripts/piped-work/pipeclient.py b/scripts/piped-work/pipeclient.py
new file mode 100644
index 000000000..3cf939501
--- /dev/null
+++ b/scripts/piped-work/pipeclient.py
@@ -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:
+
+
+"""
+
+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()
diff --git a/src/commands/DragCommand.cpp b/src/commands/DragCommand.cpp
index 638655a7c..00eafcc32 100644
--- a/src/commands/DragCommand.cpp
+++ b/src/commands/DragCommand.cpp
@@ -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);
diff --git a/src/commands/ScreenshotCommand.cpp b/src/commands/ScreenshotCommand.cpp
index dcbd95263..a8ff7c6f1 100644
--- a/src/commands/ScreenshotCommand.cpp
+++ b/src/commands/ScreenshotCommand.cpp
@@ -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( win );
+ if( (!bHasBringToTop || mbBringToTop) && (!top_win || !top_win->IsActive()) ){
+ win->Raise();
+ Yield();
}
}
- Yield();
int screenW, screenH;
wxDisplaySize(&screenW, &screenH);
diff --git a/src/commands/ScreenshotCommand.h b/src/commands/ScreenshotCommand.h
index 53ad6f41f..c481a4c57 100644
--- a/src/commands/ScreenshotCommand.h
+++ b/src/commands/ScreenshotCommand.h
@@ -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;