From d0abfbeaeaafaa7e26142e2bda6942010068cfb4 2010-09-02 16:29:04 From: Brian Granger Date: 2010-09-02 16:29:04 Subject: [PATCH] Updating terminal GUI support to use guisupport.py for qt4/wx. --- diff --git a/IPython/__init__.py b/IPython/__init__.py index c8f33cf..2a0bc8d 100755 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -45,17 +45,6 @@ from .core.error import TryNext from .core.interactiveshell import InteractiveShell from .testing import test -from .lib import ( - enable_wx, disable_wx, - enable_gtk, disable_gtk, - enable_qt4, disable_qt4, - enable_tk, disable_tk, - set_inputhook, clear_inputhook, - current_gui, spin, - appstart_qt4, appstart_wx, - appstart_gtk, appstart_tk -) - # Release data __author__ = '' for author, email in release.authors.values(): diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 3ce9ce8..0287f3d 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -55,7 +55,6 @@ from IPython.core.macro import Macro from IPython.core import page from IPython.core.prefilter import ESC_MAGIC from IPython.lib.pylabtools import mpl_runner -from IPython.lib.inputhook import enable_gui from IPython.external.Itpl import itpl, printpl from IPython.testing import decorators as testdec from IPython.utils.io import file_read, nlprint @@ -3475,7 +3474,7 @@ Defaulting color scheme to 'NoColor'""" def magic_gui(self, parameter_s=''): """Enable or disable IPython GUI event loop integration. - %gui [-a] [GUINAME] + %gui [GUINAME] This magic replaces IPython's threaded shells that were activated using the (pylab/wthread/etc.) command line flags. GUI toolkits @@ -3492,17 +3491,11 @@ Defaulting color scheme to 'NoColor'""" WARNING: after any of these has been called you can simply create an application object, but DO NOT start the event loop yourself, as we have already handled that. - - If you want us to create an appropriate application object add the - "-a" flag to your command:: - - %gui -a wx - - This is highly recommended for most users. """ - opts, arg = self.parse_options(parameter_s,'a') + from IPython.lib.inputhook import enable_gui + opts, arg = self.parse_options(parameter_s='') if arg=='': arg = None - return enable_gui(arg, 'a' in opts) + return enable_gui(arg) def magic_load_ext(self, module_str): """Load an IPython extension by its module name.""" diff --git a/IPython/lib/__init__.py b/IPython/lib/__init__.py index 647be0f..7b14e55 100644 --- a/IPython/lib/__init__.py +++ b/IPython/lib/__init__.py @@ -21,11 +21,9 @@ from IPython.lib.inputhook import ( enable_qt4, disable_qt4, enable_tk, disable_tk, set_inputhook, clear_inputhook, - current_gui, spin, - appstart_qt4, appstart_wx, - appstart_gtk, appstart_tk + current_gui ) #----------------------------------------------------------------------------- # Code -#----------------------------------------------------------------------------- \ No newline at end of file +#----------------------------------------------------------------------------- diff --git a/IPython/lib/inputhook.py b/IPython/lib/inputhook.py index cf5deae..4f3bbee 100755 --- a/IPython/lib/inputhook.py +++ b/IPython/lib/inputhook.py @@ -34,137 +34,6 @@ GUI_TK = 'tk' #----------------------------------------------------------------------------- -class _DummyMainloop(object): - """A special manager to hijack GUI mainloops that is mostly a no-op. - - We are not using this class currently as it breaks GUI code that calls - a mainloop function after the app has started to process pending events. - """ - def __init__(self, ml, ihm, gui_type): - self.ml = ml - self.ihm = ihm - self.gui_type = gui_type - - def __call__(self, *args, **kw): - if self.ihm.current_gui() == self.gui_type: - pass - else: - self.ml(*args, **kw) - - -#----------------------------------------------------------------------------- -# Appstart and spin functions -#----------------------------------------------------------------------------- - - -def appstart_qt4(app): - """Start the qt4 event loop in a way that plays with IPython. - - When a qt4 app is run interactively in IPython, the event loop should - not be started. This function checks to see if IPython's qt4 integration - is activated and if so, it passes. If not, it will call the :meth:`exec_` - method of the main qt4 app. - - This function should be used by users who want their qt4 scripts to work - both at the command line and in IPython. These users should put the - following logic at the bottom on their script, after they create a - :class:`QApplication` instance (called ``app`` here):: - - try: - from IPython.lib.inputhook import appstart_qt4 - appstart_qt4(app) - except ImportError: - app.exec_() - """ - from PyQt4 import QtCore - - assert isinstance(app, QtCore.QCoreApplication) - if app is not None: - if current_gui() == GUI_QT4: - pass - else: - app.exec_() - - -def appstart_wx(app): - """Start the wx event loop in a way that plays with IPython. - - When a wx app is run interactively in IPython, the event loop should - not be started. This function checks to see if IPython's wx integration - is activated and if so, it passes. If not, it will call the - :meth:`MainLoop` method of the main qt4 app. - - This function should be used by users who want their wx scripts to work - both at the command line and in IPython. These users should put the - following logic at the bottom on their script, after they create a - :class:`App` instance (called ``app`` here):: - - try: - from IPython.lib.inputhook import appstart_wx - appstart_wx(app) - except ImportError: - app.MainLoop() - """ - import wx - - assert isinstance(app, wx.App) - if app is not None: - if current_gui() == GUI_WX: - pass - else: - app.MainLoop() - - -def appstart_tk(app): - """Start the tk event loop in a way that plays with IPython. - - When a tk app is run interactively in IPython, the event loop should - not be started. This function checks to see if IPython's tk integration - is activated and if so, it passes. If not, it will call the - :meth:`mainloop` method of the tk object passed to this method. - - This function should be used by users who want their tk scripts to work - both at the command line and in IPython. These users should put the - following logic at the bottom on their script, after they create a - :class:`Tk` instance (called ``app`` here):: - - try: - from IPython.lib.inputhook import appstart_tk - appstart_tk(app) - except ImportError: - app.mainloop() - """ - if app is not None: - if current_gui() == GUI_TK: - pass - else: - app.mainloop() - -def appstart_gtk(): - """Start the gtk event loop in a way that plays with IPython. - - When a gtk app is run interactively in IPython, the event loop should - not be started. This function checks to see if IPython's gtk integration - is activated and if so, it passes. If not, it will call - :func:`gtk.main`. Unlike the other appstart implementations, this does - not take an ``app`` argument. - - This function should be used by users who want their gtk scripts to work - both at the command line and in IPython. These users should put the - following logic at the bottom on their script:: - - try: - from IPython.lib.inputhook import appstart_gtk - appstart_gtk() - except ImportError: - gtk.main() - """ - import gtk - if current_gui() == GUI_GTK: - pass - else: - gtk.main() - #----------------------------------------------------------------------------- # Main InputHookManager class #----------------------------------------------------------------------------- @@ -180,11 +49,6 @@ class InputHookManager(object): def __init__(self): self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int) self._apps = {} - self._spinner_dict = { - GUI_QT4 : self._spin_qt4, - GUI_WX : self._spin_wx, - GUI_GTK : self._spin_gtk, - GUI_TK : self._spin_tk} self._reset() def _reset(self): @@ -193,122 +57,6 @@ class InputHookManager(object): self._installed = False self._current_gui = None - def _hijack_wx(self): - """Hijack the wx mainloop so a user calling it won't cause badness. - - We are not currently using this as it breaks GUI code that calls a - mainloop at anytime but startup. - """ - import wx - if hasattr(wx, '_core_'): core = getattr(wx, '_core_') - elif hasattr(wx, '_core'): core = getattr(wx, '_core') - else: raise AttributeError('Could not find wx core module') - orig_mainloop = core.PyApp_MainLoop - core.PyApp_MainLoop = _DummyMainloop - return orig_mainloop - - def _hijack_qt4(self): - """Hijack the qt4 mainloop so a user calling it won't cause badness. - - We are not currently using this as it breaks GUI code that calls a - mainloop at anytime but startup. - """ - from PyQt4 import QtGui, QtCore - orig_mainloop = QtGui.qApp.exec_ - dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_QT4) - QtGui.qApp.exec_ = dumb_ml - QtGui.QApplication.exec_ = dumb_ml - QtCore.QCoreApplication.exec_ = dumb_ml - return orig_mainloop - - def _hijack_gtk(self): - """Hijack the gtk mainloop so a user calling it won't cause badness. - - We are not currently using this as it breaks GUI code that calls a - mainloop at anytime but startup. - """ - import gtk - orig_mainloop = gtk.main - dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_GTK) - gtk.mainloop = dumb_ml - gtk.main = dumb_ml - return orig_mainloop - - def _hijack_tk(self): - """Hijack the tk mainloop so a user calling it won't cause badness. - - We are not currently using this as it breaks GUI code that calls a - mainloop at anytime but startup. - """ - import Tkinter - # FIXME: gtk is not imported here and we shouldn't be using gtk.main! - orig_mainloop = gtk.main - dumb_ml = _DummyMainloop(orig_mainloop, self, GUI_TK) - Tkinter.Misc.mainloop = dumb_ml - Tkinter.mainloop = dumb_ml - - def _spin_qt4(self): - """Process all pending events in the qt4 event loop. - - This is for internal IPython use only and user code should not call this. - Instead, they should issue the raw GUI calls themselves. - """ - from PyQt4 import QtCore - - app = QtCore.QCoreApplication.instance() - if app is not None: - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents) - - def _spin_wx(self): - """Process all pending events in the wx event loop. - - This is for internal IPython use only and user code should not call this. - Instead, they should issue the raw GUI calls themselves. - """ - import wx - app = wx.GetApp() - if app is not None and wx.Thread_IsMain(): - evtloop = wx.EventLoop() - ea = wx.EventLoopActivator(evtloop) - while evtloop.Pending(): - evtloop.Dispatch() - app.ProcessIdle() - del ea - - def _spin_gtk(self): - """Process all pending events in the gtk event loop. - - This is for internal IPython use only and user code should not call this. - Instead, they should issue the raw GUI calls themselves. - """ - import gtk - gtk.gdk.threads_enter() - while gtk.events_pending(): - gtk.main_iteration(False) - gtk.gdk.flush() - gtk.gdk.threads_leave() - - def _spin_tk(self): - """Process all pending events in the tk event loop. - - This is for internal IPython use only and user code should not call this. - Instead, they should issue the raw GUI calls themselves. - """ - app = self._apps.get(GUI_TK) - if app is not None: - app.update() - - def spin(self): - """Process pending events in the current gui. - - This method is just provided for IPython to use internally if needed - for things like testing. Third party projects should not call this - method, but instead should call the underlying GUI toolkit methods - that we are calling. - """ - spinner = self._spinner_dict.get(self._current_gui, lambda: None) - spinner() - def get_pyos_inputhook(self): """Return the current PyOS_InputHook as a ctypes.c_void_p.""" return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook") @@ -365,7 +113,7 @@ class InputHookManager(object): elif self._apps.has_key(gui): del self._apps[gui] - def enable_wx(self, app=False): + def enable_wx(self): """Enable event loop integration with wxPython. Parameters @@ -393,22 +141,24 @@ class InputHookManager(object): from IPython.lib.inputhookwx import inputhook_wx self.set_inputhook(inputhook_wx) self._current_gui = GUI_WX - if app: - import wx - app = wx.GetApp() - if app is None: - app = wx.App(redirect=False, clearSigInt=False) - self._apps[GUI_WX] = app - return app + import wx + app = wx.GetApp() + if app is None: + app = wx.App(redirect=False, clearSigInt=False) + app._in_event_loop = True + self._apps[GUI_WX] = app + return app def disable_wx(self): """Disable event loop integration with wxPython. This merely sets PyOS_InputHook to NULL. """ + if self._apps.has_key(GUI_WX): + self._apps[GUI_WX]._in_event_loop = False self.clear_inputhook() - def enable_qt4(self, app=False): + def enable_qt4(self): """Enable event loop integration with PyQt4. Parameters @@ -440,19 +190,21 @@ class InputHookManager(object): except AttributeError: pass self._current_gui = GUI_QT4 - if app: - from PyQt4 import QtGui - app = QtCore.QCoreApplication.instance() - if app is None: - app = QtGui.QApplication(sys.argv) - self._apps[GUI_QT4] = app - return app + from PyQt4 import QtGui + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtGui.QApplication([" "]) + app._in_event_loop = True + self._apps[GUI_QT4] = app + return app def disable_qt4(self): """Disable event loop integration with PyQt4. This merely sets PyOS_InputHook to NULL. """ + if self._apps.has_key(GUI_QT4): + self._apps[GUI_QT4]._in_event_loop = False self.clear_inputhook() def enable_gtk(self, app=False): @@ -533,11 +285,10 @@ clear_inputhook = inputhook_manager.clear_inputhook set_inputhook = inputhook_manager.set_inputhook current_gui = inputhook_manager.current_gui clear_app_refs = inputhook_manager.clear_app_refs -spin = inputhook_manager.spin # Convenience function to switch amongst them -def enable_gui(gui=None, app=True): +def enable_gui(gui=None): """Switch amongst GUI input hooks by name. This is just a utility wrapper around the methods of the InputHookManager @@ -569,4 +320,5 @@ def enable_gui(gui=None, app=True): except KeyError: e="Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys()) raise ValueError(e) - return gui_hook(app) + return gui_hook() + diff --git a/IPython/lib/pylabtools.py b/IPython/lib/pylabtools.py index 297c60a..ca9442b 100644 --- a/IPython/lib/pylabtools.py +++ b/IPython/lib/pylabtools.py @@ -3,7 +3,9 @@ Authors ------- -Fernando Perez. + +* Fernando Perez. +* Brian Granger """ #----------------------------------------------------------------------------- @@ -177,3 +179,4 @@ def mpl_runner(safe_execfile): pylab.draw_if_interactive.called = False return mpl_execfile + diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index 538caa2..5b811e8 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -374,6 +374,11 @@ class ZMQInteractiveShell(InteractiveShell): } self.payload_manager.write_payload(payload) + def magic_gui(self, *args, **kwargs): + raise NotImplementedError('GUI support must be enabled in command line options.') + + def magic_pylab(self, *args, **kwargs): + raise NotImplementedError('pylab support must be enabled in commandl in options.') def _showtraceback(self, etype, evalue, stb):