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