Bien qu'il y ait quelques pages d'aide comme Differences Between PyQt4 and PyQt5 et New-style Signal and Slot Support, convertir un projet complet de PyQt4 vers PyQt5 peut poser quelques difficultés.

J'ai donc écrit un petit utilitaire permettant de retrouver dans quel module PyQtx se trouve telle ou telle classe. Si la classe est trouvée, la ligne affichée sera à copier/coller dans votre script, tant qu'à faire ☺.

Contenu du script find-pyqt-class.py :

# coding: utf-8
""" Trouver le nom du module qui contient telle ou telle classe PyQtx.

    Usage : python find-pyqt-class.py CLASS
       ex : python find-pyqt-class.py QDialog
"""

from importlib import import_module
from os import walk
from sys import argv

# Vous pouvez changer pour PyQt4 (ou PyQt6 :)
import PyQt5 as PyQt


def list_modules():
    ''' Récupérer la liste des modules de PyQt. '''

    for _, _, files in walk(PyQt.__path__[0]):
        for entry in files:
            if entry.startswith('Qt'):
                name = entry.split('.')[0]
                if name != 'Qt':
                    yield name


def find_class(class_name):
    """ Retrouver le module contenant la classe `class_name`. """

    for module in list_modules():
        # Importation du module sous la forme "PyQt5.QtCore" par exemple
        # Voir https://docs.python.org/3/library/importlib.html
        module_to_import = PyQt.__name__ + '.' + module
        i = import_module(module_to_import)

        # Si la classe recherchée se trouve dans la liste des exports du module
        # càd dans `__all__` défini par le module
        if class_name in dir(i):
            return module_to_import


def main():
    """ Point d'entrée du programme. """

    try:
        class_needed = argv[1]
    except IndexError:
        print('Usage: python {} CLASS'.format(argv[0]))
        print(' i.e.: python {} QDialog'.format(argv[0]))
        return 1

    module = find_class(class_needed)
    if not module:
        print('{} not found in {}.'.format(class_needed, PyQt.__name__))
        return 1

    print('from {} import {}'.format(module, class_needed))


if __name__ == '__main__':
    exit(main())

Voici ce que ça donne avec une classe qui a changé de module entre les 2 versions :

$ python find-pyqt-class.py "QDialog"
from PyQt4.QtGui import QDialog

$ python find-pyqt-class.py "QDialog"
from PyQt5.QtWidgets import QDialog

Et avec une classe obsolète supprimée dans PyQt5 :

$ python find-pyqt-class.py "QScriptEngine"
from PyQt4.QtScript import QScriptEngine

$ python find-pyqt-class.py "QScriptEngine"
QScriptEngine not found in PyQt5.

Historique

  • 2017-01-09 : utilisation de yield dans list_modules().