¡Hola a todos! Anteriormente les hablé sobre como se realizaba la internacionalización de aplicaciones PyQt/PySide (ambos bindings de la biblioteca gráfica Qt para el lenguaje de programación Python) y aunque sabía que acá en HumanOS nunca antes se había hablado sobre PySide (salvo algunos comentarios en algunas entradas). Intencionalmente no entré en detalles porque quería guardar municiones para la entrada que hoy les traigo.

pyside_vs_pyqt

Un poco de historia…

PyQt fue desarrollada por la firma británica Riverbank Computing y está disponible para Windows, GNU/Linux y Mac OS X bajo diferentes licencias.

En agosto de 2009, tras intentar negociar con Riverbank Computing la liberación de PyQt bajo licencia LGPL sin conseguirlo, Nokia, propietaria de Qt, libera bajo esta licencia un binding similar, llamado PySide. PySide soporta Linux/X11, Mac OS X, MeeGo, Windows y Maemo.

Diferencias de las APIs

  1. Diferentes nombres a la hora de importar (PySide en lugar de PyQt4). PySide utiliza un nombre de biblioteca diferente de PyQt. En lugar de escribir:
    from PyQt4.QtCore import *
    #or
    import PyQt4.QtCore

    tendremos que escribir:

    from PySide.QtCore import *
    #or
    import PySide.QtCore
  2. PySide solo soporta la API 2 de PyQt(PSEP 101). PyQt proporciona dos API distintas, la primera de las cuales proporciona QStrings, QVariants, etc igual que en Python. La nueva API 2 proporciona la conversión automática entre las clases de Qt y tipos de datos nativos de Python y es de naturaleza mucho más Pythónica. La API por defecto de PyQt en Python 2.x es la API 1, mientras que en Python 3 es la API 2. PySide sólo es compatible con la API 2 de PyQt (ver PSEP 101) para más detalles. Por lo tanto, las clases de Qt como QString, QStringList y QVariant no están disponibles en PySide. En lugar de ello, simplemente usa los tipos de datos nativos de Python. Si estás portando el código desde PyQt, es posible que desee modificar primero el código PyQt para que utilice la API 2 (utilizando una llamada a sip.setapi(clase, versión) antes de importar PyQt4), y sólo después de conseguir que funcione el cambio, cambiar a PySide. Nota: Debido al cambio de la API, QFileDialog.getOpenFileName devuelve una tupla en PySide, que a menudo es un problema al portar código de PyQt. Véase, por ejemplo error 343.
  3. Nuevo estilo de signals y slots usa una sitaxis ligeramente diferente(PSEP 100). PyQt lamentablemente utiliza un esquema de nombres específico para la implementación de este nuevo estilo de signals y slots:
    # Define a new signal called 'trigger' that has no arguments.
    trigger = QtCore.pyqtSignal()

    Como se describe en PSEP 100, utiliza QtCore.Signal() y QtCore.Slot() en su lugar. Si quieres modificar su código PyQt para que utilice el esquema de nomenclatura PySide, puedes hacerlo utilizando una definición simple:

    QtCore.Signal = QtCore.pyqtSignal
    QtCore.Slot = QtCore.pyqtSlot
  4. Declaración de Propiedades de Qt se realiza utilizando una sintaxis ligeramente diferente (PSEP 103). Al igual que el cambio de signals y slots anterior, el declarado de las Qt Properties se realiza usando QtCore.Property en lugar de QtCore.pyqtProperty(ver PSEP 103).
  5. Diferentes nombres de las herramientas. PySide utiliza nombres diferentes para las herramientas:
    • pyuic4 -> pyside-uic
    • pyrcc4 -> pyside-rcc
    • pylupdate4 -> pyside-lupdate
  6. Nombres de propiedades. PySide utiliza connectevent en el QObject. No utilices estos para nada en el código, y al mover el código desde PyQt revisa que no exista para evitar conflictos.
  7. QThreads. En PySide se debe llamar al método .wait() de un hilo después de llamar al método .stop() si vas a salir de la aplicación. De lo contrario, es posible que lance una excepción que diga: “QThread: Hilo destruido mientras sigue corriendo. Fallo de segmentación”.

Las diferencias en la funcionalidad de los bindings

  1. Los bindings para funciones obsoletas antes de Qt 4.5 no se generan. Los bindings para funciones obsoletas antes de Qt 4.5 no se generan en PySide. Si una función no está presente en PySide, compruebe la documentación de referencia de Qt en línea y ver si la función no se utiliza y qué usar en su lugar. Ejemplo de error: error 359. Por ejemplo, esto afecta a funciones tales como QColor.dark() y QColor.light(), en sy lugar debes usar QColor.darker() y QColor.lighter().
  2. El método sender() devuelve None cuando se utiliza dentro de una función parcial o una lambda. Si una función lambda se usa como slot, la función sender() no se puede utilizar para adquirir el objeto que emite la señal. Esto funciona en PyQt, pero su aplicación se comporta de forma incorrecta en ciertas situaciones. Ver bug 344 para más detalles.
  3. Al heredar clases, los constructores de clase de los padres deben ser llamados siempre. PyQt en algunos casos permite códigos, tales como:

    class Window(QtGui.QWidget):
        def __init__(self, parent=None):
            super(QtGui.QWidget, self).__init__(parent)

    en la que el constructor de la clase padre directa no se llama. PySide espera que hagas lo correcto:

    class Window(QtGui.QWidget):
        def __init__(self, parent=None):
            super(Window, self).__init__(parent)
  4. Señales del viejo estilo necesitan el uso de los paréntesis. PyQt permite el uso de señales de cortocircuito sin paréntesis, tales como:

    self.emit(SIGNAL('text_changed_cb'), text)

    Dado que esta es una característica antigua y obsoleta, y el esfuerzo para solucionar este problema, no vale la pena, hemos decidido no aplicarla. En el código PySide necesitas usar algo como:

    self.emit(SIGNAL('text_changed_cb(QString)'), text)

    Puede comprobar la discusión completa sobre el bug #314.

  5. Sólo las señales sin argumentos pueden ser auto conectadas en el constructor. Si usas:
    action = QtGui.QAction(None, triggered=handler)
    La señal triggered() será conectada al controlador del slot en lugar de la señal triggered(bool).

Soportando ambas APIs

qt_compat.py es un ejemplo de la centralización del conocimiento de PySide y PyQt sin usar monkey-patching. Sirve tanto para señalar que diferencias existen, así como para mantener su código independiente del binding. Esto es importante para cuando se necesita soportar ambos bindings, por lo general durante la transición de uno a otro y la necesidad de seguir soportando las distribuciones más antiguas que todavía no vienen con PySide.

python_qt_binding  es un paquete de Python del middleware ROS que da un ejemplo más extenso de cómo mantener su código agnóstico del binding Python-Qt utilizado. Ofrece una abstracción muy transparente, que le permite escribir las líneas globales de importación como:

from python_qt_binding.QtCore import QObject
Además, proporciona una función loadUi sustituta para la carga sencilla de archivos .ui que funcionan de la misma forma con los dos bindings. Ver ejemplos de uso en rqt_gui.

Convirtiendo código PyQt a PySide

pysider es una sencilla utilidad que convierte el código PyQt4 a PySide.

Conclusiones

A manera de conslusión PySide contiene muchísimas similitudes con PyQt, su estructura de llamadas, su construcción de elementos, conectores, signals, slots, es prácticamente la misma idea en ambas. La diferencia más notoria se encuentra en el tipo de licencia que manejan.

La licencia LGPL (Licencia Pública General Reducida GNU), creada por la Free Software Foundation (creadora de la GPL en sus diversas versiones), con el fin de garantizar a los usuarios la libertad de uso y modificación del código del software para ajustar a sus necesidades, permitiendo enlazarla a un programa no-GPL, que puede ser Software Libre o Software privativo.

De esta manera, todo software escrito con PySide, puede ser utilizado e incrustado a software privativo sin entrar en conflicto de licenciamiento, caso contrario utilizando PyQt y GPL, la cual se menciona que todo uso del software no podrá ser integrado a software privativo y toda mejora deberá ser publicada para uso de la comunidad.

Claro está que PyQt ofrece una licencia comercial también para casos en los que tengamos que desarrollar aplicaciones con fines de lucro pero uno de los requisitos es utilizar software con soporte oficial de la empresa que la provee (Riverbank). La información completa la puedes encontrar aquí. ¡Hasta la próxima!

Fuentes: Qt-Project , Pythonízame y AskUbuntu.