From 11a6689bc45d91e8667d4e711c5e4f21c72ad2a0 2009-08-25 22:13:55 From: Brian Granger Date: 2009-08-25 22:13:55 Subject: [PATCH] Merging upstream changes from inputhook and config-refactor. Some minor changes needed to be made to make sure component queries in ipapi worked properly. --- diff --git a/IPython/__init__.py b/IPython/__init__.py index 19caef8..c3a4876 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -34,8 +34,23 @@ if sys.version[0:3] < '2.4': # Therefore, non-IPython modules can be added to extensions directory sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) -from IPython.core import iplib +#----------------------------------------------------------------------------- +# Setup the top level names +#----------------------------------------------------------------------------- +from IPython.core.iplib import InteractiveShell +from IPython.core.error import TryNext + +from IPython.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__ = '' diff --git a/IPython/core/component.py b/IPython/core/component.py index dfce84d..5caed04 100644 --- a/IPython/core/component.py +++ b/IPython/core/component.py @@ -60,34 +60,30 @@ class MetaComponentTracker(type): c.__instance_refs[c.__numcreated] = instance return instance - def get_instances(cls, name=None, klass=None, root=None): + def get_instances(cls, name=None, root=None): """Get all instances of cls and its subclasses. Parameters ---------- name : str Limit to components with this name. - klass : class - Limit to components having isinstance(component, klass) root : Component or subclass Limit to components having this root. """ instances = cls.__instance_refs.values() if name is not None: instances = [i for i in instances if i.name == name] - if klass is not None: - instances = [i for i in instances if isinstance(i, klass)] if root is not None: instances = [i for i in instances if i.root == root] return instances - def get_instances_by_condition(cls, call, name=None, klass=None, root=None): + def get_instances_by_condition(cls, call, name=None, root=None): """Get all instances of cls, i such that call(i)==True. - This also takes the ``name``, ``klass`` and ``root`` arguments of + This also takes the ``name`` and ``root`` arguments of :meth:`get_instance` """ - return [i for i in cls.get_instances(name,klass,root) if call(i)] + return [i for i in cls.get_instances(name, root) if call(i)] class ComponentNameGenerator(object): @@ -199,6 +195,14 @@ class Component(HasTraitlets): "root != parent.root") def _config_changed(self, name, old, new): + """Update all the class traits having a config_key with the config. + + For any class traitlet with a ``config_key`` metadata attribute, we + update the traitlet with the value of the corresponding config entry. + + In the future, we might want to do a pop here so stale config info + is not passed onto children. + """ # Get all traitlets with a config_key metadata entry traitlets = self.traitlets('config_key') for k, v in traitlets.items(): @@ -215,13 +219,13 @@ class Component(HasTraitlets): return self._children def _remove_child(self, child): - """A private method for removing children componenets.""" + """A private method for removing children components.""" if child in self._children: index = self._children.index(child) del self._children[index] def _add_child(self, child): - """A private method for adding children componenets.""" + """A private method for adding children components.""" if child not in self._children: self._children.append(child) diff --git a/IPython/core/ipapi.py b/IPython/core/ipapi.py index f448613..f379589 100644 --- a/IPython/core/ipapi.py +++ b/IPython/core/ipapi.py @@ -29,6 +29,7 @@ Authors: from IPython.core.error import TryNext, UsageError from IPython.core.component import Component +from IPython.core.iplib import InteractiveShell #----------------------------------------------------------------------------- # Classes and functions @@ -36,7 +37,7 @@ from IPython.core.component import Component def get(): """Get the most recently created InteractiveShell instance.""" - insts = Component.get_instances(name='__IP') + insts = InteractiveShell.get_instances() most_recent = insts[0] for inst in insts[1:]: if inst.created > most_recent.created: diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 96634dc..b7d1398 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3548,11 +3548,11 @@ Defaulting color scheme to 'NoColor'""" interrupts should work without any problems. The following toolkits are supports: wxPython, PyQt4, PyGTK, and Tk:: - %gui wx # enable wxPython event loop integration - %gui qt4 # enable PyQt4 event loop integration - %gui gtk # enable PyGTK event loop integration - %gui tk # enable Tk event loop integration - %gui # disable all event loop integration + %gui wx # enable wxPython event loop integration + %gui qt4|qt # enable PyQt4 event loop integration + %gui gtk # enable PyGTK event loop integration + %gui tk # enable Tk event loop integration + %gui # disable all event loop integration WARNING: after any of these has been called you can simply create an application object, but DO NOT start the event loop yourself, as @@ -3574,7 +3574,7 @@ Defaulting color scheme to 'NoColor'""" inputhook.clear_inputhook() elif 'wx' in parameter_s: return inputhook.enable_wx(app) - elif 'qt4' in parameter_s: + elif ('qt4' in parameter_s) or ('qt' in parameter_s): return inputhook.enable_qt4(app) elif 'gtk' in parameter_s: return inputhook.enable_gtk(app) diff --git a/IPython/core/tests/test_component.py b/IPython/core/tests/test_component.py index bc21616..5db9ed9 100644 --- a/IPython/core/tests/test_component.py +++ b/IPython/core/tests/test_component.py @@ -52,7 +52,7 @@ class TestComponentMeta(TestCase): c2 = MyOtherComponent(c1) c3 = MyOtherComponent(c2) self.assertEquals(MyComponent.get_instances(), [c1, c2, c3]) - self.assertEquals(MyComponent.get_instances(klass=MyOtherComponent), [c2, c3]) + self.assertEquals(MyOtherComponent.get_instances(), [c2, c3]) def test_get_instances_root(self): class MyComponent(Component): diff --git a/IPython/lib/__init__.py b/IPython/lib/__init__.py index 8e4a70c..647be0f 100644 --- a/IPython/lib/__init__.py +++ b/IPython/lib/__init__.py @@ -20,7 +20,10 @@ from IPython.lib.inputhook import ( enable_gtk, disable_gtk, enable_qt4, disable_qt4, enable_tk, disable_tk, - set_inputhook, clear_inputhook + set_inputhook, clear_inputhook, + current_gui, spin, + appstart_qt4, appstart_wx, + appstart_gtk, appstart_tk ) #----------------------------------------------------------------------------- diff --git a/IPython/lib/inputhook.py b/IPython/lib/inputhook.py old mode 100644 new mode 100755 index ee965a6..0043379 --- a/IPython/lib/inputhook.py +++ b/IPython/lib/inputhook.py @@ -19,7 +19,153 @@ import ctypes import sys #----------------------------------------------------------------------------- -# Code +# Constants +#----------------------------------------------------------------------------- + +# Constants for identifying the GUI toolkits. +GUI_WX = 'wx' +GUI_QT4 = 'qt4' +GUI_GTK = 'gtk' +GUI_TK = 'tk' + +#----------------------------------------------------------------------------- +# Utility classes +#----------------------------------------------------------------------------- + + +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, QtGui + + 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 #----------------------------------------------------------------------------- @@ -32,6 +178,12 @@ 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): @@ -40,19 +192,131 @@ class InputHookManager(object): self._installed = False self._current_gui = None - def get_pyos_inputhook(self): - """Return the current PyOS_InputHook as a ctypes.c_void_p. + 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 + 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, QtGui + + 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") def get_pyos_inputhook_as_func(self): - """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE. - """ + """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.""" return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook") def set_inputhook(self, callback): - """Set PyOS_InputHook to callback and return the previous one. - """ + """Set PyOS_InputHook to callback and return the previous one.""" self._callback = callback self._callback_pyfunctype = self.PYFUNC(callback) pyos_inputhook_ptr = self.get_pyos_inputhook() @@ -63,14 +327,33 @@ class InputHookManager(object): return original def clear_inputhook(self): - """Set PyOS_InputHook to NULL and return the previous one. - """ + """Set PyOS_InputHook to NULL and return the previous one.""" pyos_inputhook_ptr = self.get_pyos_inputhook() original = self.get_pyos_inputhook_as_func() pyos_inputhook_ptr.value = ctypes.c_void_p(None).value self._reset() return original + def clear_app_refs(self, gui=None): + """Clear IPython's internal reference to an application instance. + + Whenever we create an app for a user on qt4 or wx, we hold a + reference to the app. This is needed because in some cases bad things + can happen if a user doesn't hold a reference themselves. This + method is provided to clear the references we are holding. + + Parameters + ---------- + gui : None or str + If None, clear all app references. If ('wx', 'qt4') clear + the app for that toolkit. References are not held for gtk or tk + as those toolkits don't have the notion of an app. + """ + if gui is None: + self._apps = {} + elif self._apps.has_key(gui): + del self._apps[gui] + def enable_wx(self, app=False): """Enable event loop integration with wxPython. @@ -81,32 +364,35 @@ class InputHookManager(object): Notes ----- - This methods sets the PyOS_InputHook for wxPython, which allows + This methods sets the ``PyOS_InputHook`` for wxPython, which allows the wxPython to integrate with terminal based applications like IPython. - - Once this has been called, you can use wx interactively by doing:: - - >>> import wx - >>> app = wx.App(redirect=False, clearSigInt=False) - + + If ``app`` is True, we create an :class:`wx.App` as follows:: + + import wx + app = wx.App(redirect=False, clearSigInt=False) + Both options this constructor are important for things to work properly in an interactive context. - - But, *don't start the event loop*. That is handled automatically by - PyOS_InputHook. + + But, we first check to see if an application has already been + created. If so, we simply return that instance. """ from IPython.lib.inputhookwx import inputhook_wx self.set_inputhook(inputhook_wx) - self._current_gui = 'wx' + self._current_gui = GUI_WX if app: import wx - app = wx.App(redirect=False, clearSigInt=False) + app = wx.GetApp() + if app is None: + app = wx.App(redirect=False, clearSigInt=False) + self._apps[GUI_WX] = app return app def disable_wx(self): """Disable event loop integration with wxPython. - + This merely sets PyOS_InputHook to NULL. """ self.clear_inputhook() @@ -121,13 +407,17 @@ class InputHookManager(object): Notes ----- - This methods sets the PyOS_InputHook for wxPython, which allows + This methods sets the PyOS_InputHook for PyQt4, which allows the PyQt4 to integrate with terminal based applications like IPython. - - Once this has been called, you can simply create a QApplication and - use it. But, *don't start the event loop*. That is handled - automatically by PyOS_InputHook. + + If ``app`` is True, we create an :class:`QApplication` as follows:: + + from PyQt4 import QtCore + app = QtGui.QApplication(sys.argv) + + But, we first check to see if an application has already been + created. If so, we simply return that instance. """ from PyQt4 import QtCore # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook @@ -138,15 +428,18 @@ class InputHookManager(object): QtCore.pyqtRestoreInputHook() except AttributeError: pass - self._current_gui = 'qt4' + self._current_gui = GUI_QT4 if app: from PyQt4 import QtGui - app = QtGui.QApplication(sys.argv) + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtGui.QApplication(sys.argv) + self._apps[GUI_QT4] = app return app def disable_qt4(self): """Disable event loop integration with PyQt4. - + This merely sets PyOS_InputHook to NULL. """ self.clear_inputhook() @@ -157,26 +450,24 @@ class InputHookManager(object): Parameters ---------- app : bool - Create a running application object or not. + Create a running application object or not. Because gtk does't + have an app class, this does nothing. Notes ----- This methods sets the PyOS_InputHook for PyGTK, which allows the PyGTK to integrate with terminal based applications like IPython. - - Once this has been called, you can simple create PyGTK objects and - use them. But, *don't start the event loop*. That is handled - automatically by PyOS_InputHook. """ import gtk try: gtk.set_interactive(True) - self._current_gui = 'gtk' + self._current_gui = GUI_GTK except AttributeError: # For older versions of gtk, use our own ctypes version from IPython.lib.inputhookgtk import inputhook_gtk - add_inputhook(inputhook_gtk) + self.set_inputhook(inputhook_gtk) + self._current_gui = GUI_GTK def disable_gtk(self): """Disable event loop integration with PyGTK. @@ -198,7 +489,13 @@ class InputHookManager(object): Currently this is a no-op as creating a :class:`Tkinter.Tk` object sets ``PyOS_InputHook``. """ - self._current_gui = 'tk' + self._current_gui = GUI_TK + if app: + import Tkinter + app = Tkinter.Tk() + app.withdraw() + self._apps[GUI_TK] = app + return app def disable_tk(self): """Disable event loop integration with Tkinter. @@ -223,4 +520,6 @@ enable_tk = inputhook_manager.enable_tk disable_tk = inputhook_manager.disable_tk clear_inputhook = inputhook_manager.clear_inputhook set_inputhook = inputhook_manager.set_inputhook -current_gui = inputhook_manager.current_gui \ No newline at end of file +current_gui = inputhook_manager.current_gui +clear_app_refs = inputhook_manager.clear_app_refs +spin = inputhook_manager.spin diff --git a/IPython/lib/inputhookwx.py b/IPython/lib/inputhookwx.py index 46db247..c908303 100644 --- a/IPython/lib/inputhookwx.py +++ b/IPython/lib/inputhookwx.py @@ -19,6 +19,7 @@ Authors: Robin Dunn, Brian Granger, Ondrej Certik #----------------------------------------------------------------------------- import os +import signal import sys import time from timeit import default_timer as clock @@ -122,6 +123,12 @@ def inputhook_wx3(): if app is not None: assert wx.Thread_IsMain() + # The import of wx on Linux sets the handler for signal.SIGINT + # to 0. This is a bug in wx or gtk. We fix by just setting it + # back to the Python default. + if not callable(signal.getsignal(signal.SIGINT)): + signal.signal(signal.SIGINT, signal.default_int_handler) + evtloop = wx.EventLoop() ea = wx.EventLoopActivator(evtloop) t = clock() @@ -139,7 +146,9 @@ def inputhook_wx3(): # 0.001 13% # 0.005 3% # 0.01 1.5% - # 0.05 0.5% + # 0.05 0.5% + if clock()-t > 1.0: + time.sleep(1.0) if clock()-t > 0.1: # Few GUI events coming in, so we can sleep longer time.sleep(0.05) diff --git a/docs/examples/lib/gui-gtk.py b/docs/examples/lib/gui-gtk.py new file mode 100644 index 0000000..a3eb048 --- /dev/null +++ b/docs/examples/lib/gui-gtk.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +"""Simple GTK example to manually test event loop integration. + +This is meant to run tests manually in ipython as: + +In [5]: %gui gtk + +In [6]: %run gui-gtk.py +""" + + +import pygtk +pygtk.require('2.0') +import gtk + + +def hello_world(wigdet, data=None): + print "Hello World" + +window = gtk.Window(gtk.WINDOW_TOPLEVEL) +button = gtk.Button("Hello World") +button.connect("clicked", hello_world, None) + +window.add(self.button) +button.show() +window.show() + +try: + from IPython.lib.inputhook import appstart_gtk + appstart_gtk() +except ImportError: + gtk.main() + + + diff --git a/docs/examples/lib/gui-mpl.py b/docs/examples/lib/gui-mpl.py new file mode 100644 index 0000000..c77c70a --- /dev/null +++ b/docs/examples/lib/gui-mpl.py @@ -0,0 +1,54 @@ +"""Test the new %gui command. Run this in ipython as + +In [1]: %gui [backend] + +In [2]: %run switchgui [backend] + +where the optional backend can be one of: qt4, gtk, tk, wx. + +Because of subtle difference in how Matplotlib handles the different GUI +toolkits (in things like draw and show), minor modifications to this script +have to be made for Tk to get it to work with the 0.99 and below releases +of Matplotlib. However, in the future, Matplotlib should be able to have +similar logic for all the toolkits, as they are all now using PyOS_InputHook. +""" + +import sys +import time + +from IPython.lib import inputhook + +gui = inputhook.current_gui() +if gui is None: + gui = 'qt4' + inputhook.enable_qt4(app=True) + +backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg') + +import matplotlib +matplotlib.use(backends[gui]) +matplotlib.interactive(True) + +import matplotlib +from matplotlib import pyplot as plt, mlab, pylab +import numpy as np + +from numpy import * +from matplotlib.pyplot import * + +x = np.linspace(0,pi,500) + +print "A plot has been created" +line, = plot(x,sin(2*x)) +inputhook.spin() # This has to be removed for Tk + + +print "Now, we will update the plot..." +print +for i in range(1,51): + print i, + sys.stdout.flush() + line.set_data(x,sin(x*i)) + plt.title('i=%d' % i) + plt.draw() + inputhook.spin() # This has to be removed for Tk diff --git a/docs/examples/lib/gui-qt.py b/docs/examples/lib/gui-qt.py new file mode 100755 index 0000000..4a0b935 --- /dev/null +++ b/docs/examples/lib/gui-qt.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +"""Simple Qt4 example to manually test event loop integration. + +This is meant to run tests manually in ipython as: + +In [5]: %gui qt + +In [6]: %run gui-qt.py + +Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/ +""" + +import sys +from PyQt4 import QtGui, QtCore + +class SimpleWindow(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + + self.setGeometry(300, 300, 200, 80) + self.setWindowTitle('Hello World') + + quit = QtGui.QPushButton('Close', self) + quit.setGeometry(10, 10, 60, 35) + + self.connect(quit, QtCore.SIGNAL('clicked()'), + self, QtCore.SLOT('close()')) + +if __name__ == '__main__': + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtGui.QApplication([]) + + sw = SimpleWindow() + sw.show() + + try: + from IPython import appstart_qt4; appstart_qt4(app) + except ImportError: + app.exec_() diff --git a/docs/examples/lib/gui-tk.py b/docs/examples/lib/gui-tk.py new file mode 100644 index 0000000..f7366aa --- /dev/null +++ b/docs/examples/lib/gui-tk.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +"""Simple Tk example to manually test event loop integration. + +This is meant to run tests manually in ipython as: + +In [5]: %gui tk + +In [6]: %run gui-tk.py +""" + +from Tkinter import * + +class MyApp: + + def __init__(self, root): + frame = Frame(root) + frame.pack() + + self.button = Button(frame, text="Hello", command=self.hello_world) + self.button.pack(side=LEFT) + + def hello_world(self): + print "Hello World!" + +root = Tk() + +app = MyApp(root) + +try: + from IPython import appstart_tk; appstart_tk(root) +except ImportError: + root.mainloop() diff --git a/docs/examples/lib/gui-wx.py b/docs/examples/lib/gui-wx.py new file mode 100644 index 0000000..007d7dc --- /dev/null +++ b/docs/examples/lib/gui-wx.py @@ -0,0 +1,99 @@ +"""A Simple wx example to test IPython's event loop integration. + +To run this do: + +In [5]: %gui wx + +In [6]: %run gui-wx.py + +Ref: Modified from wxPython source code wxPython/samples/simple/simple.py + +This example can only be run once in a given IPython session. +""" + +import wx + + +class MyFrame(wx.Frame): + """ + This is MyFrame. It just shows a few controls on a wxPanel, + and has a simple menu. + """ + def __init__(self, parent, title): + wx.Frame.__init__(self, parent, -1, title, + pos=(150, 150), size=(350, 200)) + + # Create the menubar + menuBar = wx.MenuBar() + + # and a menu + menu = wx.Menu() + + # add an item to the menu, using \tKeyName automatically + # creates an accelerator, the third param is some help text + # that will show up in the statusbar + menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") + + # bind the menu event to an event handler + self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT) + + # and put the menu on the menubar + menuBar.Append(menu, "&File") + self.SetMenuBar(menuBar) + + self.CreateStatusBar() + + # Now create the Panel to put the other controls on. + panel = wx.Panel(self) + + # and a few controls + text = wx.StaticText(panel, -1, "Hello World!") + text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) + text.SetSize(text.GetBestSize()) + btn = wx.Button(panel, -1, "Close") + funbtn = wx.Button(panel, -1, "Just for fun...") + + # bind the button events to handlers + self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn) + self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn) + + # Use a sizer to layout the controls, stacked vertically and with + # a 10 pixel border around each + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(text, 0, wx.ALL, 10) + sizer.Add(btn, 0, wx.ALL, 10) + sizer.Add(funbtn, 0, wx.ALL, 10) + panel.SetSizer(sizer) + panel.Layout() + + + def OnTimeToClose(self, evt): + """Event handler for the button click.""" + print "See ya later!" + self.Close() + + def OnFunButton(self, evt): + """Event handler for the button click.""" + print "Having fun yet?" + + +class MyApp(wx.App): + def OnInit(self): + frame = MyFrame(None, "Simple wxPython App") + self.SetTopWindow(frame) + + print "Print statements go to this stdout window by default." + + frame.Show(True) + return True + +app = wx.GetApp() +if app is None: + app = MyApp(redirect=False, clearSigInt=False) + +try: + from IPython.lib.inputhook import appstart_wx + appstart_wx(app) +except ImportError: + app.MainLoop() + diff --git a/docs/source/interactive/reference.txt b/docs/source/interactive/reference.txt index c50e0cc..2277200 100644 --- a/docs/source/interactive/reference.txt +++ b/docs/source/interactive/reference.txt @@ -1412,6 +1412,9 @@ the form of a library, the capabilities are exposed in library form in the :mod:`IPython.lib.inputhook`. Interested developers should see the module docstrings for more information. +In addition, we also have a number of examples in our source directory +:file:`docs/examples/lib` that demonstrate these capabilities. + .. _matplotlib_support: Plotting with matplotlib