http://www.stsci.edu/resources/software_hardware/pyraf/current/download
At current, this does not require anything different during installation. It can be activated at run-time with the setting of an environment variable and the use of a native (i.e not X11-linked) version of Python on your Mac (e.g. the default framework or MacPorts). That's right - no more need to build special X-linked versions of python/matplotlib/tcl/tk/libpng/etc.
To run this version of PyRAF on MacOS:
In addition, if you want to see smooth AGG graphics and scalable fonts, see the matplotlib question in the Graphics below.
Please let us know how it goes.
(1) Define PYTHONSTARTUP (if you haven't already):
setenv PYTHONSTARTUP "/home/rlw/python/pythonstartup.py"
(2) Add this special code in PYTHONSTARTUP:
# PyRAF initialization
import sys, os
executable = sys.argv[0]
while os.path.islink(executable):
executable = os.readlink(executable)
if os.path.split(executable)[1] == "pyraf":
# this code executes only if this is a pyraf session
from pyraf import iraf
startup = iraf.osfn("home$pyraflogin.py")
if os.path.exists(startup):
execfile(startup)
del startup
del executable # clean up namespace
What that does is execute a file in your IRAF home directory named
pyraflogin.py, if it exists. (This is similar to the approach the CL uses
for home$loginuser.cl.) That file can do whatever initialization you want.
It runs in the user's namespace, so it can define variables & functions, do
imports,
etc. It can also execute pyraf/iraf tasks. Here is a sample:
# import some Python modules
import re, urllib
# set an IRAF environment variable
iraf.set(mypython="home$python/")
# run an IRAF task
iraf.spy()
We will probably eventually migrate this capability into the PyRAF startup
file itself so it is easier to use.
saveToFile mystartup.save
or in Python syntax
iraf.saveToFile('mystartup.save')
Then next time you start up PyRAF, use
pyraf mystartup.save
to run PyRAF. Depending on how many packages and tasks you define during your
startup process,
this will start up about twice as fast as the standard login process.
The disadvantage of this approach is that it also restores parameters for any tasks and packages that run during the startup process to their values when you did the save. But that should not be a problem as long as you don't run any complex tasks at startup time. You should repeat this process (starting up PyRAF with no initial save file) when your login.cl or the IRAF system changes.
Unable to open CL script cache file /home/rlw/iraf/pyraf/pyraf.Database
Unable to open any CL script cache for writing
This is a sign that you are running a very old version of PyRAF and
should upgade to the current version. The old version had some
restrictions on running multiple PyRAF sessions simultaneously, but
the current version has no such restrictions.
File "/usr/ra/pyraf/irafimport.py", line 30, in _irafImport
return _originalImport(name, globals, locals, fromlist)
ImportError: ld.so.1: /usr/local/bin/python: fatal: togl.so: open
failed: No such file or directory
One can resolve this issue by adding the togl.so directory path to the
default search path for libraries. This can be done by adding the
following line to your .setenv file:
% setenv LD_LIBRARY_PATH /yourToglDirectoryPath:${LD_LIBRARY_PATH}
Togl should be a subdirectory of the directory location where the Tcl/Tk
libraries reside. The Togl subdirectory should contain togl.so.
setenv PYRAF_NO_DISPLAY 1
Of course, any tasks which attempt to display graphics will fail.
This is described in FAQ 1.2.
For example, if PYRAF_ARGS is set to '--ipython -n', then typing:
% pyrafwould be equivalent to typing:
% pyraf --ipython -nThis only applies to the PyRAF command line, as opposed to the Python API (e.g. 'from pyraf import iraf').
--> task mytask=home$mytask.cl
Inside a Python script, you must convert this to the equivalent
Python syntax:
from pyraf import iraf
iraf.task(mytask="home$mytask.cl")
For information on how to define a task that calls a Python
function instead of a CL script, see the next question.
Create two files, pytestmodule.py and pytest.par. pytest.par is a standard IRAF parameter file that contains the parameter definitions:
spar,s,a,"spar default",,,"string parameter"
j,i,h,5,,,"hidden integer parameter"
bpar,b,h,no,,,"hidden boolean parameter"
mode,s,h,"al"
The comma-separated fields in this file are the parameter name, type
(s=string, i=integer, etc.), mode (h=hidden), default value, minimum
value (or choice list), maximum value, and prompt string. See the IRAF
documentation and numerous examples of .par files in the IRAF
distribution for more information.
pytestmodule.py is a Python file that contains both the Python function definition and the IRAF task definition:
from pyraf import iraf
# define a function to be wrapped as an IrafTask object
def pytest(spar, j, bpar):
"""spar is a string, j is an int, bpar is a boolean; j, bpar are hidden"""
print "pytest: spar `%s' j `%s' bpar `%s'" % (spar,j,bpar)
k = j+5
if bpar:
print 'bpar true: k=j+5=',k
else:
print 'bpar false: k=j+5=',k
# create the IrafTask object wrapping the function pytest
# call it 'pytest', with parameters from 'pytest.par'
t = iraf.IrafTaskFactory(taskname='pytest', value='pytest.par',
function=pytest)
The parameters to the function need to be in the same order as in the parameter file. They do not necessarily need to have the same names as in the .par file (though they do in this example.) When the task is called, the task wrapper takes care of applying constraints (e.g. ensuring that parameters have the right types and that they meet the min/max/choice requirements.) It will also prompt for values for non-hidden parameters. Note that (unlike in IRAF CL tasks) the prompt always happens, even if the parameter is not used in the function, and it happens only once before the function is called.
The 'function' keyword parameter to IrafTaskFactory is what gets called when the task executes. There are no quotes around it (because it is not a string, it is the function itself.) If you want, you can specify a function using the full name, e.g. mod.func for a function 'func' defined in module 'mod'. The function can even be a method of some Python object, in which case it will have access to the instance attributes of that object.
The function is called with the actual values of the parameters (in the above example, a string, an int, and a boolean yes/no value.) If you change the values of the parameter variables inside the function, it does not change the corresponding IRAF parameter values. If you need to change the saved parameter values, you can do this inside the function:
spar = "new value for spar"
iraf.pytest.spar = spar
Once you have created the task object, it is automatically available
at the pyraf command line:
--> import pytestmodule
--> lpar pytest
spar = spar default string parameter
(j = 5) hidden integer parameter
(bpar = no) hidden boolean parameter
(mode = al)
--> epar pytest
--> pytest j=10
string parameter ('spar default'): abcde
pytest: spar `abcde' j `10' bpar `no'
bpar false: k=j+5= 15
The task will also show up in the current package listing you get when
you type '?'. Once the task is defined as shown above, you can invoke it
from CL scripts too by calling it just like any other task. As far as
PyRAF is concerned it is just another IRAF task. Naturally you
can't use it from the IRAF CL though.
Currently the only parameter types supported are those used by IRAF tasks. If you pass a Python variable not supported by IRAF (for example, a file handle or module) then the task wrapper attempts to convert it to the requested type. That can lead to some surprising behavior:
--> iraf.pytest(sys)
pytest: spar `<module 'sys' (built-in)>' j `5' bpar `no'
bpar false: k=j+5= 10
Note that the sys module's descriptive string is passed to pytest()
instead of the sys module itself.
set myenvvar=home$test/
The Python equivalent to this (which you can use inside Python scripts)
is:
from pyraf import iraf
iraf.set(myenvvar="home$test/")
First, here are some simple cases in the command-line syntax:
--> imhead dev$pix > imhead.stdout
--> imhead dev$pix >& imhead.stderr_and_stdout
--> imhead dev$pix >& imhead.stderr_only > imhead.stdout_only
--> head nlines=5 < .cshrc
The Python syntax equivalents to these are:
from pyraf import iraf
iraf.imhead("dev$pix", Stdout="imhead.stdout")
iraf.imhead("dev$pix", Stderr="imhead.stderr_and_stdout")
iraf.imhead("dev$pix", Stderr="imhead.stderr_only",
Stdout="imhead.stdout_only")
iraf.head(nlines=5, Stdin=".cshrc")
When a string is specified for one of these special keyword parameters, it is
assumed to be a file name. You can also pass a Python filehandle (e.g., as
is returned from the built-in open() function). In fact, any Python object with
the necessary read() or write() method can be used. This can be used to
implement the equivalent of pipes. In command-line syntax:
--> imhead dev$pix long+ | head nlines=5
A PyRAF equivalent to this is:
import StringIO
fh = StringIO.StringIO()
iraf.imhead("dev$pix", long=1, Stdout=fh)
fh.seek(0)
iraf.head(nlines=5, Stdin=fh)
fh.close()
Another special behavior of the Stdout keyword is that if it is set
to a non-zero integer value, the output of the task is returned
as a list of strings, with one string for each output line. For example,
--> s = iraf.imhead("dev$pix", Stdout=1)
--> print s
['dev$pix[512,512][short]: m51 B 600s']
If such a list of lines is passed as the Stdin parameter, the
input is read from it. So another equivalent to the piping example
given above is:
s = iraf.imhead("dev$pix", long=1, Stdout=1)
iraf.head(nlines=5, Stdin=s)
or even just:
iraf.head(nlines=5, Stdin=iraf.imhead("dev$pix",long=1,Stdout=1))
s1 = "test"
then you create a Python variable rather than using
the cl variable of the same name.
To avoid this, access the cl parameters using 'iraf.cl.s1' instead. This form can be used both in assigning and retrieving values:
iraf.cl.s1 = "test"
print iraf.cl.s1
Getting a graphics cursor position using '=gcur' is just a special case (since gcur is a CL parameter). Its PyRAF equivalent is:
print iraf.cl.gcur
or, at the interactive command line, simply type:
iraf.cl.gcur
Note, do NOT accidentally type:
iraf.cl.gcur()
as the use of the parenthesis is incorrect - 'gcur' is not a function call.
Note also that you can set parameters by changing the task's attributes as described in the PyRAF Tutorial.
You can also use the left and right arrow keys to scroll through the selection. Control-A jumps to the beginning of the entry, and Control-E jumps to the end of the entry.
--> from pyraf import iraf
--> plist = iraf.imhead.getParList()
--> for par in plist:
... print par
<IrafParS images s a 'dev$pix' None None "image names">
<IrafParS imlist s h '*.imh,*.fits,*.pl,*.qp,*.hhh' None None "default
image names">
<IrafParB longheader b h no None None "print header in multi-line
format">
<IrafParB userfields b h yes None None "print the user fields
(instrument parameters)">
<IrafParS mode s h 'al' None None "">
<IrafParI $nargs i h 0 None None "">
The list returned by getParList() consists of parameter objects that
contain the complete descriptions of each parameter. If you print
them (as in the example), the string gives the parameter class, name,
type, mode, value, minimum, maximum, and prompt string. You can
retrieve or change the parameter values using the get() and set() methods:
--> par = plist[0]
--> print par
<IrafParS images s a 'dev$pix' None None "image names">
--> print par.get()
dev$pix
--> par.set('newvalue')
--> print par.get()
newvalue
Note that these parameters are actually shared with the original task
(they are not copies), so modifying them also changes the task parameters:
--> lpar imhead
images = newvalue image names
(imlist = *.imh,*.fits,*.pl,*.qp,*.hhh) default image names
(longheader = no) print header in multi-line format
(userfields = yes) print the user fields (instrument parameters)
(mode = al)
If you want a completely independent set of parameters, use the Python copy
module or the optional docopy parameter to getParList:
--> plist1 = iraf.imhead.getParList(docopy=1) # this is a copy
--> import copy
--> plist2 = copy.deepcopy(iraf.imhead.getParList()) # so is this
Here are a few other task methods that may be handy for manipulating
parameters:
getDefaultParList() Returns list of all parameter objects with default values.
getParObject(name) Returns the IrafPar object for the given parameter.
getParDict() Returns dictionary of all parameter objects. Example:
--> d = iraf.imhead.getParDict()
--> print d['mode']
<IrafParS mode s h 'al' None None "">
--> from pyraf import iraf
--> from pyraf.irafpar import IrafParList
--> plist = IrafParList('imhead', parlist=iraf.imhead.getParList(docopy=1))
IrafParList objects have methods to do various useful things:
lParam() Lists parameters in lpar format
eParam() Run parameter editor
dParam() Print parameters in dpar (assignment) format
saveParList(filename) Save parameters to file in .par format
Parameters that have been saved to a file can be used to create an
IrafParList as well:
--> plist.saveParList('mylist.par')
'5 parameters written to mylist.par'
--> newplist = IrafParList('imhead', filename='mylist.par')
--> newplist.eParam()
You can get a complete list of the available IrafParList methods using help(plist).
--> iraf.imhead.saveParList('myfile.par')
'5 parameters written to myfile.par'
If the filename is omitted, the parameters are saved in the uparm
directory in a file with the usual name created from a combination of
the task and package. You can print task.scrunchName() to see the
filename.
If you just want to insure that the parameters used in the call to an IRAF task get saved in your uparm directory, include the special keyword parameter _save as a task parameter:
--> iraf.imhead('dev$pix', _save=1)
In the absence of the _save parameter, the parameters used for task
execution do not get saved. Note that this is also the default when
executing IRAF tasks from CL scripts, but in Python scripts you do have
the option to decide that parameters should be saved.
See the answer to the previous question (on making copies of parameter lists and editing them with epar) for more information on using these .par files.
from pyraf import iraf
from pyraf.irafpar import IrafParList
task = iraf.imstat
plist = IrafParList(task.getName(), filename='mypars.par',
parlist=task.getParList(docopy=1))
iraf.epar(plist)
Now we have created the file 'mypars.par' with the desired
parameters. To run the task using those parameters, set the
special ParList task keyword parameter to the .par filename:
iraf.imstat(ParList='mypars.par')
or alternatively ParList can accept an IrafParList instead of
a filename:
plist = IrafParList('', filename='mypars.par')
iraf.imstat(ParList=plist)
You might run into a problem calling IRAF tasks that have parameters which conflict with Python reserved words. Here is an example (from ticket #57):
--> noao
--> onedspec
--> iraf.dispcor(input=specname, output=specname, global='NO')
File "", line 1
iraf.dispcor(input=specname, output=specname, global='NO')
^
SyntaxError: invalid syntax
In this case, the parameter named 'global' matches the Python reserved
word with the same name.
However, the parameter's value can be manipulated this way:
iraf.dispcor.setParam('global','yes')
to set it, and then:
iraf.dispcor.getParam('global')
to find out the value of this parameter.
You can then call dispcor without specifying the value of this parameter.
For most purposes the PyRAF graphics window is preferred. Note that you can run PyRAF in an xgterm terminal window, but the graphics will appear in the standard PyRAF plot window. If you need to use other IRAF graphics devices, do this:
set stdgraph=stgkern
iraf.stdgraph.device="xterm" # or whatever device
This forces the use of the standard IRAF graphics kernel. You cannot
run interactive graphics tasks (that read the graphics cursor position,
e.g. splot) using this approach, but non-interactive graphics tasks
should work. You will generally need to do a gflush to get the graphics
to appear.
Setting stdgraph to devices that are not built-in to IRAF (e.g., "stdplot" or "imdr") also works for non-interactive graphics. In that case you do not have to use the special "stgkern" value for stdgraph.
We do plan to add the capability of doing interactive plot overlays on the image display, and we will probably support interactive graphics on some other devices in the future.
from pyraf import gwm
gwm.window('New Window Name')
If the new window name is omitted, an unused default name of the
form 'graphics<n>' is selected. If a window with the specified
name already exists, the graphics focus is switched to that window
(so that the next plot will appear there), but the existing
window is not modified.
from pyraf import gwm
gwm.window('graphics1')
The 'graphics1' window is not modified, but the next plot will appear
in that window. If a graphics window with the specified name does not
exist, a new window is created.
gwm.window([windowName]) Create new window or switch focus to
existing window
gwm.delete([windowName]) Delete specified window (active window if
name is omitted)
gwm.raiseActiveWindow() Raise the active window to the front on
the screen
gwm.getActiveWindowName() Returns name of the active window
gwm.getGraphicsWindowManager() Returns the graphics window manager object
The latter function can be used to get a list of all the graphics windows:
--> wm = pyraf.gwm.getGraphicsWindowManager()
--> print wm.windows.keys()
['graphics2', 'graphics1']
The windows attribute is a dictionary with entries for all existing graphics
windows.
The File->Print graphics menu item can be used to print the current plot on your default IRAF printer, as specified by the stdplot variable.
pyraf.gki.printPlot() can be called from a Python script (or typed at the PyRAF command line) to do the same thing.
In gcur mode -- while running an interactive IRAF graphics task so the crosshair cursor is visible in the graphics window -- typing an equal sign = or the colon command :.snap prints the plot.
The File->Save graphics menu item can also be useful for printing. It allows you to save the graphics metacode for the current plot to a file. That metacode can be later loaded back into the graphics window (using File->Load) and can also be printed using special purpose IRAF tasks. For example, the plot.stdplot task takes a metacode file as input and prints the result to the stdplot output device.
from pyraf import iraf
from iraf import stsdas, graphics, stplot, igi, gflush
s = ['zsection "myimage[100:300,500:600]"']
s = s + ['location 0.1 0.9 0.1 0.9']
and so on, and then
s = s + ['end'] # don't forget to terminate the igi script
igi(Stdin = s) # execute the commands
gflush # flush the graphics buffer.
If you've set up igi for postscript output ('psi-port' or 'psi-land'),
this should produce an eps file.
--> epar display
X Error of failed request: BadValue (integer parameter out of range for
operation)
Major opcode of failed request: 91 (X_QueryColors)
Value in failed request: 0xff141312
Serial number of failed request: 2524
Current serial number in output stream: 2524
This seems to be due to a change in the default behavior of the Xorg X server
software (v7.2 or v7.3).
By default, the "Composite" extension is now enabled, but this causes color
depth conflicts with PyRAF graphics. Composites are
used for some 3-D rendering effects (e.g in Mandriva), but if you do not
directly rely on this extension, the
problem can be easily resolved by disabling the extension. To do so, place
these lines
in your system's "xorg.conf" file:
Section "Extensions"
Option "Composite" "Disable"
EndSection
and then restart your X server (a reboot may be necessary).
As an alternative, "Nedit" users found a work-around for the same issue by setting the XLIB_SKIP_ARGB_VISUALS environment variable. In (t)csh:
> setenv XLIB_SKIP_ARGB_VISUALS 1
> pyraf
The topic is discussed
in this Nedit thread.
Also, if you do not yet have an Xorg configuration file (e.g. some Fedora Core 10 installs do not have one in /etc/X11/xorg), you may generate one via:
Note that color depth issues in general may be debugged by running "xwininfo" and clicking on any GUI components in question.
PyRAF does not make matplotlib an installation requirement, so as to leave installation as simple as possible. However, if the user has matplotlib installed on their machine (with the TkAgg back-end), they may enable the matplotlib graphics kernel by simply setting an environment variable:
setenv PYRAFGRAPHICS matplotlib
Note that PyRAF does receive plotting instructions at a very low level (GKI), so there are limits to how much matplotlib widgetry can be brought to bear inside the PyRAF graphics windows.
AttributeError: 'NoneType' object has no attribute 'get_xbound'
This feature is has been fixed in the next version of matplotlib (0.99.1). PyRAF users who want to continue using the matplotlib graphics kernel can work around this by either:
The fix is to turn off the cmdtool scrollbar by right-clicking in the window and selecting 'Disable Scrolling' from the 'Scrolling' menu. Or you can use another type of terminal window (e.g., xterm) instead of cmdtool.
--> print ("382 4783") | scan(i,j)
Traceback (innermost last):
File "<console>", line 1, in ?
NameError: scan
This problem is caused by mixing Python syntax with CL syntax.
The print statement is interpreted in Python mode (because "print"
is a Python keyword and also because it has an open parenthesis).
That puts the whole line into Python mode, so scan is not found.
The command will work fine inside a CL script (since it is legal
CL even if it is not legal Python.) In PyRAF you can do this:
--> clPrint "382 4783" | scan(i,j)
--> print iraf.cl.i, iraf.cl.j
382 4783
Note that you have to use clPrint instead of just print to avoid
conflicts with the Python print command, and you have to leave
the parentheses off the clPrint to get into CL emulation mode so the
pipe is properly translated. Also note the somewhat awkward syntax
for accessing the CL parameters i, j (which are not the same as
Python variables i, j).
In Python code (including at the PyRAF command line) there are better alternatives to scan. Here's one Python approach:
--> f = string.split("382 4783")
--> print f
['382', '4783']
--> i = int(f[0])
--> j = int(f[1])
--> print i,j
382 4783
Or if you want a 1-line alternative:
--> i, j = map(int, string.split("382 4783"))
The combination of the Python string and re modules for string
processing is generally more powerful than scan/scanf, and the results
go into simple Python variables instead of CL task parameters.