Skip to content

Commit

Permalink
Merge pull request #63 from sosey/notebook
Browse files Browse the repository at this point in the history
funcational updates for examine class
  • Loading branch information
sosey committed Jul 17, 2016
2 parents d85e6bd + 7579849 commit a3fc29e
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 100 deletions.
2 changes: 1 addition & 1 deletion docs/rtd-pip-requirements
Expand Up @@ -4,4 +4,4 @@ numpy
matplotlib
Cython
ginga
jupyter
ipython
8 changes: 5 additions & 3 deletions imexam/connect.py
Expand Up @@ -107,6 +107,8 @@ def setlog(self, filename=None, on=True, level=logging.DEBUG):

def close(self):
"""Close the window and end connection."""
# make sure all the plots are closed
self.exam._close_plots()
self.window.close()

def imexam(self):
Expand Down Expand Up @@ -137,7 +139,7 @@ def reopen(self):

def grab(self):
"""Display a snapshop of the current image in the browser window."""
self.window.grab()
return self.window.grab()

def get_data_filename(self):
"""Return the filename for the data in the current window."""
Expand Down Expand Up @@ -316,12 +318,12 @@ def get_image(self):
"""Return the full image object, not just the numpy array."""
return self.window.get_image()

def get_header(self):
def get_header(self, **kwargs):
"""Return the current fits header as a string.
None is returned if there's a problem
"""
return self.window.get_header()
return self.window.get_header(**kwargs)

def grid(self, *args, **kwargs):
"""Convenience method to turn the grid on and off.
Expand Down
40 changes: 27 additions & 13 deletions imexam/ds9_viewer.py
Expand Up @@ -1048,10 +1048,11 @@ def get_image(self):
for the full object")
self.get_data()

def get_header(self):
def get_header(self, fitsobj=False):
"""Return the current fits header.
The return value is the string or None if there's a problem
If fits is True then a fits header object is returned instead
"""
# TODO return the simple header for arrays which are loaded

Expand All @@ -1060,11 +1061,14 @@ def get_header(self):
self._set_frameinfo()

if self._viewer[frame]['filename']:
try:
header = self.get("fits header")
except XpaException as e:
print("XPA Exception getting header: {0}".format(e))
return None
if fitsobj:
header = fits.getheader(self._viewer[frame]['filename'])
else:
try:
header = self.get("fits header")
except XpaException as e:
print("XPA Exception getting header: {0}".format(e))
return None

return header
else:
Expand Down Expand Up @@ -1098,7 +1102,7 @@ def embed(self):
"""Embed the viewer in a notebook."""
print("Not Implemented for DS9")

def load_fits(self, fname="", extver=1, extname=None):
def load_fits(self, fname="", extver=None, extname=None, mecube=False):
"""convenience function to load fits image to current frame.
Parameters
Expand All @@ -1113,6 +1117,9 @@ def load_fits(self, fname="", extver=1, extname=None):
extname: string, optional
The name (EXTNAME in the header) of the image to load
mecube: bool, optional
If mecube is True, load the fits file as a cube into ds9
Notes
-----
Expand All @@ -1132,14 +1139,20 @@ def load_fits(self, fname="", extver=1, extname=None):
frame = self.frame()
if not frame:
frame = 1 # load into first frame
if not extver:
extver = 1

if fname:
# see if the image is MEF or Simple
fname = os.path.abspath(fname)

# strip the extensions for now
shortname = fname.split("[")[0]
cstring = util.verify_filename(shortname, extver, extname)
if mecube:
cstring = "mecube {0:s}".format(shortname)
else:
cstring = util.verify_filename(shortname, extver, extname)
print(cstring)
self.set(cstring)
self._set_frameinfo()
# make sure any previous reference is reset
Expand Down Expand Up @@ -1655,13 +1668,13 @@ def grab(self):
"""Make a copy of the image view."""
backend = get_backend().lower()
supported = ["nbagg", "tkagg", "qt4agg"]
if backend in supported or "pylab" in backend:
fname = self.snapsave(format="png")
fname = self.snapsave(format="png")
if "nbagg" in backend: # save inside the notebook
data = mpimage.imread(fname)
plt.clf()
plt.imshow(data, origin="upper")
return plt.imshow(data, origin="upper")
else:
print("Not supported for {0:s}".format(backend))
print("Notebook embedding not supported for {0:s}".format(backend))

def view(self, img):
"""Display numpy image array to current frame.
Expand Down Expand Up @@ -1703,7 +1716,7 @@ def view(self, img):
raise UnsupportedDatatypeException(e)

option = "[xdim={0:d},ydim={1:d},bitpix={2:d}{3:s}]".format(xdim,
ydim,bitpix, endianness)
ydim, bitpix, endianness)
try:
self.set("array " + option, arr_str)
self._set_frameinfo()
Expand Down Expand Up @@ -1753,5 +1766,6 @@ def reopen(self):
print("Not available for DS9, start a new object instead")
raise NotImplementedError


atexit.register(ds9._purge_tmp_dirs)
atexit.register(ds9._stop_running_process)
81 changes: 41 additions & 40 deletions imexam/ginga_viewer.py
@@ -1,4 +1,4 @@
"""Licensed under a 3-clause BSD style license - see LICENSE.rst.
"""Licensed under a 3-clause BSD style license - see LICENSE.rst
This class supports communication with a Ginga-based viewer.
For default key and mouse shortcuts in a Ginga window, see:
Expand All @@ -12,7 +12,6 @@
import warnings
import threading
import numpy as np
from matplotlib import get_backend

from . import util
from astropy.io import fits
Expand All @@ -24,8 +23,8 @@
from ginga.util import paths
from ginga.util import wcsmod

wcsmod.use('AstropyWCS')

wcsmod.use('AstropyWCS')

# module variables
_matplotlib_cmaps_added = False
Expand All @@ -39,23 +38,23 @@
class ginga_general(object):

""" A base class which controls all interactions between the user and the
ginga widget
ginga widget.
The ginga contructor creates a new window using the
ginga backend.
The ginga contructor creates a new window using the
ginga backend.
Parameters
----------
close_on_del : boolean, optional
If True, try to close the window when this instance is deleted.
Parameters
----------
close_on_del : boolean, optional
If True, try to close the window when this instance is deleted.
Attributes
----------
view: Ginga view object
The object instantiated from a Ginga view class
Attributes
----------
view: Ginga view object
The object instantiated from a Ginga view class
exam: imexamine object
exam: imexamine object
"""

def __init__(self, exam=None, close_on_del=True, logger=None, port=None):
Expand All @@ -65,15 +64,13 @@ def __init__(self, exam=None, close_on_del=True, logger=None, port=None):
-----
Ginga viewers all need a logger, if none is provided it will create one
the port option is for use in the Jupyter notebook since the server
The port option is for use in the Jupyter notebook since the server
displays the image to a distict port. The user can choose to have
multiple windows open at the same time as long as they have different
ports.
ports. If no port is specified, this class will choose an open port.
"""
global _matplotlib_cmaps_added
if port is None:
port = 667
self._port = port
self.exam = exam
self._close_on_del = close_on_del
Expand Down Expand Up @@ -435,7 +432,7 @@ def disp_header(self):
self.get_header()

def get_header(self):
"""Return current fits header as string or None if there's a problem."""
"""Return current fits header as string, None if there's a problem."""

# TODO return the simple header for arrays which are loaded

Expand Down Expand Up @@ -486,6 +483,7 @@ def _key_press_imexam(self, canvas, keyname):
if keyname == 'q':
# temporarily switch to non-imexam mode
self._release()
self.exam._close_plots()
return True

if keyname == 'backslash':
Expand Down Expand Up @@ -846,7 +844,7 @@ def show_window_commands(self):

def grab(self):
# if self.ginga_view:
self.ginga_view.show()
return self.ginga_view.show()


class ginga(ginga_general):
Expand All @@ -872,8 +870,6 @@ def __init__(self, exam=None, close_on_del=True, logger=None, port=None,
self.use_opencv = use_opencv
self._host = host
self._server = None
if port is None:
port = 9904
self._port = port

super(ginga, self).__init__(exam=exam, close_on_del=close_on_del,
Expand All @@ -890,11 +886,12 @@ def _open_browser(self):
doc directory for the HTML help pages")
print("Open a new browser window for: {}".format(self.ginga_view.url()))

def _create_viewer(self, bind_prefs, viewer_prefs, opencv=False, threads=1):
def _create_viewer(self, bind_prefs, viewer_prefs,
opencv=False, threads=1):
"""Ginga setup for data display in an HTML5 browser."""
from ginga.web.pgw import Widgets

# Set this to True if you have a non-buggy python OpenCv bindings
# Set opencv to True if you have a non-buggy python OpenCv bindings
# --it greatly speeds up some operations
self.use_opencv = opencv
self._threads = threads
Expand Down Expand Up @@ -926,29 +923,33 @@ def _start_server(self):
from ginga.web.pgw import ipg
if not self._port:
import socket
import errno
socket.setdefaulttimeout(0.05)
ports = [p for p in range(5800, 6000) if socket.socket().connect_ex(('127.0.0.1', p)) != 10035]
self._port = ports.pop()
ports = [p for p in range(8800, 9000) if
socket.socket().connect_ex(('127.0.0.1', p)) not in
(errno.EAGAIN, errno.EWOULDBLOCK)]
self._port = ports[-1]

self._server = ipg.make_server(host=self._host,
port=self._port,
use_opencv=self.use_opencv,
numthreads=self._threads)

backend = get_backend().lower()
no_ioloop = False # works best with qtconsole
if 'nbagg' in backend:
no_ioloop = True # assume in notebook
elif "pylab" in backend:
raise NotImplementedError
elif "qt4agg" in backend:
raise NotImplementedError
elif "tkagg" in backend:
print("If you aren't in the notebook and have problems with\
graphics, try switching to jupyter console")
try:
backend_check = get_ipython().config
except NameError:
backend_check = []
no_ioloop = False # ipython terminal
if 'IPKernelApp' in backend_check:
no_ioloop = True # jupyter console and notebook

self._server.start(no_ioloop=no_ioloop)

def _shutdown(self):
self._server.stop() # stop accepting connections
print('Stopped http server')


def close(self):
"""Close the viewing window"""
print("You must close the image window by hand")
"""Close the viewing window."""
self._shutdown()

0 comments on commit a3fc29e

Please sign in to comment.