Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Pyside support #259

Merged
6 commits merged into from

3 participants

@epatters

Adds a Qt bindings switcher to allow PyQt and PySide support. Because PySide only supports the new-style PyQt string API, a number of changes had to be made. Going forward, however, it should be easy to maintain support for both sets of bindings: just remember that implicit QString -> unicode, QByteArray -> str conversion is being performed. Incidentally, this should ease the Python 3 conversion, since PyQt in Python 3 only supports the new-style string API.

Note that even the most recent PySide beta 5 release will segfault sporadically. This is fixed in the PySide master branch and IPython will work with the upcoming RC 1 release.

@takluyver
Owner

Thanks, that will also help the Python 3 port, where the new string API is the default. I'd already made some of the relevant changes, but I suspect you've done a more thorough job than I did.

@fperez
Owner

Thanks for this work, Evan!

I merged and tested, and the PyQt part seems OK. But when I try to run with pyside, I get:

uqbar[~]> QT_API='pyside' iqlab
Traceback (most recent call last):
  File "/home/fperez/usr/bin/ipython-qtconsole", line 4, in 
    from IPython.frontend.qt.console.ipythonqt import main
  File "/home/fperez/usr/lib/python2.6/site-packages/IPython/frontend/qt/console/ipythonqt.py", line 18, in 
    from IPython.frontend.qt.kernelmanager import QtKernelManager
  File "/home/fperez/usr/lib/python2.6/site-packages/IPython/frontend/qt/kernelmanager.py", line 87, in 
    class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):
  File "/home/fperez/usr/lib/python2.6/site-packages/IPython/frontend/qt/kernelmanager.py", line 105, in QtSubSocketChannel
    display_data_received = QtCore.pyqtSignal(object)
AttributeError: 'module' object has no attribute 'pyqtSignal'

Note that I'm using pyside binaries indicated here: http://developer.qt.nokia.com/wiki/PySide_Binaries_Linux for Ubuntu 10.10. Do you think it's just that these bindings are incomplete, or is there an issue on your side of the code?

@epatters

It looks the 'display_data_received' signal was added in the master branch after I started my 'pyside-support' branch, so I didn't fix it.

Compare this:

https://github.com/ipython/ipython/blob/master/IPython/frontend/qt/kernelmanager.py#L87

with this:

https://github.com/epatters/ipython/blob/pyside-support/IPython/frontend/qt/kernelmanager.py#L87

We need only replace QtCore.pyqtSignal with QtCore.Signal to fix this. Hopefully there are not too many other things to patch because of the time lag.

If you want, I can merge with the master branch to make sure this is right. But Brian suggested on the mailing list that I refrain from doing that.

@fperez
Owner

To keep the history a little cleaner and to be able to better see the work that's only new on this branch, doing a rebase against master would perhaps be the best option. Do you feel comfortable doing a rebase?

If you're OK rebasing against master, go ahead and do that; otherwise go ahead and merge master into your branch, it's not that bad.

@epatters

I'll give the rebase a shot first. It's time I learn how to do that anyway.

Evan Patterson and others added some commits
Evan Patterson Paved the way for PySide support.
Created a Qt API switcher that imports PyQt4 or PySide as appropriate. If PyQt4 is loaded, the new-style QString API is used. This facillates both Python 3 and PySide compatibility.
3cc304d
Evan Patterson More PySide compatibility fixes. d42ebb7
epatters Clean up in Qt API switcher. ff3c3c3
epatters Yet more PySide compatibility fixes. 0dc37d3
@epatters

OK, should be good to go. Let me know if you encounter further problems, Fernando.

@fperez
Owner

Hi Evan,

thanks, the new code causes no conflicts. But as soon as I tried running it, I opened a console and hit up arrow, and the window disappeared, with this message left on my terminal:

Segmentation fault
(epatters-pyside-support)uqbar[ipython]> Killed by parent poller!

Do you think this is due to the binary packages I'm using or a problem on your side?

Also, I would improve this message:

RuntimeError: Invalid Qt API "foo"

to read something like:

RuntimeError: Invalid Qt API "foo", valid values are: "pyqt" or "pyside"

Thanks for working on this, it looks like we're close, we just need to sort out where the crashes are coming from (I'm willing to install fresher packages for pyside if you think that's the issue).

@epatters

That is indeed the issue. To get this working, I had to submit several bugs to the PySide team (which is why it took so long to get this pull request together). The upshot is that until the next beta/RC is released, you need to build PySide from the master branch. Fortunately, there are some Linux build scripts that make this very easy.

I'll do something about that message.

@fperez
Owner

OK, I built all of pyside from master (that took a while :) and now I don't see any segfaults. But trying to display something shows a problem:

In [2]: plot(rand(100))
Out[2]: []

In [3]: display(*getfigs())

# NOTE: nothing shows up here where the figure should be
In [3]: 

In [4]: 

In addition to no figure being displayed, this error appears on the console:

Error calling slot "_dispatch" 
Traceback (most recent call last):
  File "/home/fperez/ipython/ipython/IPython/frontend/qt/base_frontend_mixin.py", line 102, in _dispatch
    handler(msg)
  File "/home/fperez/ipython/ipython/IPython/frontend/qt/console/rich_ipython_widget.py", line 105, in _handle_display_data
    self._append_svg(svg)
  File "/home/fperez/ipython/ipython/IPython/frontend/qt/console/rich_ipython_widget.py", line 144, in _append_svg
    image = svg_to_image(svg)
  File "/home/fperez/ipython/ipython/IPython/frontend/qt/svg.py", line 71, in svg_to_image
    renderer = QtSvg.QSvgRenderer(QtCore.QByteArray(string))
TypeError: 'PySide.QtCore.QByteArray' called with wrong argument types:
  PySide.QtCore.QByteArray(unicode)
Supported signatures:
  PySide.QtCore.QByteArray()
  PySide.QtCore.QByteArray(PySide.QtCore.QByteArray)
  PySide.QtCore.QByteArray(str)
  PySide.QtCore.QByteArray(int, char)

I can test again once you sort this out, though I'm afraid I can't offer much help myself beyond testing :)

@takluyver
Owner

It looks like you're getting unicode from the json message, and Qt's XML reader expects an 8 bit string. svg_to_image(svg.encode("utf-8")) should resolve that.

@epatters

Oddly, I could not reproduce Fernando's problem (my strings were always ASCII, not unicode). But in any case it should be fixed.

I hope this is the last of these incompatibility issues, although it probably isn't :)

@fperez
Owner

Great! No other issues that I can see from this side, so go for the merge. Many thanks!

@damianavila damianavila referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@damianavila damianavila referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@damianavila damianavila referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 9, 2011
  1. Paved the way for PySide support.

    Evan Patterson authored epatters committed
    Created a Qt API switcher that imports PyQt4 or PySide as appropriate. If PyQt4 is loaded, the new-style QString API is used. This facillates both Python 3 and PySide compatibility.
  2. More PySide compatibility fixes.

    Evan Patterson authored
  3. Clean up in Qt API switcher.

    epatters authored
  4. Yet more PySide compatibility fixes.

    epatters authored
Commits on Feb 15, 2011
  1. Improved error message for Qt API switcher.

    epatters authored
This page is out of date. Refresh to see the latest.
View
30 IPython/external/qt.py
@@ -0,0 +1,30 @@
+""" A Qt API selector that can be used to switch between PyQt and PySide.
+"""
+
+import os
+
+# Available APIs.
+QT_API_PYQT = 'pyqt'
+QT_API_PYSIDE = 'pyside'
+
+# Use PyQt by default until PySide is stable.
+QT_API = os.environ.get('QT_API', QT_API_PYQT)
+
+if QT_API == QT_API_PYQT:
+ # For PySide compatibility, use the new string API that automatically
+ # converts QStrings to Unicode Python strings.
+ import sip
+ sip.setapi('QString', 2)
+
+ from PyQt4 import QtCore, QtGui, QtSvg
+
+ # Alias PyQt-specific functions for PySide compatibility.
+ QtCore.Signal = QtCore.pyqtSignal
+ QtCore.Slot = QtCore.pyqtSlot
+
+elif QT_API == QT_API_PYSIDE:
+ from PySide import QtCore, QtGui, QtSvg
+
+else:
+ raise RuntimeError('Invalid Qt API %r, valid values are: %r or %r' %
+ (QT_API, QT_API_PYQT, QT_API_PYSIDE))
View
2  IPython/frontend/qt/console/ansi_code_processor.py
@@ -9,7 +9,7 @@
import re
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
#-----------------------------------------------------------------------------
# Constants and datatypes
View
9 IPython/frontend/qt/console/bracket_matcher.py
@@ -2,7 +2,7 @@
"""
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
class BracketMatcher(QtCore.QObject):
@@ -42,8 +42,7 @@ def _find_match(self, position):
"""
# Decide what character to search for and what direction to search in.
document = self._text_edit.document()
- qchar = document.characterAt(position)
- start_char = qchar.toAscii()
+ start_char = document.characterAt(position)
search_char = self._opening_map.get(start_char)
if search_char:
increment = 1
@@ -55,9 +54,9 @@ def _find_match(self, position):
return -1
# Search for the character.
+ char = start_char
depth = 0
while position >= 0 and position < document.characterCount():
- char = qchar.toAscii()
if char == start_char:
depth += 1
elif char == search_char:
@@ -65,7 +64,7 @@ def _find_match(self, position):
if depth == 0:
break
position += increment
- qchar = document.characterAt(position)
+ char = document.characterAt(position)
else:
position = -1
return position
View
18 IPython/frontend/qt/console/call_tip_widget.py
@@ -1,9 +1,10 @@
# Standard library imports
import re
from textwrap import dedent
+from unicodedata import category
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
class CallTipWidget(QtGui.QLabel):
@@ -35,7 +36,7 @@ def __init__(self, text_edit):
self.setMargin(1 + self.style().pixelMetric(
QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self))
self.setWindowOpacity(self.style().styleHint(
- QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self) / 255.0)
+ QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self, None) / 255.0)
def eventFilter(self, obj, event):
""" Reimplemented to hide on certain key presses and on text edit focus
@@ -99,7 +100,7 @@ def paintEvent(self, event):
"""
painter = QtGui.QStylePainter(self)
option = QtGui.QStyleOptionFrame()
- option.init(self)
+ option.initFrom(self)
painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option)
painter.end()
@@ -184,11 +185,10 @@ def _find_parenthesis(self, position, forward=True):
"""
commas = depth = 0
document = self._text_edit.document()
- qchar = document.characterAt(position)
- while (position > 0 and qchar.isPrint() and
- # Need to check explicitly for line/paragraph separators:
- qchar.unicode() not in (0x2028, 0x2029)):
- char = qchar.toAscii()
+ char = document.characterAt(position)
+ # Search until a match is found or a non-printable character is
+ # encountered.
+ while category(char) != 'Cc' and position > 0:
if char == ',' and depth == 0:
commas += 1
elif char == ')':
@@ -200,7 +200,7 @@ def _find_parenthesis(self, position, forward=True):
break
depth -= 1
position += 1 if forward else -1
- qchar = document.characterAt(position)
+ char = document.characterAt(position)
else:
position = -1
return position, commas
View
2  IPython/frontend/qt/console/completion_widget.py
@@ -1,5 +1,5 @@
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
class CompletionWidget(QtGui.QListWidget):
View
53 IPython/frontend/qt/console/console_widget.py
@@ -5,14 +5,15 @@
#-----------------------------------------------------------------------------
# Standard library imports
+import os
from os.path import commonprefix
import re
-import os
import sys
from textwrap import dedent
+from unicodedata import category
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
# Local imports
from IPython.config.configurable import Configurable
@@ -22,6 +23,16 @@
from completion_widget import CompletionWidget
#-----------------------------------------------------------------------------
+# Functions
+#-----------------------------------------------------------------------------
+
+def is_letter_or_number(char):
+ """ Returns whether the specified unicode character is a letter or a number.
+ """
+ cat = category(char)
+ return cat.startswith('L') or cat.startswith('N')
+
+#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
@@ -78,16 +89,16 @@ class ConsoleWidget(Configurable, QtGui.QWidget):
#------ Signals ------------------------------------------------------------
# Signals that indicate ConsoleWidget state.
- copy_available = QtCore.pyqtSignal(bool)
- redo_available = QtCore.pyqtSignal(bool)
- undo_available = QtCore.pyqtSignal(bool)
+ copy_available = QtCore.Signal(bool)
+ redo_available = QtCore.Signal(bool)
+ undo_available = QtCore.Signal(bool)
# Signal emitted when paging is needed and the paging style has been
# specified as 'custom'.
- custom_page_requested = QtCore.pyqtSignal(object)
+ custom_page_requested = QtCore.Signal(object)
# Signal emitted when the font is changed.
- font_changed = QtCore.pyqtSignal(QtGui.QFont)
+ font_changed = QtCore.Signal(QtGui.QFont)
#------ Protected class variables ------------------------------------------
@@ -267,7 +278,7 @@ def eventFilter(self, obj, event):
elif etype == QtCore.QEvent.Drop and obj == self._control.viewport():
cursor = self._control.cursorForPosition(event.pos())
if self._in_buffer(cursor.position()):
- text = unicode(event.mimeData().text())
+ text = event.mimeData().text()
self._insert_plain_text_into_buffer(cursor, text)
# Qt is expecting to get something here--drag and drop occurs in its
@@ -328,7 +339,7 @@ def can_paste(self):
""" Returns whether text can be pasted from the clipboard.
"""
if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
- return not QtGui.QApplication.clipboard().text().isEmpty()
+ return bool(QtGui.QApplication.clipboard().text())
return False
def can_export(self):
@@ -469,7 +480,7 @@ def _get_input_buffer(self):
cursor = self._get_end_cursor()
cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
- input_buffer = unicode(cursor.selection().toPlainText())
+ input_buffer = cursor.selection().toPlainText()
# Strip out continuation prompts.
return input_buffer.replace('\n' + self._continuation_prompt, '\n')
@@ -532,7 +543,7 @@ def paste(self, mode=QtGui.QClipboard.Clipboard):
# Remove any trailing newline, which confuses the GUI and forces the
# user to backspace.
- text = unicode(QtGui.QApplication.clipboard().text(mode)).rstrip()
+ text = QtGui.QApplication.clipboard().text(mode).rstrip()
self._insert_plain_text_into_buffer(cursor, dedent(text))
def print_(self, printer = None):
@@ -895,7 +906,7 @@ def _clear_temporary_buffer(self):
while cursor.movePosition(QtGui.QTextCursor.NextBlock):
temp_cursor = QtGui.QTextCursor(cursor)
temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
- text = unicode(temp_cursor.selection().toPlainText()).lstrip()
+ text = temp_cursor.selection().toPlainText().lstrip()
if not text.startswith(prompt):
break
else:
@@ -1088,7 +1099,7 @@ def _event_filter_console_keypress(self, event):
elif not self._executing:
cursor.movePosition(QtGui.QTextCursor.End,
QtGui.QTextCursor.KeepAnchor)
- at_end = cursor.selectedText().trimmed().isEmpty()
+ at_end = len(cursor.selectedText().strip()) == 0
single_line = (self._get_end_cursor().blockNumber() ==
self._get_prompt_cursor().blockNumber())
if (at_end or shift_down or single_line) and not ctrl_down:
@@ -1430,7 +1441,7 @@ def _get_block_plain_text(self, block):
cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
QtGui.QTextCursor.KeepAnchor)
- return unicode(cursor.selection().toPlainText())
+ return cursor.selection().toPlainText()
def _get_cursor(self):
""" Convenience method that returns a cursor for the current position.
@@ -1506,10 +1517,10 @@ def _get_word_start_cursor(self, position):
document = self._control.document()
position -= 1
while position >= self._prompt_pos and \
- not document.characterAt(position).isLetterOrNumber():
+ not is_letter_or_number(document.characterAt(position)):
position -= 1
while position >= self._prompt_pos and \
- document.characterAt(position).isLetterOrNumber():
+ is_letter_or_number(document.characterAt(position)):
position -= 1
cursor = self._control.textCursor()
cursor.setPosition(position + 1)
@@ -1523,10 +1534,10 @@ def _get_word_end_cursor(self, position):
document = self._control.document()
end = self._get_end_cursor().position()
while position < end and \
- not document.characterAt(position).isLetterOrNumber():
+ not is_letter_or_number(document.characterAt(position)):
position += 1
while position < end and \
- document.characterAt(position).isLetterOrNumber():
+ is_letter_or_number(document.characterAt(position)):
position += 1
cursor = self._control.textCursor()
cursor.setPosition(position)
@@ -1573,7 +1584,7 @@ def _insert_html_fetching_plain_text(self, cursor, html):
self._insert_html(cursor, html)
end = cursor.position()
cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
- text = unicode(cursor.selection().toPlainText())
+ text = cursor.selection().toPlainText()
cursor.setPosition(end)
cursor.endEditBlock()
@@ -1614,7 +1625,7 @@ def _insert_plain_text_into_buffer(self, cursor, text):
must be in the input buffer), ensuring that continuation prompts are
inserted as necessary.
"""
- lines = unicode(text).splitlines(True)
+ lines = text.splitlines(True)
if lines:
cursor.beginEditBlock()
cursor.insertText(lines[0])
@@ -1821,7 +1832,7 @@ def _show_prompt(self, prompt=None, html=False, newline=True):
if cursor.position() > 0:
cursor.movePosition(QtGui.QTextCursor.Left,
QtGui.QTextCursor.KeepAnchor)
- if unicode(cursor.selection().toPlainText()) != '\n':
+ if cursor.selection().toPlainText() != '\n':
self._append_plain_text('\n')
# Write the prompt.
View
26 IPython/frontend/qt/console/frontend_widget.py
@@ -7,7 +7,7 @@
# System library imports
from pygments.lexers import PythonLexer
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
# Local imports
from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
@@ -32,13 +32,13 @@ def __init__(self, frontend):
self._frontend = frontend
self.highlighting_on = False
- def highlightBlock(self, qstring):
+ def highlightBlock(self, string):
""" Highlight a block of text. Reimplemented to highlight selectively.
"""
if not self.highlighting_on:
return
- # The input to this function is unicode string that may contain
+ # The input to this function is a unicode string that may contain
# paragraph break characters, non-breaking spaces, etc. Here we acquire
# the string as plain text so we can compare it.
current_block = self.currentBlock()
@@ -53,11 +53,11 @@ def highlightBlock(self, qstring):
# Don't highlight the part of the string that contains the prompt.
if string.startswith(prompt):
self._current_offset = len(prompt)
- qstring.remove(0, len(prompt))
+ string = string[len(prompt):]
else:
self._current_offset = 0
- PygmentsHighlighter.highlightBlock(self, qstring)
+ PygmentsHighlighter.highlightBlock(self, string)
def rehighlightBlock(self, block):
""" Reimplemented to temporarily enable highlighting if disabled.
@@ -81,20 +81,20 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
# An option and corresponding signal for overriding the default kernel
# interrupt behavior.
custom_interrupt = Bool(False)
- custom_interrupt_requested = QtCore.pyqtSignal()
+ custom_interrupt_requested = QtCore.Signal()
# An option and corresponding signals for overriding the default kernel
# restart behavior.
custom_restart = Bool(False)
- custom_restart_kernel_died = QtCore.pyqtSignal(float)
- custom_restart_requested = QtCore.pyqtSignal()
+ custom_restart_kernel_died = QtCore.Signal(float)
+ custom_restart_requested = QtCore.Signal()
# Emitted when an 'execute_reply' has been received from the kernel and
# processed by the FrontendWidget.
- executed = QtCore.pyqtSignal(object)
+ executed = QtCore.Signal(object)
# Emitted when an exit request has been received from the kernel.
- exit_requested = QtCore.pyqtSignal()
+ exit_requested = QtCore.Signal()
# Protected class variables.
_CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
@@ -153,7 +153,7 @@ def __init__(self, *args, **kw):
def copy(self):
""" Copy the currently selected text to the clipboard, removing prompts.
"""
- text = unicode(self._control.textCursor().selection().toPlainText())
+ text = self._control.textCursor().selection().toPlainText()
if text:
lines = map(transform_classic_prompt, text.splitlines())
text = '\n'.join(lines)
@@ -491,7 +491,7 @@ def _call_tip(self):
# Decide if it makes sense to show a call tip
cursor = self._get_cursor()
cursor.movePosition(QtGui.QTextCursor.Left)
- if cursor.document().characterAt(cursor.position()).toAscii() != '(':
+ if cursor.document().characterAt(cursor.position()) != '(':
return False
context = self._get_context(cursor)
if not context:
@@ -534,7 +534,7 @@ def _get_context(self, cursor=None):
cursor = self._get_cursor()
cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
QtGui.QTextCursor.KeepAnchor)
- text = unicode(cursor.selection().toPlainText())
+ text = cursor.selection().toPlainText()
return self._completion_lexer.get_context(text)
def _process_execute_abort(self, msg):
View
2  IPython/frontend/qt/console/history_console_widget.py
@@ -1,5 +1,5 @@
# System library imports
-from PyQt4 import QtGui
+from IPython.external.qt import QtGui
# Local imports
from console_widget import ConsoleWidget
View
16 IPython/frontend/qt/console/ipython_widget.py
@@ -1,9 +1,5 @@
""" A FrontendWidget that emulates the interface of the console IPython and
supports the additional functionality provided by the IPython kernel.
-
- TODO: Add support for retrieving the system default editor. Requires code
- paths for Windows (use the registry), Mac OS (use LaunchServices), and
- Linux (use the xdg system).
"""
#-----------------------------------------------------------------------------
@@ -17,7 +13,7 @@
from textwrap import dedent
# System library imports
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
# Local imports
from IPython.core.inputsplitter import IPythonInputSplitter, \
@@ -56,7 +52,7 @@ class IPythonWidget(FrontendWidget):
# an editor is needed for a file. This overrides 'editor' and 'editor_line'
# settings.
custom_edit = Bool(False)
- custom_edit_requested = QtCore.pyqtSignal(object, object)
+ custom_edit_requested = QtCore.Signal(object, object)
# A command for invoking a system text editor. If the string contains a
# {filename} format specifier, it will be used. Otherwise, the filename will
@@ -227,7 +223,7 @@ def copy(self):
""" Copy the currently selected text to the clipboard, removing prompts
if possible.
"""
- text = unicode(self._control.textCursor().selection().toPlainText())
+ text = self._control.textCursor().selection().toPlainText()
if text:
lines = map(transform_ipy_prompt, text.splitlines())
text = '\n'.join(lines)
@@ -325,7 +321,7 @@ def _show_interpreter_prompt(self, number=None):
# Load code from the %loadpy magic, if necessary.
if self._code_to_load is not None:
- self.input_buffer = dedent(unicode(self._code_to_load).rstrip())
+ self.input_buffer = dedent(self._code_to_load.rstrip())
self._code_to_load = None
def _show_interpreter_prompt_for_reply(self, msg):
@@ -339,7 +335,7 @@ def _show_interpreter_prompt_for_reply(self, msg):
block = self._previous_prompt_obj.block
# Make sure the prompt block has not been erased.
- if block.isValid() and not block.text().isEmpty():
+ if block.isValid() and block.text():
# Remove the old prompt and insert a new prompt.
cursor = QtGui.QTextCursor(block)
@@ -484,7 +480,7 @@ def _style_sheet_changed(self):
if self._page_control:
self._page_control.document().setDefaultStyleSheet(self.style_sheet)
- bg_color = self._control.palette().background().color()
+ bg_color = self._control.palette().window().color()
self._ansi_processor.set_background_color(bg_color)
def _syntax_style_changed(self):
View
6 IPython/frontend/qt/console/ipythonqt.py
@@ -6,8 +6,9 @@
#-----------------------------------------------------------------------------
# Systemm library imports
-from PyQt4 import QtGui
+from IPython.external.qt import QtGui
from pygments.styles import get_all_styles
+
# Local imports
from IPython.external.argparse import ArgumentParser
from IPython.frontend.qt.console.frontend_widget import FrontendWidget
@@ -82,7 +83,8 @@ def closeEvent(self, event):
justthis.setShortcut('N')
closeall = QtGui.QPushButton("&Yes, quit everything", self)
closeall.setShortcut('Y')
- box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg)
+ box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
+ title, msg)
box.setInformativeText(info)
box.addButton(cancel)
box.addButton(justthis, QtGui.QMessageBox.NoRole)
View
8 IPython/frontend/qt/console/pygments_highlighter.py
@@ -1,5 +1,5 @@
# System library imports.
-from PyQt4 import QtGui
+from IPython.external.qt import QtGui
from pygments.formatters.html import HtmlFormatter
from pygments.lexer import RegexLexer, _TokenType, Text, Error
from pygments.lexers import PythonLexer
@@ -99,12 +99,10 @@ def __init__(self, parent, lexer=None):
self._lexer = lexer if lexer else PythonLexer()
self.set_style('default')
- def highlightBlock(self, qstring):
+ def highlightBlock(self, string):
""" Highlight a block of text.
"""
- qstring = unicode(qstring)
prev_data = self.currentBlock().previous().userData()
-
if prev_data is not None:
self._lexer._saved_state_stack = prev_data.syntax_stack
elif hasattr(self._lexer, '_saved_state_stack'):
@@ -112,7 +110,7 @@ def highlightBlock(self, qstring):
# Lex the text using Pygments
index = 0
- for token, text in self._lexer.get_tokens(qstring):
+ for token, text in self._lexer.get_tokens(string):
length = len(text)
self.setFormat(index, length, self._get_format(token))
index += length
View
18 IPython/frontend/qt/console/rich_ipython_widget.py
@@ -1,8 +1,10 @@
-# System library imports
+# Standard libary imports.
+from base64 import decodestring
import os
import re
-from base64 import decodestring
-from PyQt4 import QtCore, QtGui
+
+# System libary imports.
+from IPython.external.qt import QtCore, QtGui
# Local imports
from IPython.frontend.qt.svg import save_svg, svg_to_clipboard, svg_to_image
@@ -41,9 +43,7 @@ def _context_menu_make(self, pos):
"""
format = self._control.cursorForPosition(pos).charFormat()
name = format.stringProperty(QtGui.QTextFormat.ImageName)
- if name.isEmpty():
- menu = super(RichIPythonWidget, self)._context_menu_make(pos)
- else:
+ if name:
menu = QtGui.QMenu()
menu.addAction('Copy Image', lambda: self._copy_image(name))
@@ -51,11 +51,13 @@ def _context_menu_make(self, pos):
menu.addSeparator()
svg = format.stringProperty(self._svg_text_format_property)
- if not svg.isEmpty():
+ if svg:
menu.addSeparator()
menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg))
menu.addAction('Save SVG As...',
lambda: save_svg(svg, self._control))
+ else:
+ menu = super(RichIPythonWidget, self)._context_menu_make(pos)
return menu
#---------------------------------------------------------------------------
@@ -171,7 +173,7 @@ def _add_image(self, image):
QTextImageFormat that references it.
"""
document = self._control.document()
- name = QtCore.QString.number(image.cacheKey())
+ name = str(image.cacheKey())
document.addResource(QtGui.QTextDocument.ImageResource,
QtCore.QUrl(name), image)
format = QtGui.QTextImageFormat()
View
42 IPython/frontend/qt/kernelmanager.py
@@ -2,7 +2,7 @@
"""
# System library imports.
-from PyQt4 import QtCore
+from IPython.external.qt import QtCore
# IPython imports.
from IPython.utils.traitlets import Type
@@ -14,10 +14,10 @@
class SocketChannelQObject(SuperQObject):
# Emitted when the channel is started.
- started = QtCore.pyqtSignal()
+ started = QtCore.Signal()
# Emitted when the channel is stopped.
- stopped = QtCore.pyqtSignal()
+ stopped = QtCore.Signal()
#---------------------------------------------------------------------------
# 'ZmqSocketChannel' interface
@@ -39,16 +39,16 @@ def stop(self):
class QtXReqSocketChannel(SocketChannelQObject, XReqSocketChannel):
# Emitted when any message is received.
- message_received = QtCore.pyqtSignal(object)
+ message_received = QtCore.Signal(object)
# Emitted when a reply has been received for the corresponding request
# type.
- execute_reply = QtCore.pyqtSignal(object)
- complete_reply = QtCore.pyqtSignal(object)
- object_info_reply = QtCore.pyqtSignal(object)
+ execute_reply = QtCore.Signal(object)
+ complete_reply = QtCore.Signal(object)
+ object_info_reply = QtCore.Signal(object)
# Emitted when the first reply comes back.
- first_reply = QtCore.pyqtSignal()
+ first_reply = QtCore.Signal()
# Used by the first_reply signal logic to determine if a reply is the
# first.
@@ -87,29 +87,29 @@ def reset_first_reply(self):
class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):
# Emitted when any message is received.
- message_received = QtCore.pyqtSignal(object)
+ message_received = QtCore.Signal(object)
# Emitted when a message of type 'stream' is received.
- stream_received = QtCore.pyqtSignal(object)
+ stream_received = QtCore.Signal(object)
# Emitted when a message of type 'pyin' is received.
- pyin_received = QtCore.pyqtSignal(object)
+ pyin_received = QtCore.Signal(object)
# Emitted when a message of type 'pyout' is received.
- pyout_received = QtCore.pyqtSignal(object)
+ pyout_received = QtCore.Signal(object)
# Emitted when a message of type 'pyerr' is received.
- pyerr_received = QtCore.pyqtSignal(object)
+ pyerr_received = QtCore.Signal(object)
# Emitted when a message of type 'display_data' is received
- display_data_received = QtCore.pyqtSignal(object)
+ display_data_received = QtCore.Signal(object)
# Emitted when a crash report message is received from the kernel's
# last-resort sys.excepthook.
- crash_received = QtCore.pyqtSignal(object)
+ crash_received = QtCore.Signal(object)
# Emitted when a shutdown is noticed.
- shutdown_reply_received = QtCore.pyqtSignal(object)
+ shutdown_reply_received = QtCore.Signal(object)
#---------------------------------------------------------------------------
# 'SubSocketChannel' interface
@@ -138,10 +138,10 @@ def flush(self):
class QtRepSocketChannel(SocketChannelQObject, RepSocketChannel):
# Emitted when any message is received.
- message_received = QtCore.pyqtSignal(object)
+ message_received = QtCore.Signal(object)
# Emitted when an input request is received.
- input_requested = QtCore.pyqtSignal(object)
+ input_requested = QtCore.Signal(object)
#---------------------------------------------------------------------------
# 'RepSocketChannel' interface
@@ -162,7 +162,7 @@ def call_handlers(self, msg):
class QtHBSocketChannel(SocketChannelQObject, HBSocketChannel):
# Emitted when the kernel has died.
- kernel_died = QtCore.pyqtSignal(object)
+ kernel_died = QtCore.Signal(object)
#---------------------------------------------------------------------------
# 'HBSocketChannel' interface
@@ -182,10 +182,10 @@ class QtKernelManager(KernelManager, SuperQObject):
__metaclass__ = MetaQObjectHasTraits
# Emitted when the kernel manager has started listening.
- started_channels = QtCore.pyqtSignal()
+ started_channels = QtCore.Signal()
# Emitted when the kernel manager has stopped listening.
- stopped_channels = QtCore.pyqtSignal()
+ stopped_channels = QtCore.Signal()
# Use Qt-specific channel classes that emit signals.
sub_channel_class = Type(QtSubSocketChannel)
View
31 IPython/frontend/qt/svg.py
@@ -2,7 +2,7 @@
"""
# System library imports.
-from PyQt4 import QtCore, QtGui, QtSvg
+from IPython.external.qt import QtCore, QtGui, QtSvg
def save_svg(string, parent=None):
@@ -10,8 +10,8 @@ def save_svg(string, parent=None):
Parameters:
-----------
- string : str
- A Python string or QString containing a SVG document.
+ string : basestring
+ A Python string containing a SVG document.
parent : QWidget, optional
The parent to use for the file dialog.
@@ -40,15 +40,14 @@ def svg_to_clipboard(string):
Parameters:
-----------
- string : str
- A Python string or QString containing a SVG document.
+ string : basestring
+ A Python string containing a SVG document.
"""
- if isinstance(string, basestring):
- bytes = QtCore.QByteArray(string)
- else:
- bytes = string.toAscii()
+ if isinstance(string, unicode):
+ string = string.encode('utf-8')
+
mime_data = QtCore.QMimeData()
- mime_data.setData('image/svg+xml', bytes)
+ mime_data.setData('image/svg+xml', string)
QtGui.QApplication.clipboard().setMimeData(mime_data)
def svg_to_image(string, size=None):
@@ -56,8 +55,8 @@ def svg_to_image(string, size=None):
Parameters:
-----------
- string : str
- A Python string or QString containing a SVG document.
+ string : basestring
+ A Python string containing a SVG document.
size : QSize, optional
The size of the image that is produced. If not specified, the SVG
@@ -72,12 +71,10 @@ def svg_to_image(string, size=None):
--------
A QImage of format QImage.Format_ARGB32.
"""
- if isinstance(string, basestring):
- bytes = QtCore.QByteArray.fromRawData(string) # shallow copy
- else:
- bytes = string.toAscii()
+ if isinstance(string, unicode):
+ string = string.encode('utf-8')
- renderer = QtSvg.QSvgRenderer(bytes)
+ renderer = QtSvg.QSvgRenderer(QtCore.QByteArray(string))
if not renderer.isValid():
raise ValueError('Invalid SVG data.')
View
2  IPython/frontend/qt/util.py
@@ -5,7 +5,7 @@
import inspect
# System library imports.
-from PyQt4 import QtCore, QtGui
+from IPython.external.qt import QtCore, QtGui
# IPython imports.
from IPython.utils.traitlets import HasTraits, TraitType
Something went wrong with that request. Please try again.