PyQt4

Introducción

PyQt4 es un conjunto de herramientas para crear aplicaciones con interfaz gráfica. Es una mezcla entre Python y la biblioteca Qt. Qt library is one of the most powerful GUI libraries. Es desarrollado por la compañía Riverbank Computing Limited.

PyQt4 está implementado como un conjunto de módulos de Python. Cuenta con 440 clases y 6000 funciones. Es un conjunto de herramientas multiplataforma que corre en los sistemas operativos más importantes, incluyendo Unix, Windows, y Mac OS.

Clases

Las clases de PyQt4 están divididas en varios módulos:

  • QtCore
  • QtGui
  • QtNetwork
  • QtXml
  • QtSvg
  • QtOpenGL
  • QtSql

QtCore

Contiene las las funcionalidades principales que no tienen que ver con la interfaz gráfica. Se usa para trabajar con tiempo, archivos y directorios, varios tipos de datos, flujos, urls.

QtGui

Contiene los componentes gráficos y sus clases relacionadas. Por ejemplo botones, ventanas, barras de estado, barras de herramienta, deslizadores, mapas de bit, colores y fuentes.

QtNetwork

Contiene las clases para programación en redes. Estas clses facilitan la programanción de clientes y servidores de tipo TCP/IP y UDPT haciendo que sea más fácil y más portable nuestor código.

QtXml

Contiene clases para trabajar con archivos XML.

QtSvg

Nos provee de claes para desplegar contenido de archivos SVG. Scalable Vector Graphics (SVG) es un lenguage para describir gráficos bidimensionales y aplicaciones gráficas en XML.

QtOpenGL

Es usado para mostrar gráficos en 3D y 2D usando la biblioteca OpenGL.

QtSql

Nos provee de clases para trabajar con bases de datos.

Requisitos

Para poder usar PyQt4 se necesitan al menos lo siguiente: - Qt versión 4 - PyQt4 para la versión de python que estén usado

http://www.riverbankcomputing.com/software/pyqt/download

Hola Mundo!

import sys
from PyQt4 import QtGui

def main():
  app = QtGui.QApplication(sys.argv)

  etiqueta_hola = QtGui.QLabel("Hola Mundo!")
  etiqueta_hola.show()

  return app.exec_()

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

Surgen algunos pensamientos:

  • ¿Qué hace app.exec_()? [1]
  • No veo ningún ciclo. ¿Por qué la aplicación no termina? [2]
  • ¿No necesitamos una ventana para mostrar cosas? [3] [4]

Ejemplo sencillo

import sys
from PyQt4 import QtGui

def main():
  app = QtGui.QApplication(sys.argv)

  widget = QtGui.QWidget()
  widget.resize(250, 150)
  widget.move(300, 300)
  widget.setWindowTitle('Simple')
  widget.show()

  return app.exec_()

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

Botones

QtGui.QPushButton

import sys
from PyQt4 import QtGui


class Example(QtGui.QWidget):

  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):
    boton1 = QtGui.QPushButton('Botón1', self)
    boton2 = QtGui.QPushButton('Botón2', self)
    boton1.move(50, 50)
    self.setGeometry(300, 300, 200, 150)
    self.setWindowTitle('Boton')
    self.show()

def main():
  app = QtGui.QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())


if __name__ == '__main__':
  main()

Cerrando una ventana

import sys
from PyQt4 import QtGui, QtCore


class Example(QtGui.QWidget):

  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):
    boton1 = QtGui.QPushButton('Quit button', self)
    boton2 = QtGui.QPushButton('Botón2', self)
    boton1.clicked.connect(QtCore.QCoreApplication.instance().quit)
    boton1.move(50, 50)
    self.setGeometry(300, 300, 200, 150)
    self.setWindowTitle('Boton')
    self.show()

def main():
  app = QtGui.QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())


if __name__ == '__main__':
  main()

Entrada de texto y etiquetas

QtGui.QLineEdit

QtGui.QLabel

import sys
from PyQt4 import QtGui, QtCore


class Example(QtGui.QWidget):

  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):
    self.entrada_texto = QtGui.QLineEdit(self)
    self.resultado_lbl = QtGui.QLabel('Resultado:', self)

    self.entrada_texto.move(0, 0)
    self.resultado_lbl.move(0,40)

    self.setGeometry(200, 200, 400, 300)
    self.setWindowTitle('Mostrar y recibir texto')
    self.show()

def main():
  app = QtGui.QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())


if __name__ == '__main__':
  main()

Widgets

Mostrar texto

QtGui.QLabel

Recibir texto

QtGui.QLineEdit

Fuentes

fuente.setPixelSize(20)
fuente.setUnderline(True)
fuente.setBold(True)
fuente.setItalic(True)
fuente = QtGui.QFont()
fuente.setPixelSize(20)
QLabel("Hola").setFont(fuente)
etiqueta = QLabel("Hola")
fuente = etiqueta.font()
fuente.setPixelSize(20)
fuente.setFamily("Courier New")
etiqueta.setFont(fuente)

Layouts

Posición absoluta

Se espicifica a mano la posición y el tamaño de cada widget en pixeles. Cuando usamos posición absoluta debemos tener en cuenta las siguientes limitaciones:

  • El tamaño y posición del widget no cambia si cambiamos el tamaño de la ventana.
  • La aplicación puede cambiar de apariencia dependiendo de la plataforma
  • Cmabiar la fuente la aplicación puede echar a perder la disposición de los elementos
widget.move(x, y)
widget.setGeomtry(x, y, anchura, altura)

Box layout

QtGui.QVBoxLayout y QtGui.QHBoxLayout

import sys
from PyQt4 iport QtGui

class Ejemplo(QtGui.QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        boton_ok = QtGui.QPushButton("OK")
        boton_cancelar = QtGui.QPushButton("Cancelar")

        fuente = QtGui.QFont()
        fuente.setPixelSize(40)
        boton_ok.setFont(fuente)

        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(boton_ok)
        hbox.addStretch(1)
        hbox.addWidget(boton_cancelar)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('Buttons')
        self.show()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Ejemplo()
    sys.exit(app.exec_())

QtGui.QGridLayout

grid = QtGui.QGridLayout()
self.setLayout(grid)
grid.addWidget(widget, fila, columna)

Cosas importantes sobre los layouts y los padres:

  • Cuando se usen layouts, no se necesita pasar el padre como parámetro al construir un widget. El layout va a asignarle como padre(usando QWidget.setParent()) el widget al que pertenece.
  • Los Widgets en un layout son hijos del widget al que pertenece el layout, no son hijos del layout. Los widgets sólo pueden tener como padres otros widgets, no layouts.
  • Se pueden anidar layous usando addLayout() en un layout. El layout interior se vuelve hijo del layout en el que fue insertado.

Signals y Slots

Para que una clase pueda contener Signals y Slots debe de heredar de QObject o una de sus subclases. Cuando se emite una señal los slots conectados a esta usualmente se ejecutan inmediatamente, como si se hubiera mandado a llamar a cada una en vez de emitir la señal.

La ejecución del código que sigue a la emisión de una señal se realiza después de que todos los slots hayan hecho su return, es decir al emitir una señal esta bloquea la ejecución del método hasta que las funciones que estén escuchando esta señal hayan terminado de ejecutarse.

Si muchos slots están conectados a la misma señal se ejecutaran en el orden en el que fueron conectadas.

mi_senial = QtCore.pyqtSignal(int, str)
mi_senial.emit()
mi_senial.connect(objeto.funcion)
import sys
from PyQt4 import QtGui, QtCore

class Example(QtGui.QWidget):
  mi_senial = QtCore.pyqtSignal(Objeto)
  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):
    self.entrada_texto = QtGui.QLineEdit(self)
    self.resultado_lbl = QtGui.QLabel('Resultado:', self)

    self.entrada_texto.move(0, 0)
    self.resultado_lbl.move(0,40)

    self.entrada_texto.textChanged.connect(self.calcula_suma)

    self.setGeometry(200, 200, 200, 150)
    self.setWindowTitle('Boton')
    self.show()

  def calcula_suma(self, entrada):
    resultado = "Error"
    print(entrada)
    try:
      resultado = "Resultado: " + str(eval(entrada))
    except Exception as e:
      print(e)
    self.resultado_lbl.setText(resultado)
    self.resultado_lbl.adjustSize()

def main():
  app = QtGui.QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())
if __name__ == '__main__':
  main()
import sys
from PyQt4 import QtGui, QtCore

class Objeto:
  val = 99

class Ejemplo(QtGui.QWidget):
  mi_senial = QtCore.pyqtSignal(Objeto)
  def __init__(self):
      super().__init__()

      self.initUI()

  def initUI(self):

      boton_ok = QtGui.QPushButton("OK")
      boton_cancelar = QtGui.QPushButton("Cancelar")

      fuente = QtGui.QFont()
      fuente.setPixelSize(40)
      boton_ok.setFont(fuente)

      hbox = QtGui.QHBoxLayout()
      hbox.addWidget(boton_ok,0)
      hbox.addStretch(1)
      hbox.addWidget(boton_cancelar)

      vbox = QtGui.QVBoxLayout()
      vbox.addStretch(1)
      vbox.addLayout(hbox)

      self.setLayout(vbox)

      boton_ok.clicked.connect(self.emite)
      self.mi_senial.connect(self.procesa_senial)
      self.algo = Objeto()
      self.setGeometry(300, 300, 300, 150)
      self.setWindowTitle('Buttons')
      self.show()

  def emite(self):
    self.mi_senial.emit(self.algo)
  def procesa_senial(self, algo):
    print(algo, type(algo), algo.val)

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Ejemplo()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Ejemplo más complicado

Recomendaciones

  • Recuerda que PyQt4 usa Qt4 por lo que si la documentación de PyQt4 no te satisface puedes ir a la documentación de Qt4
  • Salvo tu Widget principal intenta que todos los widgets que construyas tengan un padre para evitar errores
[1]QtGui.Application.exec_
[2]main event loop
[3]QWidget PyQt
[4]QWidget Qt