PyQt4 : Utiliser un fichier .ui et l'auto-connexion

PyQt permet d'utiliser un fichier .ui issu de Qt Designer dans du code Python, sans avoir à le convertir/compiler au préalable.

Voici un exemple de code minimal permettant de charger le fichier .ui (inspiré de ce que génère le Plugin Builder de QGIS, par exemple) :

# -*- coding: utf-8 -*-

import os

from PyQt4 import uic
from PyQt4.QtGui import QDialog

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'test_dialog.ui'))

class TestDialog(QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__(parent)
        self.setupUi(self)

Premier détail intéressant : dans le code des méthodes de cette classe, il n'est pas nécessaire d'utiliser la méthode findChild() pour accéder aux widgets définis dans Qt Designer.

Ainsi, si vous avez créé une QDoubleSpinBox nommée doubleSpinBoxLength depuis Qt Designer, le code suivant :

self.findChild(QDoubleSpinBox, 'doubleSpinBoxLength')

peut être remplacé par un simple :

self.doubleSpinBoxLength

Autre détail potentiellement utile : toujours pour les widgets ajoutés via Qt Designer, il y a une fonctionnalité d'auto-connexion ("autoconnect" en anglais) des signaux et des slots.
Cette fonctionnalité vous dispense d'écrire des appels à .connect() pour chaque widget que vous souhaitez connecter.

Cette connexion automatique se base sur le nom de la méthode, qui doit être composé ainsi : on_nomDuWidget_nomDuSignal

Dans notre exemple, on pourrait par exemple définir une méthode :

def on_doubleSpinBoxLength_valueChanged(self, value):
    print value
    print type(value)

Seulement, si vous ne faites rien de plus, vous aurez la surprise de voir votre méthode appelée 2 fois à chaque changement de valeur de la QDoubleSpinBox.
En regardant attentivement, vous vous apercevrez que le type du paramètre value n'est pas le même pour les 2 appels.

En effet, en consultant la documentation de la classe QDoubleSpinBox (en version C++ ou en version Python), on s'aperçoit qu'il existe 2 signaux nommés valueChanged : l'un avec un paramètre de type nombre flottant, et l'autre de type de chaîne de caractères.

Pour lever l'ambiguïté, et n'avoir plus qu'un seul appel à notre méthode, il faut utiliser le décorateur @pyqtSlot (présent dans le module QtCore) :

@pyqtSlot(float)
def on_doubleSpinBoxLength_valueChanged(self, value):
    print value
    print type(value)

ou

@pyqtSlot(str)
def on_doubleSpinBoxLength_valueChanged(self, value):
    print value
    print type(value)

selon que l'on souhaite récupérer la valeur sous forme d'un float ou d'une str.

Ne pas oublier de rajouter l'import correspondant en début de fichier :

from PyQt4.QtCore import pyqtSlot

On pourrait même avoir 2 méthodes différentes (une pour le paramètre de type float, et l'autre pour le paramètre de type str), que l'on pourrait nommer comme on le souhaite (sans avoir à respecter de contrainte sur le format du nom), toujours grâce au décorateur @pyqtSlot :

@pyqtSlot(float, name='on_doubleSpinBoxLength_valueChanged')
def lengthValueChangedCustomMethod1(self, value):
    print 'method1'
    print value
    print type(value)

@pyqtSlot(str, name='on_doubleSpinBoxLength_valueChanged')
def lengthValueChangedCustomMethod2(self, value):
    print 'method2'
    print value
    print type(value)

Pour plus d'informations, n'hésitez pas à consulter la page de documentation suivante : http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html#connecting-slots-by-name

Tags :
Qt
Python
PyQt4
Qt Designer
Signaux
Slots