I forgot the most important thing: to attach the file :) On Saturday, December 20, 2014 8:46:40 PM UTC-6, Sandor Racz wrote: > > Hi Everyone, > > I have added PyQt5 support to Spyder (keeping the PyQt4 and PySide > support). > > It has been tested for Python 2/ Python 3 and PyQt4/PyQt5. > > What I have: Using SourceTree I cloned a recent default, added code > changes, tested it, created a new local branch and committed the changes > into it. > > In case of need for a PyQt5 version I would appreciate if someone could > summarize what should be the steps to submit it for code review. I am new > to SourceTree, Bitbucket and Git, know the basics though. > > > Sandor > > > > >
-- You received this message because you are subscribed to the Google Groups "spyder" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/spyderlib. For more options, visit https://groups.google.com/d/optout.
# HG changeset patch # User sandorracz <[email protected]> # Date 1419111140 21600 # Sat Dec 20 15:32:20 2014 -0600 # Branch PyQt5FullSupport # Node ID 2c7136f39d60358f4c76eb13798a61ff3013e97a # Parent c4cc844716061d9c72e3d4783bfa90ed12dd632d Added PyQt5 support (PyQ4 and PySide are also supported) diff --git a/bootstrap.py b/bootstrap.py --- a/bootstrap.py +++ b/bootstrap.py @@ -32,7 +32,7 @@ Type `python bootstrap.py -- --help` to read about Spyder options.""") parser.add_option('--gui', default=None, - help="GUI toolkit: pyqt (for PyQt4) or pyside (for PySide)") + help="GUI toolkit: pyqt (for PyQt4/PyQt5) or pyside (for PySide)") parser.add_option('--hide-console', action='store_true', default=False, help="Hide parent console window (Windows only)") parser.add_option('--test', dest="test", action='store_true', default=False, @@ -104,7 +104,7 @@ print(" and %s" % EXTPATH) -# Selecting the GUI toolkit: PySide if installed, otherwise PyQt4 +# Selecting the GUI toolkit: PySide if installed, otherwise PyQt4 or PyQt5 # (Note: PyQt4 is still the officially supported GUI toolkit for Spyder) if options.gui is None: try: @@ -112,7 +112,7 @@ print("02. PySide is detected, selecting (experimental)") os.environ['QT_API'] = 'pyside' except: - print("02. No PySide detected, using PyQt4 if available") + print("02. No PySide detected, using PyQt4 or PyQt5 if available") else: print ("02. Skipping GUI toolkit detection") os.environ['QT_API'] = options.gui diff --git a/spyderlib/plugins/__init__.py b/spyderlib/plugins/__init__.py --- a/spyderlib/plugins/__init__.py +++ b/spyderlib/plugins/__init__.py @@ -31,6 +31,7 @@ from spyderlib.plugins.configdialog import SpyderConfigPage from spyderlib.py3compat import configparser, is_text_string import sys +from spyderlib.qt import PYQT5 class PluginConfigPage(SpyderConfigPage): @@ -154,9 +155,12 @@ show_message = None update_plugin_title = None - def __init__(self, main): + def __init__(self, main = None, **kwds): """Bind widget to a QMainWindow instance""" - super(SpyderPluginMixin, self).__init__() + if PYQT5: + super().__init__(**kwds) + else: + super(SpyderPluginMixin, self).__init__() assert self.CONF_SECTION is not None self.main = main self.default_margins = None @@ -169,6 +173,7 @@ # the one that comes with dockwidget because it's not possible # to raise and focus the plugin with it. self.toggle_view_action = None + def initialize_plugin(self): """Initialize plugin: connect signals, setup actions, ...""" @@ -341,6 +346,7 @@ def set_plugin_font(self, font, option=None): """Set plugin font option""" set_font(font, self.CONF_SECTION, option) + def __show_message(self, message, timeout=0): """Show message in main window's status bar""" @@ -399,10 +405,14 @@ sig_option_changed = Signal(str, object) show_message = Signal(str, int) update_plugin_title = Signal() - - def __init__(self, parent): - QWidget.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) + if PYQT5: + def __init__(self, parent, **kwds): + super().__init__(**kwds) + else: + def __init__(self, parent): + QWidget.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + def get_plugin_title(self): """ diff --git a/spyderlib/plugins/console.py b/spyderlib/plugins/console.py --- a/spyderlib/plugins/console.py +++ b/spyderlib/plugins/console.py @@ -34,6 +34,7 @@ from spyderlib.plugins import SpyderPluginWidget from spyderlib.py3compat import to_text_string, getcwd +from spyderlib.qt import PYQT5 class Console(SpyderPluginWidget): """ @@ -43,10 +44,16 @@ focus_changed = Signal() redirect_stdio = Signal(bool) edit_goto = Signal(str, int, str) - + show_message = Signal(str, int) + update_plugin_title = Signal() + + def __init__(self, parent=None, namespace=None, commands=[], message=None, exitfunc=None, profile=False, multithreaded=False): - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) debug_print(" ..internal console: initializing") self.dialog_manager = DialogManager() @@ -61,13 +68,15 @@ self.shell.status.connect(lambda msg: self.show_message.emit(msg, 0)) self.shell.go_to_error.connect(self.go_to_error) self.shell.focus_changed.connect(lambda: self.focus_changed.emit()) + + # Redirecting some signals: self.shell.redirect_stdio.connect(lambda state: self.redirect_stdio.emit(state)) # Initialize plugin self.initialize_plugin() - + # Find/replace widget self.find_widget = FindReplace(self) self.find_widget.set_editor(self.shell) @@ -85,6 +94,7 @@ # Accepting drops self.setAcceptDrops(True) + #------ Private API -------------------------------------------------------- def set_historylog(self, historylog): diff --git a/spyderlib/plugins/editor.py b/spyderlib/plugins/editor.py --- a/spyderlib/plugins/editor.py +++ b/spyderlib/plugins/editor.py @@ -45,6 +45,7 @@ get_run_configuration, ALWAYS_OPEN_FIRST_RUN_OPTION) from spyderlib.py3compat import to_text_string, getcwd, qbytearray_to_str +from spyderlib.qt import PYQT5 def _load_all_breakpoints(): @@ -339,9 +340,15 @@ open_dir = Signal(str) breakpoints_saved = Signal() run_in_current_extconsole = Signal(str, str, str, bool) + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent, ignore_last_opened_files=False): - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main=parent) + else: + SpyderPluginWidget.__init__(self, parent) self.__set_eol_chars = True @@ -1548,7 +1555,8 @@ if valid: self.set_option('max_recent_files', mrf) - @Slot() + + @Slot(str, int, str, object) def load(self, filenames=None, goto=None, word='', editorwindow=None, processevents=True): """ @@ -1985,9 +1993,12 @@ self.main.extconsole.execute_python_code(command) else: self.main.ipyconsole.write_to_stdin(command) + focus_widget = self.main.ipyconsole.get_focus_widget() + if focus_widget: focus_widget.setFocus() + else: self.main.extconsole.execute_python_code(command) diff --git a/spyderlib/plugins/explorer.py b/spyderlib/plugins/explorer.py --- a/spyderlib/plugins/explorer.py +++ b/spyderlib/plugins/explorer.py @@ -36,6 +36,9 @@ create_module = Signal(str) run = Signal(str) open_dir = Signal(str) + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent=None): ExplorerWidget.__init__(self, parent=parent, diff --git a/spyderlib/plugins/externalconsole.py b/spyderlib/plugins/externalconsole.py --- a/spyderlib/plugins/externalconsole.py +++ b/spyderlib/plugins/externalconsole.py @@ -41,6 +41,7 @@ from spyderlib.plugins.runconfig import get_run_configuration from spyderlib.py3compat import to_text_string, is_text_string, getcwd from spyderlib import dependencies +from spyderlib.qt import PYQT5 MPL_REQVER = '>=1.0' dependencies.add("matplotlib", _("Interactive data plotting in the consoles"), @@ -48,6 +49,7 @@ class ExternalConsoleConfigPage(PluginConfigPage): + def __init__(self, plugin, parent): PluginConfigPage.__init__(self, plugin, parent) self.get_name = lambda: _("Console") @@ -448,9 +450,16 @@ edit_goto = Signal(str, int, str, bool) focus_changed = Signal() redirect_stdio = Signal(bool) + show_message = Signal(str, int) + update_plugin_title = Signal() + go_to_error = Signal(str) + def __init__(self, parent, light_mode): - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) self.light_mode = light_mode self.tabwidget = None self.menu_actions = None @@ -693,7 +702,7 @@ # This is a unique form of the edit_goto signal that is intended to # prevent keyboard input from accidentally entering the editor # during repeated, rapid entry of debugging commands. - self.edit_got.emit(fname, lineno, '', False) + self.edit_goto.emit(fname, lineno, '', False) if shellwidget.is_ipykernel: # Focus client widget, not kernel ipw = self.main.ipyconsole.get_focus_widget() diff --git a/spyderlib/plugins/findinfiles.py b/spyderlib/plugins/findinfiles.py --- a/spyderlib/plugins/findinfiles.py +++ b/spyderlib/plugins/findinfiles.py @@ -29,6 +29,9 @@ toggle_visibility = Signal(bool) edit_goto = Signal(str, int, str) redirect_stdio = Signal(bool) + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent=None): supported_encodings = self.get_option('supported_encodings') diff --git a/spyderlib/plugins/history.py b/spyderlib/plugins/history.py --- a/spyderlib/plugins/history.py +++ b/spyderlib/plugins/history.py @@ -24,6 +24,7 @@ from spyderlib.widgets.findreplace import FindReplace from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage from spyderlib.py3compat import to_text_string, is_text_string +from spyderlib.qt import PYQT5 class HistoryConfigPage(PluginConfigPage): @@ -75,6 +76,9 @@ CONFIGWIDGET_CLASS = HistoryConfigPage # Signals focus_changed = Signal() + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent): self.tabwidget = None @@ -85,8 +89,10 @@ self.editors = [] self.filenames = [] self.icons = [] - - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) # Initialize plugin self.initialize_plugin() diff --git a/spyderlib/plugins/inspector.py b/spyderlib/plugins/inspector.py --- a/spyderlib/plugins/inspector.py +++ b/spyderlib/plugins/inspector.py @@ -34,6 +34,7 @@ from spyderlib.widgets.externalshell.pythonshell import ExtPythonShellWidget from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage from spyderlib.py3compat import to_text_string, get_meth_class_inst +from spyderlib.qt import PYQT5 #XXX: Hardcoded dependency on optional IPython plugin component # that requires the hack to make this work without IPython @@ -354,9 +355,16 @@ LOG_PATH = get_conf_path(CONF_SECTION) # Signals focus_changed = Signal() + show_message = Signal(str, int) + update_plugin_title = Signal() + focus_changed = Signal() + def __init__(self, parent): - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) self.internal_shell = None @@ -371,6 +379,7 @@ self.set_default_color_scheme() self.plain_text = PlainText(self) + self.rich_text = RichText(self) color_scheme = get_color_scheme(self.get_option('color_scheme_name')) diff --git a/spyderlib/plugins/ipythonconsole.py b/spyderlib/plugins/ipythonconsole.py --- a/spyderlib/plugins/ipythonconsole.py +++ b/spyderlib/plugins/ipythonconsole.py @@ -59,6 +59,7 @@ from spyderlib.widgets.findreplace import FindReplace from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage from spyderlib.py3compat import to_text_string, u +from spyderlib.qt import PYQT5 SYMPY_REQVER = '>=0.7.0' @@ -148,6 +149,8 @@ class IPythonConsoleConfigPage(PluginConfigPage): + + append_to_history = Signal(str, str) def __init__(self, plugin, parent): PluginConfigPage.__init__(self, plugin, parent) @@ -577,9 +580,14 @@ focus_changed = Signal() edit_goto = Signal(str, int, str) focus_changed = Signal() + show_message = Signal(str, int) + update_plugin_title = Signal() def __init__(self, parent): - SpyderPluginWidget.__init__(self, parent) + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) self.tabwidget = None self.menu_actions = None diff --git a/spyderlib/plugins/onlinehelp.py b/spyderlib/plugins/onlinehelp.py --- a/spyderlib/plugins/onlinehelp.py +++ b/spyderlib/plugins/onlinehelp.py @@ -22,8 +22,12 @@ Online Help Plugin """ sig_option_changed = Signal(str, object) + show_message = Signal(str, int) + update_plugin_title = Signal() + CONF_SECTION = 'onlinehelp' LOG_PATH = get_conf_path(CONF_SECTION) + def __init__(self, parent): self.main = parent PydocBrowser.__init__(self, parent) diff --git a/spyderlib/plugins/outlineexplorer.py b/spyderlib/plugins/outlineexplorer.py --- a/spyderlib/plugins/outlineexplorer.py +++ b/spyderlib/plugins/outlineexplorer.py @@ -24,6 +24,8 @@ class OutlineExplorer(OutlineExplorerWidget, SpyderPluginMixin): CONF_SECTION = 'outline_explorer' sig_option_changed = Signal(str, object) + show_message = Signal(str, int) + update_plugin_title = Signal() def __init__(self, parent=None, fullpath_sorting=True): show_fullpath = self.get_option('show_fullpath') diff --git a/spyderlib/plugins/projectexplorer.py b/spyderlib/plugins/projectexplorer.py --- a/spyderlib/plugins/projectexplorer.py +++ b/spyderlib/plugins/projectexplorer.py @@ -31,6 +31,9 @@ removed_tree = Signal(str) renamed = Signal(str, str) redirect_stdio = Signal(bool) + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent=None): ProjectExplorerWidget.__init__(self, parent=parent, diff --git a/spyderlib/plugins/shortcuts.py b/spyderlib/plugins/shortcuts.py --- a/spyderlib/plugins/shortcuts.py +++ b/spyderlib/plugins/shortcuts.py @@ -211,6 +211,11 @@ return True return False + def reset(self): + self.beginResetModel() + self.endResetModel() + + class ShortcutsDelegate(QItemDelegate): def __init__(self, parent=None): diff --git a/spyderlib/plugins/variableexplorer.py b/spyderlib/plugins/variableexplorer.py --- a/spyderlib/plugins/variableexplorer.py +++ b/spyderlib/plugins/variableexplorer.py @@ -86,6 +86,9 @@ CONF_SECTION = 'variable_explorer' CONFIGWIDGET_CLASS = VariableExplorerConfigPage sig_option_changed = Signal(str, object) + show_message = Signal(str, int) + update_plugin_title = Signal() + def __init__(self, parent): QStackedWidget.__init__(self, parent) SpyderPluginMixin.__init__(self, parent) diff --git a/spyderlib/plugins/workingdirectory.py b/spyderlib/plugins/workingdirectory.py --- a/spyderlib/plugins/workingdirectory.py +++ b/spyderlib/plugins/workingdirectory.py @@ -29,6 +29,7 @@ from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage from spyderlib.py3compat import to_text_string, getcwd +from spyderlib.qt import PYQT5 class WorkingDirectoryConfigPage(PluginConfigPage): def setup_page(self): @@ -147,10 +148,17 @@ set_explorer_cwd = Signal(str) refresh_findinfiles = Signal() set_current_console_wd = Signal(str) + show_message = Signal(str, int) + update_plugin_title = Signal() + - def __init__(self, parent, workdir=None): - QToolBar.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) + def __init__(self, parent, workdir=None, **kwds): + if PYQT5: + super().__init__(**kwds) + else: + QToolBar.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + # Initialize plugin self.initialize_plugin() diff --git a/spyderlib/qt/QtCore.py b/spyderlib/qt/QtCore.py --- a/spyderlib/qt/QtCore.py +++ b/spyderlib/qt/QtCore.py @@ -5,16 +5,49 @@ # (see spyderlib/__init__.py for details) import os +import sys + if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtCore import * # analysis:ignore - from PyQt4.Qt import QCoreApplication # analysis:ignore - from PyQt4.Qt import Qt # analysis:ignore - from PyQt4.QtCore import pyqtSignal as Signal # analysis:ignore - from PyQt4.QtCore import pyqtSlot as Slot # analysis:ignore - from PyQt4.QtCore import pyqtProperty as Property # analysis:ignore - from PyQt4.QtCore import QT_VERSION_STR as __version__ + if "PyQt4" in sys.modules: + from PyQt4.Qt import QSize, QByteArray, QUrl, QThread + from PyQt4.Qt import QAbstractTableModel, QModelIndex + from PyQt4.Qt import QObject, Qt, QLocale, QTranslator + from PyQt4.Qt import QProcess, QTimer, QTextCodec + from PyQt4.Qt import QEventLoop, QEvent, QPoint, QRect + from PyQt4.Qt import QRegExp, QFileInfo, QMimeData, QDir + from PyQt4.Qt import QMutexLocker, QMutex, QCoreApplication, QDateTime + from PyQt4.Qt import QBasicTimer + from PyQt4.QtCore import QLibraryInfo + from PyQt4.QtCore import pyqtSignal as Signal + from PyQt4.QtCore import pyqtSlot as Slot + from PyQt4.QtCore import pyqtProperty as Property + from PyQt4.QtCore import QT_VERSION_STR as __version__ + from PyQt4.QtCore import QProcessEnvironment + try: + # PyQt <v4.6 (API #1) + from PyQt4.Qt import QString + except ImportError: + # PyQt >=v4.6 + QString = None + elif "PyQt5" in sys.modules: + from PyQt5.QtCore import QSize, QByteArray, QUrl, QThread + from PyQt5.QtCore import QAbstractTableModel, QModelIndex + from PyQt5.QtCore import QObject, Qt, QLocale, QTranslator + from PyQt5.QtCore import QProcess, QTimer, QTextCodec + from PyQt5.QtCore import QEventLoop, QEvent, QPoint, QRect + from PyQt5.QtCore import QRegExp, QFileInfo, QMimeData, QDir + from PyQt5.QtCore import QMutexLocker, QMutex, QCoreApplication, QDateTime + from PyQt5.QtCore import QBasicTimer, QLibraryInfo + from PyQt5.QtCore import pyqtSignal as Signal + from PyQt5.QtCore import pyqtSlot as Slot + from PyQt5.QtCore import pyqtProperty as Property + from PyQt5.QtCore import QT_VERSION_STR as __version__ + from PyQt5.QtCore import QProcessEnvironment + + + else: import PySide.QtCore - __version__ = PySide.QtCore.__version__ # analysis:ignore - from PySide.QtCore import * # analysis:ignore + __version__ = PySide.QtCore.__version__ + from PySide.QtCore import * diff --git a/spyderlib/qt/QtGui.py b/spyderlib/qt/QtGui.py --- a/spyderlib/qt/QtGui.py +++ b/spyderlib/qt/QtGui.py @@ -5,9 +5,77 @@ # (see spyderlib/__init__.py for details) import os +import sys if os.environ['QT_API'] == 'pyqt': - from PyQt4.Qt import QKeySequence, QTextCursor # analysis:ignore - from PyQt4.QtGui import * # analysis:ignore + if "PyQt4" in sys.modules: + from PyQt4.QtGui import QApplication, QMainWindow, QWidget, QLabel + from PyQt4.QtGui import QDockWidget, QShortcut, QCursor, QDialog, QListWidget + from PyQt4.QtGui import QListWidgetItem, QVBoxLayout, QStackedWidget, QListView + from PyQt4.QtGui import QHBoxLayout, QDialogButtonBox, QCheckBox, QMessageBox + from PyQt4.QtGui import QLabel, QLineEdit, QSpinBox, QPushButton + from PyQt4.QtGui import QFontComboBox, QGroupBox, QComboBox, QColor, QGridLayout + from PyQt4.QtGui import QTabWidget, QRadioButton, QButtonGroup, QSplitter + from PyQt4.QtGui import QStyleFactory, QScrollArea, QAction, QPrinter + from PyQt4.QtGui import QPrintDialog, QToolBar, QActionGroup + from PyQt4.QtGui import QInputDialog, QMenu, QAbstractPrintDialog, QKeySequence + from PyQt4.QtGui import QPrintPreviewDialog, QFontDialog, QSizePolicy, QToolButton + from PyQt4.QtGui import QFormLayout, QStackedWidget, QFrame, QItemDelegate + from PyQt4.QtGui import QTableView, QStackedWidget, QDesktopServices, QStyle + from PyQt4.QtGui import QIcon, QKeyEvent, QPixmap, QFont + from PyQt4.QtGui import QCursor, QTextCursor, QTextEdit, QTextCharFormat + from PyQt4.QtGui import QToolTip, QPlainTextEdit, QPalette, QTextOption + from PyQt4.QtGui import QMouseEvent, QTextFormat, QClipboard, QPainter + from PyQt4.QtGui import QBrush, QTextDocument, QTextBlockUserData, QIntValidator + from PyQt4.QtGui import QSyntaxHighlighter, QDoubleValidator, QAbstractItemDelegate, QProgressBar + from PyQt4.QtGui import QColorDialog, QCompleter, QDateEdit, QDateTimeEdit + from PyQt4.QtGui import QTreeWidgetItem, QFileSystemModel, QDrag, QSortFilterProxyModel + from PyQt4.QtGui import QSpacerItem, QFileIconProvider, QHeaderView, QAbstractItemView + from PyQt4.QtGui import QTabBar, QFontDatabase, QSplashScreen + from PyQt4.QtGui import QFileDialog, QTreeWidget, QTreeView + from PyQt4.QtGui import QStylePainter, QStyleOptionFrame,QPaintEvent + elif "PyQt5" in sys.modules: + from PyQt5.QtGui import QCursor + from PyQt5.QtGui import QColor + from PyQt5.QtGui import QKeySequence + from PyQt5.QtGui import QDesktopServices + from PyQt5.QtGui import QIcon, QKeyEvent, QPixmap, QFont + from PyQt5.QtGui import QCursor, QTextCursor + from PyQt5.QtGui import QTextCharFormat + from PyQt5.QtGui import QPalette, QTextOption + from PyQt5.QtGui import QMouseEvent, QTextFormat, QClipboard, QPainter + from PyQt5.QtGui import QBrush, QTextDocument, QTextBlockUserData, QIntValidator + from PyQt5.QtGui import QSyntaxHighlighter, QDoubleValidator + from PyQt5.QtGui import QDrag + from PyQt5.QtGui import QFontDatabase, QPaintEvent + from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel + from PyQt5.QtWidgets import QDockWidget, QShortcut + from PyQt5.QtWidgets import QDialog, QListWidget + from PyQt5.QtWidgets import QListWidgetItem, QVBoxLayout, QStackedWidget, QListView + from PyQt5.QtWidgets import QHBoxLayout, QDialogButtonBox, QCheckBox, QMessageBox + from PyQt5.QtWidgets import QLabel, QLineEdit, QSpinBox, QPushButton + from PyQt5.QtWidgets import QFontComboBox, QGroupBox, QComboBox + from PyQt5.QtWidgets import QGridLayout + from PyQt5.QtWidgets import QTabWidget, QRadioButton, QButtonGroup, QSplitter + from PyQt5.QtWidgets import QStyleFactory, QScrollArea, QAction + from PyQt5.QtWidgets import QToolBar, QActionGroup + from PyQt5.QtWidgets import QInputDialog, QMenu + from PyQt5.QtWidgets import QFontDialog, QSizePolicy, QToolButton + from PyQt5.QtWidgets import QFormLayout, QStackedWidget, QFrame, QItemDelegate + from PyQt5.QtWidgets import QTableView, QStackedWidget + from PyQt5.QtWidgets import QStyle + from PyQt5.QtWidgets import QTextEdit + from PyQt5.QtWidgets import QToolTip, QPlainTextEdit + from PyQt5.QtWidgets import QProgressBar, QAbstractItemDelegate + from PyQt5.QtWidgets import QColorDialog, QCompleter, QDateEdit, QDateTimeEdit + from PyQt5.QtWidgets import QTreeWidgetItem, QFileSystemModel + from PyQt5.QtWidgets import QSpacerItem, QFileIconProvider, QHeaderView, QAbstractItemView + from PyQt5.QtWidgets import QTabBar, QSplashScreen + from PyQt5.QtWidgets import QFileDialog, QTreeWidget, QTreeView + from PyQt5.QtWidgets import QStylePainter, QStyleOptionFrame + from PyQt5.QtCore import QSortFilterProxyModel + from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QAbstractPrintDialog + from PyQt5.QtPrintSupport import QPrintPreviewDialog + else: from PySide.QtGui import * # analysis:ignore diff --git a/spyderlib/qt/QtSvg.py b/spyderlib/qt/QtSvg.py --- a/spyderlib/qt/QtSvg.py +++ b/spyderlib/qt/QtSvg.py @@ -5,8 +5,13 @@ # (see spyderlib/__init__.py for details) import os +import sys if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtSvg import * # analysis:ignore + if "PyQt4" in sys.modules: + from PyQt4.QtSvg import * # analysis:ignore + elif "PyQt5" in sys.modules: + from PyQt5.QtSvg import * # analysis:ignore + else: from PySide.QtSvg import * # analysis:ignore \ No newline at end of file diff --git a/spyderlib/qt/QtWebKit.py b/spyderlib/qt/QtWebKit.py --- a/spyderlib/qt/QtWebKit.py +++ b/spyderlib/qt/QtWebKit.py @@ -5,8 +5,14 @@ # (see spyderlib/__init__.py for details) import os +import sys if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtWebKit import * # analysis:ignore + if "PyQt4" in sys.modules: + from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings + elif "PyQt5" in sys.modules: + from PyQt5.QtWebKitWidgets import QWebPage, QWebView + from PyQt5.QtWebKit import QWebSettings + else: from PySide.QtWebKit import * # analysis:ignore \ No newline at end of file diff --git a/spyderlib/qt/__init__.py b/spyderlib/qt/__init__.py --- a/spyderlib/qt/__init__.py +++ b/spyderlib/qt/__init__.py @@ -12,7 +12,10 @@ assert os.environ['QT_API'] in ('pyqt', 'pyside') API = os.environ['QT_API'] -API_NAME = {'pyqt': 'PyQt4', 'pyside': 'PySide'}[API] +#API_NAME = {'pyqt': 'PyQt4', 'pyside': 'PySide'}[API] + +PYQT5 = False + if API == 'pyqt': # We do not force QString, QVariant, ... API to #1 or #2 anymore @@ -27,20 +30,28 @@ # pass try: from PyQt4.QtCore import PYQT_VERSION_STR as __version__ - except ImportError: - # Switching to PySide - API = os.environ['QT_API'] = 'pyside' - API_NAME = 'PySide' - else: __version_info__ = tuple(__version__.split('.')+['final', 1]) is_old_pyqt = __version__.startswith(('4.4', '4.5', '4.6', '4.7')) is_pyqt46 = __version__.startswith('4.6') + API_NAME = 'PyQt4' import sip try: + API_NAME += (" (API v%d)" % sip.getapi('QString')) except AttributeError: pass + except ImportError: + try: + from PyQt5.QtCore import PYQT_VERSION_STR as __version__ + is_pyqt46 = False + API_NAME = 'PyQt5' + PYQT5 = True + except: + # Switching to PySide + API = os.environ['QT_API'] = 'pyside' + API_NAME = 'PySide' + if API == 'pyside': try: from PySide import __version__ # analysis:ignore diff --git a/spyderlib/qt/compat.py b/spyderlib/qt/compat.py --- a/spyderlib/qt/compat.py +++ b/spyderlib/qt/compat.py @@ -33,12 +33,13 @@ PYQT_API_1 = False if os.environ['QT_API'] == 'pyqt': - import sip - try: - PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 - except AttributeError: - # PyQt <v4.6 - PYQT_API_1 = True + if 'PyQt4' in sys.modules: + import sip + try: + PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 + except AttributeError: + # PyQt <v4.6 + PYQT_API_1 = True def to_qvariant(pyobj=None): """Convert Python object to QVariant This is a transitional function from PyQt API #1 (QVariant exist) diff --git a/spyderlib/requirements.py b/spyderlib/requirements.py --- a/spyderlib/requirements.py +++ b/spyderlib/requirements.py @@ -35,16 +35,17 @@ def check_qt(): """Check Qt binding requirements""" - qt_infos = dict(pyqt=("PyQt4", "4.6"), pyside=("PySide", "1.2.0")) - try: - from spyderlib import qt - package_name, required_ver = qt_infos[qt.API] - actual_ver = qt.__version__ - if LooseVersion(actual_ver) < LooseVersion(required_ver): + if "PyQt4" in sys.modules: + qt_infos = dict(pyqt=("PyQt4", "4.6"), pyside=("PySide", "1.2.0")) + try: + from spyderlib import qt + package_name, required_ver = qt_infos[qt.API] + actual_ver = qt.__version__ + if LooseVersion(actual_ver) < LooseVersion(required_ver): + show_warning("Please check Spyder installation requirements:\n" + "%s %s+ is required (found v%s)." + % (package_name, required_ver, actual_ver)) + except ImportError: show_warning("Please check Spyder installation requirements:\n" - "%s %s+ is required (found v%s)." - % (package_name, required_ver, actual_ver)) - except ImportError: - show_warning("Please check Spyder installation requirements:\n" - "%s %s+ (or %s %s+) is required." - % (qt_infos['pyqt']+qt_infos['pyside'])) + "%s %s+ (or %s %s+) is required." + % (qt_infos['pyqt']+qt_infos['pyside'])) diff --git a/spyderlib/spyder.py b/spyderlib/spyder.py --- a/spyderlib/spyder.py +++ b/spyderlib/spyder.py @@ -790,7 +790,7 @@ # Working directory plugin self.debug_print(" ..plugin: working directory") from spyderlib.plugins.workingdirectory import WorkingDirectory - self.workingdirectory = WorkingDirectory(self, self.init_workdir) + self.workingdirectory = WorkingDirectory(self, self.init_workdir, main=self) self.workingdirectory.register_plugin() self.toolbarslist.append(self.workingdirectory) @@ -930,9 +930,10 @@ spyder_doc = 'http://pythonhosted.org/spyder' else: spyder_doc = file_uri(spyder_doc) - doc_action = create_bookmark_action(self, spyder_doc, - _("Spyder documentation"), shortcut="F1", - icon=get_std_icon('DialogHelpButton')) + doc_action = create_action( self, _("Spyder documentation"), shortcut="F1", + icon=get_std_icon('DialogHelpButton'), + triggered=lambda : programs.start_file(spyder_doc)) + tut_action = create_action(self, _("Spyder tutorial"), triggered=self.inspector.show_tutorial) self.help_menu_actions = [doc_action, tut_action, None, diff --git a/spyderlib/widgets/arrayeditor.py b/spyderlib/widgets/arrayeditor.py --- a/spyderlib/widgets/arrayeditor.py +++ b/spyderlib/widgets/arrayeditor.py @@ -284,6 +284,11 @@ else: return to_qvariant(labels[section]) + def reset(self): + self.beginResetModel() + self.endResetModel() + + class ArrayDelegate(QItemDelegate): """Array Editor Item Delegate""" diff --git a/spyderlib/widgets/dataframeeditor.py b/spyderlib/widgets/dataframeeditor.py --- a/spyderlib/widgets/dataframeeditor.py +++ b/spyderlib/widgets/dataframeeditor.py @@ -316,6 +316,11 @@ else: return shape[1]+1 + def reset(self): + self.beginResetModel() + self.endResetModel() + + class DataFrameView(QTableView): """Data Frame view class""" diff --git a/spyderlib/widgets/dependencies.py b/spyderlib/widgets/dependencies.py --- a/spyderlib/widgets/dependencies.py +++ b/spyderlib/widgets/dependencies.py @@ -90,6 +90,11 @@ color.setAlphaF(.25) return to_qvariant(color) + def reset(self): + self.beginResetModel() + self.endResetModel() + + class DependenciesDelegate(QItemDelegate): def __init__(self, parent=None): diff --git a/spyderlib/widgets/dicteditor.py b/spyderlib/widgets/dicteditor.py --- a/spyderlib/widgets/dicteditor.py +++ b/spyderlib/widgets/dicteditor.py @@ -215,7 +215,9 @@ self.keys = sort_against(self.keys, values, reverse) self.sizes = sort_against(self.sizes, values, reverse) self.types = sort_against(self.types, values, reverse) - self.reset() + self.beginResetModel() + self.endResetModel() + def columnCount(self, qindex=QModelIndex()): """Array column number""" @@ -313,6 +315,10 @@ return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| Qt.ItemIsEditable) + def reset(self): + self.beginResetModel() + self.endResetModel() + class DictModel(ReadOnlyDictModel): """DictEditor Table Model""" diff --git a/spyderlib/widgets/externalshell/baseshell.py b/spyderlib/widgets/externalshell/baseshell.py --- a/spyderlib/widgets/externalshell/baseshell.py +++ b/spyderlib/widgets/externalshell/baseshell.py @@ -204,7 +204,11 @@ self.is_closing = True self.process.kill() self.process.waitForFinished(100) - self.timer.timeout.disconnect(self.show_time) + + try: + self.timer.timeout.disconnect(self.show_time) + except RuntimeError: + pass def set_running_state(self, state=True): self.set_buttons_runnning_state(state) diff --git a/spyderlib/widgets/externalshell/pythonshell.py b/spyderlib/widgets/externalshell/pythonshell.py --- a/spyderlib/widgets/externalshell/pythonshell.py +++ b/spyderlib/widgets/externalshell/pythonshell.py @@ -13,6 +13,7 @@ from spyderlib.qt.QtGui import QApplication, QMessageBox, QSplitter, QMenu from spyderlib.qt.QtCore import QProcess, Signal, Slot, Qt +from spyderlib.qt.QtCore import QProcessEnvironment from spyderlib.qt.compat import getexistingdirectory # Local imports @@ -36,6 +37,9 @@ class ExtPythonShellWidget(PythonShellWidget): wait_for_ready_read = Signal() + go_to_error = Signal(str) + focus_changed = Signal() + def __init__(self, parent, history_filename, profile=False): PythonShellWidget.__init__(self, parent, history_filename, profile) @@ -523,7 +527,12 @@ env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')] # 3. - self.process.setEnvironment(env) + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + self.process.setProcessEnvironment(processEnvironment) +# self.process.setEnvironment(env) self.process.start(self.pythonexecutable, p_args) #-------------------------Python specific------------------------------ diff --git a/spyderlib/widgets/externalshell/sitecustomize.py b/spyderlib/widgets/externalshell/sitecustomize.py --- a/spyderlib/widgets/externalshell/sitecustomize.py +++ b/spyderlib/widgets/externalshell/sitecustomize.py @@ -259,9 +259,15 @@ # natively by PySide if os.environ.get("INSTALL_QT_INPUTHOOK", "").lower() == "true": if os.environ["QT_API"] == 'pyqt': - from PyQt4 import QtCore - # Removing PyQt's PyOS_InputHook implementation: - QtCore.pyqtRemoveInputHook() + if 'PyQt4' in sys.modules: + from PyQt4 import QtCore + # Removing PyQt's PyOS_InputHook implementation: + QtCore.pyqtRemoveInputHook() + elif 'PyQt5' in sys.modules: + from PyQt5 import QtCore + # Removing PyQt's PyOS_InputHook implementation: + QtCore.pyqtRemoveInputHook() + elif os.environ["QT_API"] == 'pyside': from PySide import QtCore # XXX: when PySide will implement an input hook, we will have to @@ -351,6 +357,7 @@ kwargs['testRunner'] = kwargs.pop('testRunner', test_runner) kwargs['exit'] = False TestProgram.__init__(self, *args, **kwargs) + unittest.main = IPyTesProgram # Patch a Pandas function to make it recognize our IPython consoles as diff --git a/spyderlib/widgets/externalshell/systemshell.py b/spyderlib/widgets/externalshell/systemshell.py --- a/spyderlib/widgets/externalshell/systemshell.py +++ b/spyderlib/widgets/externalshell/systemshell.py @@ -10,6 +10,7 @@ from spyderlib.qt.QtGui import QMessageBox from spyderlib.qt.QtCore import QProcess, Signal, QTextCodec +from spyderlib.qt.QtCore import QProcessEnvironment LOCALE_CODEC = QTextCodec.codecForLocale() CP850_CODEC = QTextCodec.codecForName('cp850') @@ -58,8 +59,15 @@ # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe) env = [to_text_string(_path) for _path in self.process.systemEnvironment()] + + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + add_pathlist_to_PYTHONPATH(env, self.path) - self.process.setEnvironment(env) + self.process.setProcessEnvironment(processEnvironment) + # Working directory if self.wdir is not None: diff --git a/spyderlib/widgets/importwizard.py b/spyderlib/widgets/importwizard.py --- a/spyderlib/widgets/importwizard.py +++ b/spyderlib/widgets/importwizard.py @@ -324,6 +324,9 @@ except Exception as instance: print(instance) + def reset(self): + self.beginResetModel() + self.endResetModel() class PreviewTable(QTableView): """Import wizard preview widget""" diff --git a/spyderlib/widgets/internalshell.py b/spyderlib/widgets/internalshell.py --- a/spyderlib/widgets/internalshell.py +++ b/spyderlib/widgets/internalshell.py @@ -122,6 +122,7 @@ status = Signal(str) refresh = Signal() + go_to_error = Signal(str) focus_changed = Signal() def __init__(self, parent=None, namespace=None, commands=[], message=None, diff --git a/spyderlib/widgets/ipython.py b/spyderlib/widgets/ipython.py --- a/spyderlib/widgets/ipython.py +++ b/spyderlib/widgets/ipython.py @@ -74,6 +74,7 @@ """ QT_CLASS = QTextEdit visibility_changed = Signal(bool) + focus_changed = Signal() go_to_error = Signal(str) focus_changed = Signal() diff --git a/spyderlib/widgets/mixins.py b/spyderlib/widgets/mixins.py --- a/spyderlib/widgets/mixins.py +++ b/spyderlib/widgets/mixins.py @@ -19,7 +19,7 @@ from spyderlib.qt.QtGui import (QTextCursor, QTextDocument, QApplication, QCursor, QToolTip) -from spyderlib.qt.QtCore import Qt, QPoint, QRegExp +from spyderlib.qt.QtCore import Qt, QPoint, QRegExp, Signal # Local imports from spyderlib.baseconfig import _ @@ -35,6 +35,8 @@ class BaseEditMixin(object): + focus_changed = Signal() + def __init__(self): self.eol_chars = None self.calltip_size = 600 diff --git a/spyderlib/widgets/projectexplorer.py b/spyderlib/widgets/projectexplorer.py --- a/spyderlib/widgets/projectexplorer.py +++ b/spyderlib/widgets/projectexplorer.py @@ -31,6 +31,7 @@ from spyderlib.widgets.formlayout import fedit from spyderlib.widgets.pathmanager import PathManager from spyderlib.py3compat import to_text_string, getcwd, pickle +from spyderlib.qt import PYQT5 def has_children_files(path, include, exclude, show_all): @@ -664,7 +665,10 @@ self.show_hscrollbar = checked self.header().setStretchLastSection(not checked) self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) - self.header().setResizeMode(QHeaderView.ResizeToContents) + if PYQT5: + self.header().setSectionResizeMode(QHeaderView.ResizeToContents) + else: + self.header().setResizeMode(QHeaderView.ResizeToContents) def set_folder_names(self, folder_names): """Set folder names""" diff --git a/spyderlib/widgets/shell.py b/spyderlib/widgets/shell.py --- a/spyderlib/widgets/shell.py +++ b/spyderlib/widgets/shell.py @@ -968,6 +968,8 @@ COM = 'rem' if os.name == 'nt' else '#' INITHISTORY = ['%s *** Spyder Terminal History Log ***' % COM, COM,] SEPARATOR = '%s%s ---(%s)---' % (os.linesep*2, COM, time.ctime()) + go_to_error = Signal(str) + def __init__(self, parent, history_filename, profile=False): ShellBaseWidget.__init__(self, parent, history_filename, profile) diff --git a/spyderlib/widgets/sourcecode/base.py b/spyderlib/widgets/sourcecode/base.py --- a/spyderlib/widgets/sourcecode/base.py +++ b/spyderlib/widgets/sourcecode/base.py @@ -179,6 +179,7 @@ BRACE_MATCHING_SCOPE = ('sof', 'eof') cell_separators = None focus_in = Signal() + focus_center = Signal() zoom_in = Signal() zoom_out = Signal() focus_changed = Signal() diff --git a/spyderplugins/p_breakpoints.py b/spyderplugins/p_breakpoints.py --- a/spyderplugins/p_breakpoints.py +++ b/spyderplugins/p_breakpoints.py @@ -19,11 +19,16 @@ from spyderlib.plugins import SpyderPluginMixin from spyderplugins.widgets.breakpointsgui import BreakpointWidget from spyderlib.py3compat import to_text_string, is_text_string +from spyderlib.qt.QtCore import Signal class Breakpoints(BreakpointWidget, SpyderPluginMixin): """Breakpoint list""" CONF_SECTION = 'breakpoints' + show_message = Signal(str, int) + update_plugin_title = Signal() + + # CONFIGWIDGET_CLASS = BreakpointConfigPage def __init__(self, parent=None): BreakpointWidget.__init__(self, parent=parent) diff --git a/spyderplugins/p_profiler.py b/spyderplugins/p_profiler.py --- a/spyderplugins/p_profiler.py +++ b/spyderplugins/p_profiler.py @@ -52,6 +52,11 @@ CONF_SECTION = 'profiler' CONFIGWIDGET_CLASS = ProfilerConfigPage edit_goto = Signal(str, int, str) + show_message = Signal(str, int) + update_plugin_title = Signal() + + + def __init__(self, parent=None): ProfilerWidget.__init__(self, parent=parent, diff --git a/spyderplugins/p_pylint.py b/spyderplugins/p_pylint.py --- a/spyderplugins/p_pylint.py +++ b/spyderplugins/p_pylint.py @@ -76,6 +76,11 @@ CONF_SECTION = 'pylint' CONFIGWIDGET_CLASS = PylintConfigPage edit_goto = Signal(str, int, str) + show_message = Signal(str, int) + update_plugin_title = Signal() + + + def __init__(self, parent=None): PylintWidget.__init__(self, parent=parent, diff --git a/spyderplugins/widgets/breakpointsgui.py b/spyderplugins/widgets/breakpointsgui.py --- a/spyderplugins/widgets/breakpointsgui.py +++ b/spyderplugins/widgets/breakpointsgui.py @@ -113,6 +113,11 @@ else: return to_qvariant() + def reset(self): + self.beginResetModel() + self.endResetModel() + + class BreakpointDelegate(QItemDelegate): def __init__(self, parent=None): diff --git a/spyderplugins/widgets/profilergui.py b/spyderplugins/widgets/profilergui.py --- a/spyderplugins/widgets/profilergui.py +++ b/spyderplugins/widgets/profilergui.py @@ -40,6 +40,7 @@ from spyderlib.widgets.externalshell import baseshell from spyderlib.py3compat import to_text_string, getcwd _ = get_translation("p_profiler", dirname="spyderplugins") +from spyderlib.qt.QtCore import QProcessEnvironment def is_profiler_installed(): @@ -213,7 +214,13 @@ env = [to_text_string(_pth) for _pth in self.process.systemEnvironment()] baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) - self.process.setEnvironment(env) + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + self.process.setProcessEnvironment(processEnvironment) + + self.output = '' self.error_output = ''
