Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Gui Qt example and docs #599

Closed
wants to merge 4 commits into from

4 participants

@takluyver
Owner

Updates to example code and docs - should land before 0.11, but I wanted someone else to give this a once over, because I don't know much about this code.

@takluyver
Owner

N.B. There's now also some unrelated corrections to the docs lumped in with this. I know that's bad practice, but the other changes should be straightforward.

@code-affinity

The import at the top of the example unconditionally uses PyQt4, while IPython itself has been refactored to honor the QT_API setting. Is IPython.external.qt_for_kernel meant for application-level use? If so, it could be used instead of the direct PyQt4 import. If not, a comment above the PyQt4 import might be helpful.

I have tried to run this example three ways:

  1. From ipython qtconsole --gui-qt: The result is, the SimpleWindow is not rendered unless app.exec_() is explicitly invoked at the console, at which point the console stops being interactive until the window is closed.

  2. From ipython --gui-qt: Same result.

  3. From ipython: The SimpleWindow is rendered properly, but only because the except clause was executed, causing app.exec_() to be invoked. Again, the console stops being interactive until the SimpleWindow is closed.

In all of these cases, I don't think that start_event_loop_qt4 has any effect if enable_qt4 has already been invoked. inputhook.enable_qt4 unconditionally sets the _in_event_loop flag to True for the current QApplication instance, even though it never invoked QApplication.exec_(). (See inputhookd.py, line 211.)

Then, guisupport.start_event_loop_qt4 checks this flag, sees that it is already set, and does nothing except set the flag again. (See guisupport.py, line 139.)

Is your installed version of IPython up to date? 2 and 3 work for me (interactive console etc.), although 1 doesn't show the window at all. You're right that start_event_loop_qt4 has no effect, although I'm not sure what is actually starting the event loop.

I'm running directly from github head, installed and invoked as described at http://ipython.scipy.org/moin/Developer_Zone. (I.E. I ran setupegg.py develop, and I'm invoking its ipython.py directly.) Perhaps it's a PyQt4 vs. PySide difference? (I have the latest stable version of PySide installed, as of yesterday, and QT_API is set to "pyside".)

OK, I'm testing with PyQt. At a guess, we're installing the inputhook in pyside (via qt_for_kernel, which looks at QT_API), and the gui-qt.py example is running with PyQt. I'm not sure what the best way to handle this is. We could get external applications to import from qt_for_kernel and fall back to PyQt/Pyside, but that would require further changes for any code already written with the v1 APIs in PyQt (as Pyside only has v2).

The code does work, but the example doesn't take into account the possible combinations of Qt bindings. For now, you'll need to ensure that your code imports the same bindings as set in QT_API - via qt_for_kernel if it's convenient.

Oh, I already discovered the hard way that the example code doesn't run at all unless I change the import from PyQt4 to import from PySide (hence my request for a comment about that, and several noisy messages on the mail list). So I think I've already got that covered. The results that I described in my second comment here were already in the context of using consistent bindings.

Note that import from external.qt_for_kernel throws ImportError for cases 2 or 3. In other words, it seems I can only use it if I'm running from the Qt console. So I modified my copy of the example to directly import from PySide.

If there's any grunt work I could do that would be helpful, let me know. E.G. I could test, either on Windows or Linux, with either PyQt or PySide.

I get the same results as you (almost) if I use PyQt4 instead of PySide.

  1. From ipython qtconsole --gui-qt: The SimpleWindow is not rendered unless app.exec_() is explicitly invoked at the console, at which point the console stops being interactive until the window is closed. You mentioned that you never saw the window at all. I immediately see a window frame, and then after I invoke exec_ I see the complete window.

  2. From ipython --gui-qt: The fully-rendered window immediately appears. The console continues to be interactive, although performance is very bad, and it stays that way even after the SimpleWindow is closed. I guess this is the pyreadline interaction that was warned about.

  3. From ipython: Same result as 2.

So, I concur that the problem must be related to PySide vs. PyQt.

@code-affinity

When using the Qt console, %gui throws a NotImplementedError (zmqshell.py line 387). Unless this is a temporary condition, can we have a note in the documentation explaining that --gui=qt must be used instead when starting the Qt console?

@minrk: Do you know anything about this? Is it simple to make the %gui and %pylab commands work from the Qt console?

Based on the results I got from the gui-qt.py example, I don't know if it is proper for line 1215 to imply that GUIs can be used interactively from the IPython console, at least for Qt. I still don't know enough to be able to tell if this is just because the example is still not quite right, or if this is a limitation of the current QT GUI support, or maybe I'm just plain wrong. If this is a temporary limitation of the current code base, could the documentation include a warning message that this is the case? (Such as the warning at the top of the "IPython as a System Shell" section.)

Later in this documentation section, there is a code snippet (wx-specific) that includes a call to enable_wx. Based on your changes to the gui-qt.py example, it appears that at least in the case of Qt, an additional call to start_event_loop_qt4 is also required there. I'm not sure if that is right, but as it stands the example and the documentation are not consistent.

I appreciate the time you have been taking to look at these issues. I do not want to come across as critical.

@fperez
Owner

Closing because I've merged it into #620, where further discussion/work is taking place until we sort this out completely. Thanks!

@fperez fperez closed this
@minrk minrk added this to the 0.11 milestone
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 31 additions and 65 deletions.
  1. +2 −1  docs/examples/lib/gui-qt.py
  2. +29 −64 docs/source/interactive/reference.txt
View
3  docs/examples/lib/gui-qt.py
@@ -35,6 +35,7 @@ def __init__(self, parent=None):
sw.show()
try:
- from IPython import enable_qt4; enable_qt4(app)
+ from IPython.lib.inputhook import enable_qt4
+ enable_qt4()
except ImportError:
app.exec_()
View
93 docs/source/interactive/reference.txt
@@ -604,7 +604,7 @@ Session logging and restoring
-----------------------------
You can log all input from a session either by starting IPython with the
-command line switche ``logfile=foo.py`` (see :ref:`here <command_line_options>`)
+command line switch ``--logfile=foo.py`` (see :ref:`here <command_line_options>`)
or by activating the logging at any moment with the magic function %logstart.
Log files can later be reloaded by running them as scripts and IPython
@@ -648,7 +648,7 @@ System shell access
Any input line beginning with a ! character is passed verbatim (minus
the !, of course) to the underlying operating system. For example,
-typing !ls will run 'ls' in the current directory.
+typing ``!ls`` will run 'ls' in the current directory.
Manual capture of command output
--------------------------------
@@ -702,9 +702,9 @@ The %alias magic function and the alias option in the ipythonrc
configuration file allow you to define magic functions which are in fact
system shell commands. These aliases can have parameters.
-'%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
+``%alias alias_name cmd`` defines 'alias_name' as an alias for 'cmd'
-Then, typing '%alias_name params' will execute the system command 'cmd
+Then, typing ``%alias_name params`` will execute the system command 'cmd
params' (from your underlying operating system).
You can also define aliases with parameters using %s specifiers (one per
@@ -722,9 +722,8 @@ replaced by a positional parameter to the call to %parts::
If called with no parameters, %alias prints the table of currently
defined aliases.
-The %rehash/rehashx magics allow you to load your entire $PATH as
-ipython aliases. See their respective docstrings (or sec. 6.2
-<#sec:magic> for further details).
+The %rehashx magic allows you to load your entire $PATH as
+ipython aliases. See its docstring for further details.
.. _dreload:
@@ -765,15 +764,14 @@ addition to the %rep magic command that brings a history entry
up for editing on the next command line.
The following GLOBAL variables always exist (so don't overwrite them!):
-_i: stores previous input. _ii: next previous. _iii: next-next previous.
-_ih : a list of all input _ih[n] is the input from line n and this list
-is aliased to the global variable In. If you overwrite In with a
-variable of your own, you can remake the assignment to the internal list
-with a simple 'In=_ih'.
+
+* _i, _ii, _iii: store previous, next previous and next-next previous inputs.
+* In, _ih : a list of all inputs; _ih[n] is the input from line n. If you
+ overwrite In with a variable of your own, you can remake the assignment to the
+ internal list with a simple ``In=_ih``.
Additionally, global variables named _i<n> are dynamically created (<n>
-being the prompt counter), such that
-_i<n> == _ih[<n>] == In[<n>].
+being the prompt counter), so ``_i<n> == _ih[<n>] == In[<n>]``.
For example, what you typed at prompt 14 is available as _i14, _ih[14]
and In[14].
@@ -781,55 +779,23 @@ and In[14].
This allows you to easily cut and paste multi line interactive prompts
by printing them out: they print like a clean string, without prompt
characters. You can also manipulate them like regular variables (they
-are strings), modify or exec them (typing 'exec _i9' will re-execute the
-contents of input prompt 9, 'exec In[9:14]+In[18]' will re-execute lines
-9 through 13 and line 18).
+are strings), modify or exec them (typing ``exec _i9`` will re-execute the
+contents of input prompt 9.
You can also re-execute multiple lines of input easily by using the
magic %macro function (which automates the process and allows
re-execution without having to type 'exec' every time). The macro system
also allows you to re-execute previous lines which include magic
-function calls (which require special processing). Type %macro? or see
-sec. 6.2 <#sec:magic> for more details on the macro system.
+function calls (which require special processing). Type %macro? for more details
+on the macro system.
A history function %hist allows you to see any part of your input
history by printing a range of the _i variables.
You can also search ('grep') through your history by typing
-'%hist -g somestring'. This also searches through the so called *shadow history*,
-which remembers all the commands (apart from multiline code blocks)
-you have ever entered. Handy for searching for svn/bzr URL's, IP adrresses
-etc. You can bring shadow history entries listed by '%hist -g' up for editing
-(or re-execution by just pressing ENTER) with %rep command. Shadow history
-entries are not available as _iNUMBER variables, and they are identified by
-the '0' prefix in %hist -g output. That is, history entry 12 is a normal
-history entry, but 0231 is a shadow history entry.
-
-Shadow history was added because the readline history is inherently very
-unsafe - if you have multiple IPython sessions open, the last session
-to close will overwrite the history of previountly closed session. Likewise,
-if a crash occurs, history is never saved, whereas shadow history entries
-are added after entering every command (so a command executed
-in another IPython session is immediately available in other IPython
-sessions that are open).
-
-To conserve space, a command can exist in shadow history only once - it doesn't
-make sense to store a common line like "cd .." a thousand times. The idea is
-mainly to provide a reliable place where valuable, hard-to-remember commands can
-always be retrieved, as opposed to providing an exact sequence of commands
-you have entered in actual order.
-
-Because shadow history has all the commands you have ever executed,
-time taken by %hist -g will increase oven time. If it ever starts to take
-too long (or it ends up containing sensitive information like passwords),
-clear the shadow history by `%clear shadow_nuke`.
-
-Time taken to add entries to shadow history should be negligible, but
-in any case, if you start noticing performance degradation after using
-IPython for a long time (or running a script that floods the shadow history!),
-you can 'compress' the shadow history by executing
-`%clear shadow_compress`. In practice, this should never be necessary
-in normal use.
+``%hist -g somestring``. This is handy for searching for URLs, IP addresses,
+etc. You can bring history entries listed by '%hist -g' up for editing
+with the %recall command, or run them immediately with %rerun.
.. _output_caching:
@@ -874,7 +840,7 @@ Directory history
Your history of visited directories is kept in the global list _dh, and
the magic %cd command can be used to go to any entry in that list. The
-%dhist command allows you to view this history. Do ``cd -<TAB`` to
+%dhist command allows you to view this history. Do ``cd -<TAB>`` to
conveniently view the directory history.
@@ -1186,8 +1152,8 @@ import IPython.extensions.PhysicalQInput
.. _gui_support:
-GUI event loop support support
-==============================
+GUI event loop support
+======================
.. versionadded:: 0.11
The ``%gui`` magic and :mod:`IPython.lib.inputhook`.
@@ -1207,16 +1173,15 @@ advantages of this are:
For users, enabling GUI event loop integration is simple. You simple use the
``%gui`` magic as follows::
- %gui [-a] [GUINAME]
+ %gui [GUINAME]
With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
-arguments are ``wx``, ``qt4``, ``gtk`` and ``tk``. The ``-a`` option will
-create and return a running application object for the selected GUI toolkit.
+arguments are ``wx``, ``qt4``, ``gtk`` and ``tk``.
Thus, to use wxPython interactively and create a running :class:`wx.App`
object, do::
- %gui -a wx
+ %gui wx
For information on IPython's Matplotlib integration (and the ``pylab`` mode)
see :ref:`this section <matplotlib_support>`.
@@ -1261,7 +1226,7 @@ PyQt and PySide
.. attempt at explanation of the complete mess that is Qt support
-When you use ``gui=qt`` or ``pylab=qt``, IPython can work with either
+When you use ``--gui=qt`` or ``--pylab=qt``, IPython can work with either
PyQt4 or PySide. There are three options for configuration here, because
PyQt4 has two APIs for QString and QVariant - v1, which is the default on
Python 2, and the more natural v2, which is the only API supported by PySide.
@@ -1279,7 +1244,7 @@ PyQt4 to use its v2 API. So if ``QT_API=pyside`` PySide will be used,
and if ``QT_API=pyqt`` then PyQt4 will be used *with the v2 API* for
QString and QVariant, so ETS codes like MayaVi will also work with IPython.
-If you launch IPython in pylab mode with ``ipython pylab=qt``, then IPython
+If you launch IPython in pylab mode with ``ipython --pylab=qt``, then IPython
will ask matplotlib which Qt library to use (only if QT_API is *not set*),
via the 'backend.qt4' rcParam.
If matplotlib is version 1.0.1 or older, then IPython will always use PyQt4
@@ -1292,7 +1257,7 @@ without setting the v2 APIs, since neither v2 PyQt nor PySide work.
an incompatible mode.
It also means that you must *not* have ``QT_API`` set if you want to
- use ``gui=qt`` with code that requires PyQt4 API v1.
+ use ``--gui=qt`` with code that requires PyQt4 API v1.
@@ -1314,7 +1279,7 @@ process of working with the Matplotlib developers to finalize the new pylab
API, but for now you can use Matplotlib interactively using the following
commands::
- %gui -a wx
+ %gui wx
import matplotlib
matplotlib.use('wxagg')
from matplotlib import pylab
Something went wrong with that request. Please try again.