Format: text/x-python
⬇ Download File (6.2 KB)
   1  # coding: utf-8
   2  """
   3  Support for creating GUI apps and starting event loops.
   4  
   5  IPython's GUI integration allows interactive plotting and GUI usage in IPython
   6  session. IPython has two different types of GUI integration:
   7  
   8  1. The terminal based IPython supports GUI event loops through Python's
   9     PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
  10     whenever raw_input is waiting for a user to type code. We implement GUI
  11     support in the terminal by setting PyOS_InputHook to a function that
  12     iterates the event loop for a short while. It is important to note that
  13     in this situation, the real GUI event loop is NOT run in the normal
  14     manner, so you can't use the normal means to detect that it is running.
  15  2. In the two process IPython kernel/frontend, the GUI event loop is run in
  16     the kernel. In this case, the event loop is run in the normal manner by
  17     calling the function or method of the GUI toolkit that starts the event
  18     loop.
  19  
  20  In addition to starting the GUI event loops in one of these two ways, IPython
  21  will *always* create an appropriate GUI application object when GUi
  22  integration is enabled.
  23  
  24  If you want your GUI apps to run in IPython you need to do two things:
  25  
  26  1. Test to see if there is already an existing main application object. If
  27     there is, you should use it. If there is not an existing application object
  28     you should create one.
  29  2. Test to see if the GUI event loop is running. If it is, you should not
  30     start it. If the event loop is not running you may start it.
  31  
  32  This module contains functions for each toolkit that perform these things
  33  in a consistent manner. Because of how PyOS_InputHook runs the event loop
  34  you cannot detect if the event loop is running using the traditional calls
  35  (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is
  36  set These methods will return a false negative. That is, they will say the
  37  event loop is not running, when is actually is. To work around this limitation
  38  we proposed the following informal protocol:
  39  
  40  * Whenever someone starts the event loop, they *must* set the ``_in_event_loop``
  41    attribute of the main application object to ``True``. This should be done
  42    regardless of how the event loop is actually run.
  43  * Whenever someone stops the event loop, they *must* set the ``_in_event_loop``
  44    attribute of the main application object to ``False``.
  45  * If you want to see if the event loop is running, you *must* use ``hasattr``
  46    to see if ``_in_event_loop`` attribute has been set. If it is set, you
  47    *must* use its value. If it has not been set, you can query the toolkit
  48    in the normal manner.
  49  * If you want GUI support and no one else has created an application or
  50    started the event loop you *must* do this. We don't want projects to
  51    attempt to defer these things to someone else if they themselves need it.
  52  
  53  The functions below implement this logic for each GUI toolkit. If you need
  54  to create custom application subclasses, you will likely have to modify this
  55  code for your own purposes. This code can be copied into your own project
  56  so you don't have to depend on IPython.
  57  
  58  """
  59  
  60  # Copyright (c) IPython Development Team.
  61  # Distributed under the terms of the Modified BSD License.
  62  
  63  from IPython.core.getipython import get_ipython
  64  
  65  #-----------------------------------------------------------------------------
  66  # wx
  67  #-----------------------------------------------------------------------------
  68  
  69  def get_app_wx(*args, **kwargs):
  70      """Create a new wx app or return an exiting one."""
  71      import wx
  72      app = wx.GetApp()
  73      if app is None:
  74          if 'redirect' not in kwargs:
  75              kwargs['redirect'] = False
  76          app = wx.PySimpleApp(*args, **kwargs)
  77      return app
  78  
  79  def is_event_loop_running_wx(app=None):
  80      """Is the wx event loop running."""
  81      # New way: check attribute on shell instance
  82      ip = get_ipython()
  83      if ip is not None:
  84          if ip.active_eventloop and ip.active_eventloop == 'wx':
  85              return True
  86          # Fall through to checking the application, because Wx has a native way
  87          # to check if the event loop is running, unlike Qt.
  88  
  89      # Old way: check Wx application
  90      if app is None:
  91          app = get_app_wx()
  92      if hasattr(app, '_in_event_loop'):
  93          return app._in_event_loop
  94      else:
  95          return app.IsMainLoopRunning()
  96  
  97  def start_event_loop_wx(app=None):
  98      """Start the wx event loop in a consistent manner."""
  99      if app is None:
 100          app = get_app_wx()
 101      if not is_event_loop_running_wx(app):
 102          app._in_event_loop = True
 103          app.MainLoop()
 104          app._in_event_loop = False
 105      else:
 106          app._in_event_loop = True
 107  
 108  #-----------------------------------------------------------------------------
 109  # Qt
 110  #-----------------------------------------------------------------------------
 111  
 112  def get_app_qt4(*args, **kwargs):
 113      """Create a new Qt app or return an existing one."""
 114      from IPython.external.qt_for_kernel import QtGui
 115      app = QtGui.QApplication.instance()
 116      if app is None:
 117          if not args:
 118              args = ([""],)
 119          app = QtGui.QApplication(*args, **kwargs)
 120      return app
 121  
 122  def is_event_loop_running_qt4(app=None):
 123      """Is the qt event loop running."""
 124      # New way: check attribute on shell instance
 125      ip = get_ipython()
 126      if ip is not None:
 127          return ip.active_eventloop and ip.active_eventloop.startswith('qt')
 128  
 129      # Old way: check attribute on QApplication singleton
 130      if app is None:
 131          app = get_app_qt4([""])
 132      if hasattr(app, '_in_event_loop'):
 133          return app._in_event_loop
 134      else:
 135          # Does qt provide a other way to detect this?
 136          return False
 137  
 138  def start_event_loop_qt4(app=None):
 139      """Start the qt event loop in a consistent manner."""
 140      if app is None:
 141          app = get_app_qt4([""])
 142      if not is_event_loop_running_qt4(app):
 143          app._in_event_loop = True
 144          app.exec_()
 145          app._in_event_loop = False
 146      else:
 147          app._in_event_loop = True
 148  
 149  #-----------------------------------------------------------------------------
 150  # Tk
 151  #-----------------------------------------------------------------------------
 152  
 153  #-----------------------------------------------------------------------------
 154  # gtk
 155  #-----------------------------------------------------------------------------
 156