diff --git a/IPython/frontend/terminal/console/interactiveshell.py b/IPython/frontend/terminal/console/interactiveshell.py
index 1291822..983d154 100644
--- a/IPython/frontend/terminal/console/interactiveshell.py
+++ b/IPython/frontend/terminal/console/interactiveshell.py
@@ -19,15 +19,26 @@ from __future__ import print_function
 
 import bdb
 import signal
+import os
 import sys
 import time
+import subprocess
+from io import BytesIO
+import base64
 
 from Queue import Empty
 
+try:
+    from contextlib import nested
+except:
+    from IPython.utils.nested_context import nested
+
 from IPython.core.alias import AliasManager, AliasError
 from IPython.core import page
 from IPython.utils.warn import warn, error, fatal
 from IPython.utils import io
+from IPython.utils.traitlets import List, Enum, Any
+from IPython.utils.tempdir import NamedFileInTemporaryDirectory
 
 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
 from IPython.frontend.terminal.console.completer import ZMQCompleter
@@ -36,7 +47,64 @@ from IPython.frontend.terminal.console.completer import ZMQCompleter
 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
     """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
     _executing = False
-    
+
+    image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
+                         config=True, help=
+        """
+        Handler for image type output.  This is useful, for example,
+        when connecting to the kernel in which pylab inline backend is
+        activated.  There are four handlers defined.  'PIL': Use
+        Python Imaging Library to popup image; 'stream': Use an
+        external program to show the image.  Image will be fed into
+        the STDIN of the program.  You will need to configure
+        `stream_image_handler`; 'tempfile': Use an external program to
+        show the image.  Image will be saved in a temporally file and
+        the program is called with the temporally file.  You will need
+        to configure `tempfile_image_handler`; 'callable': You can set
+        any Python callable which is called with the image data.  You
+        will need to configure `callable_image_handler`.
+        """
+    )
+
+    stream_image_handler = List(config=True, help=
+        """
+        Command to invoke an image viewer program when you are using
+        'stream' image handler.  This option is a list of string where
+        the first element is the command itself and reminders are the
+        options for the command.  Raw image data is given as STDIN to
+        the program.
+        """
+    )
+
+    tempfile_image_handler = List(config=True, help=
+        """
+        Command to invoke an image viewer program when you are using
+        'tempfile' image handler.  This option is a list of string
+        where the first element is the command itself and reminders
+        are the options for the command.  You can use {file} and
+        {format} in the string to represent the location of the
+        generated image file and image format.
+        """
+    )
+
+    callable_image_handler = Any(config=True, help=
+        """
+        Callable object called via 'callable' image handler with one
+        argument, `data`, which is `msg["content"]["data"]` where
+        `msg` is the message from iopub channel.  For exmaple, you can
+        find base64 encoded PNG data as `data['image/png']`.
+        """
+    )
+
+    mime_preference = List(
+        default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
+        config=True, allow_none=False, help=
+        """
+        Preferred object representation MIME type in order.  First
+        matched MIME type will be used.
+        """
+    )
+
     def __init__(self, *args, **kwargs):
         self.km = kwargs.pop('kernel_manager')
         self.session_id = self.km.session.session
@@ -163,6 +231,7 @@ class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
                 elif msg_type == 'pyout':
                     self.execution_count = int(sub_msg["content"]["execution_count"])
                     format_dict = sub_msg["content"]["data"]
+                    self.handle_rich_data(format_dict)
                     # taken from DisplayHook.__call__:
                     hook = self.displayhook
                     hook.start_displayhook()
@@ -171,6 +240,61 @@ class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
                     hook.log_output(format_dict)
                     hook.finish_displayhook()
 
+                elif msg_type == 'display_data':
+                    self.handle_rich_data(sub_msg["content"]["data"])
+
+    _imagemime = {
+        'image/png': 'png',
+        'image/jpeg': 'jpeg',
+        'image/svg+xml': 'svg',
+    }
+
+    def handle_rich_data(self, data):
+        for mime in self.mime_preference:
+            if mime in data and mime in self._imagemime:
+                self.handle_image(data, mime)
+                return
+
+    def handle_image(self, data, mime):
+        handler = getattr(
+            self, 'handle_image_{0}'.format(self.image_handler), None)
+        if handler:
+            handler(data, mime)
+
+    def handle_image_PIL(self, data, mime):
+        if mime not in ('image/png', 'image/jpeg'):
+            return
+        import PIL.Image
+        raw = base64.decodestring(data[mime].encode('ascii'))
+        img = PIL.Image.open(BytesIO(raw))
+        img.show()
+
+    def handle_image_stream(self, data, mime):
+        raw = base64.decodestring(data[mime].encode('ascii'))
+        imageformat = self._imagemime[mime]
+        fmt = dict(format=imageformat)
+        args = [s.format(**fmt) for s in self.stream_image_handler]
+        with open(os.devnull, 'w') as devnull:
+            proc = subprocess.Popen(
+                args, stdin=subprocess.PIPE,
+                stdout=devnull, stderr=devnull)
+            proc.communicate(raw)
+
+    def handle_image_tempfile(self, data, mime):
+        raw = base64.decodestring(data[mime].encode('ascii'))
+        imageformat = self._imagemime[mime]
+        filename = 'tmp.{0}'.format(imageformat)
+        with nested(NamedFileInTemporaryDirectory(filename),
+                    open(os.devnull, 'w')) as (f, devnull):
+            f.write(raw)
+            f.flush()
+            fmt = dict(file=f.name, format=imageformat)
+            args = [s.format(**fmt) for s in self.tempfile_image_handler]
+            subprocess.call(args, stdout=devnull, stderr=devnull)
+
+    def handle_image_callable(self, data, mime):
+        self.callable_image_handler(data)
+
     def handle_stdin_request(self, timeout=0.1):
         """ Method to capture raw_input
         """
diff --git a/IPython/frontend/terminal/console/tests/test_image_handler.py b/IPython/frontend/terminal/console/tests/test_image_handler.py
new file mode 100644
index 0000000..b10df00
--- /dev/null
+++ b/IPython/frontend/terminal/console/tests/test_image_handler.py
@@ -0,0 +1,95 @@
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+import os
+import sys
+import unittest
+import base64
+
+from IPython.zmq.kernelmanager import KernelManager
+from IPython.frontend.terminal.console.interactiveshell \
+    import ZMQTerminalInteractiveShell
+from IPython.utils.tempdir import TemporaryDirectory
+from IPython.testing.tools import monkeypatch
+from IPython.testing.decorators import skip_without
+from IPython.utils.ipstruct import Struct
+
+
+SCRIPT_PATH = os.path.join(
+    os.path.abspath(os.path.dirname(__file__)), 'writetofile.py')
+
+
+class ZMQTerminalInteractiveShellTestCase(unittest.TestCase):
+
+    def setUp(self):
+        km = KernelManager()
+        self.shell = ZMQTerminalInteractiveShell(kernel_manager=km)
+        self.raw = b'dummy data'
+        self.mime = 'image/png'
+        self.data = {self.mime: base64.encodestring(self.raw).decode('ascii')}
+
+    def test_no_call_by_default(self):
+        def raise_if_called(*args, **kwds):
+            assert False
+
+        shell = self.shell
+        shell.handle_image_PIL = raise_if_called
+        shell.handle_image_stream = raise_if_called
+        shell.handle_image_tempfile = raise_if_called
+        shell.handle_image_callable = raise_if_called
+
+        shell.handle_image(None, None)  # arguments are dummy
+
+    @skip_without('PIL')
+    def test_handle_image_PIL(self):
+        import PIL.Image
+
+        open_called_with = []
+        show_called_with = []
+
+        def fake_open(arg):
+            open_called_with.append(arg)
+            return Struct(show=lambda: show_called_with.append(None))
+
+        with monkeypatch(PIL.Image, 'open', fake_open):
+            self.shell.handle_image_PIL(self.data, self.mime)
+
+        self.assertEqual(len(open_called_with), 1)
+        self.assertEqual(len(show_called_with), 1)
+        self.assertEqual(open_called_with[0].getvalue(), self.raw)
+
+    def check_handler_with_file(self, inpath, handler):
+        shell = self.shell
+        configname = '{0}_image_handler'.format(handler)
+        funcname = 'handle_image_{0}'.format(handler)
+
+        assert hasattr(shell, configname)
+        assert hasattr(shell, funcname)
+
+        with TemporaryDirectory() as tmpdir:
+            outpath = os.path.join(tmpdir, 'data')
+            cmd = [sys.executable, SCRIPT_PATH, inpath, outpath]
+            setattr(shell, configname, cmd)
+            getattr(shell, funcname)(self.data, self.mime)
+            # cmd is called and file is closed.  So it's safe to open now.
+            with open(outpath, 'rb') as file:
+                transferred = file.read()
+
+        self.assertEqual(transferred, self.raw)
+
+    def test_handle_image_stream(self):
+        self.check_handler_with_file('-', 'stream')
+
+    def test_handle_image_tempfile(self):
+        self.check_handler_with_file('{file}', 'tempfile')
+
+    def test_handle_image_callable(self):
+        called_with = []
+        self.shell.callable_image_handler = called_with.append
+        self.shell.handle_image_callable(self.data, self.mime)
+        self.assertEqual(len(called_with), 1)
+        assert called_with[0] is self.data
diff --git a/IPython/frontend/terminal/console/tests/writetofile.py b/IPython/frontend/terminal/console/tests/writetofile.py
new file mode 100644
index 0000000..a6f9e28
--- /dev/null
+++ b/IPython/frontend/terminal/console/tests/writetofile.py
@@ -0,0 +1,33 @@
+#-----------------------------------------------------------------------------
+# Copyright (C) 2012 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+"""
+Copy data from input file to output file for testing.
+
+Command line usage:
+
+    python writetofile.py INPUT OUTPUT
+
+Binary data from INPUT file is copied to OUTPUT file.
+If INPUT is '-', stdin is used.
+
+"""
+
+if __name__ == '__main__':
+    import sys
+    from IPython.utils.py3compat import PY3
+    (inpath, outpath) = sys.argv[1:]
+
+    if inpath == '-':
+        if PY3:
+            infile = sys.stdin.buffer
+        else:
+            infile = sys.stdin
+    else:
+        infile = open(inpath, 'rb')
+
+    open(outpath, 'w+b').write(infile.read())
diff --git a/IPython/utils/tempdir.py b/IPython/utils/tempdir.py
index 57f02fc..364e119 100644
--- a/IPython/utils/tempdir.py
+++ b/IPython/utils/tempdir.py
@@ -3,14 +3,14 @@
 This is copied from the stdlib and will be standard in Python 3.2 and onwards.
 """
 
+import os as _os
+
 # This code should only be used in Python versions < 3.2, since after that we
 # can rely on the stdlib itself.
 try:
     from tempfile import TemporaryDirectory
 
 except ImportError:
-    
-    import os as _os
     from tempfile import mkdtemp, template
 
     class TemporaryDirectory(object):
@@ -74,3 +74,33 @@ except ImportError:
                 self._rmdir(path)
             except self._os_error:
                 pass
+
+
+class NamedFileInTemporaryDirectory(object):
+
+    def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
+        """
+        Open a file named `filename` in a temporary directory.
+
+        This context manager is preferred over `NamedTemporaryFile` in
+        stdlib `tempfile` when one needs to reopen the file.
+
+        Arguments `mode` and `bufsize` are passed to `open`.
+        Rest of the arguments are passed to `TemporaryDirectory`.
+
+        """
+        self._tmpdir = TemporaryDirectory(**kwds)
+        path = _os.path.join(self._tmpdir.name, filename)
+        self.file = open(path, mode, bufsize)
+
+    def cleanup(self):
+        self.file.close()
+        self._tmpdir.cleanup()
+
+    __del__ = cleanup
+
+    def __enter__(self):
+        return self.file
+
+    def __exit__(self, type, value, traceback):
+        self.cleanup()
diff --git a/IPython/utils/tests/test_tempdir.py b/IPython/utils/tests/test_tempdir.py
new file mode 100644
index 0000000..8e919e5
--- /dev/null
+++ b/IPython/utils/tests/test_tempdir.py
@@ -0,0 +1,20 @@
+#-----------------------------------------------------------------------------
+#  Copyright (C) 2012-  The IPython Development Team
+#
+#  Distributed under the terms of the BSD License.  The full license is in
+#  the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+import os
+
+from IPython.utils.tempdir import NamedFileInTemporaryDirectory
+
+
+def test_named_file_in_temporary_directory():
+    with NamedFileInTemporaryDirectory('filename') as file:
+        name = file.name
+        assert not file.closed
+        assert os.path.exists(name)
+        file.write(b'test')
+    assert file.closed
+    assert not os.path.exists(name)