Mostrando entradas con la etiqueta Phonon. Mostrar todas las entradas
Mostrando entradas con la etiqueta Phonon. Mostrar todas las entradas

viernes, 2 de agosto de 2013

Muy rápido, muy fácil, reproductor de audio con Python (Ahora con video)

En el articulo pasado hicimos un reproductor de audio, este únicamente reproducía pistas de audio, en esta entrega vamos a hacer que mediante un modulo nuevo y unas cuantas lineas extra sea posible reproducir vídeo.

Qt es un framework que ofrece herramientas muy poderosas, una de estas es el modulo Phonon, el cual nos da la posibilidad de reproducir audio y vídeo, para este fin usaremos el widget VideoWidget, el cual viene incluido dentro del grupo de widgets de la librería Phonon. el VideoWidget será enlazado a un AudioObject  y a un MediaObject, este ultimo sera el encargado de cambiar el archivo fuente e iniciar la reproducción.
Todo lo anterior sera agrupado en una clase para intentar hacer esto un poco más modular y poder reusarlo de ser necesario.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from PySide.QtGui import QApplication
from PySide.phonon import Phonon
from PySide import QtCore, QtGui


class player(Phonon.VideoWidget):

    def __init__(self, file_path):
        super(player, self).__init__(parent=None)
        self.setWindowTitle("Video")
        QtGui.QApplication.addLibraryPath(
            os.path.join(os.path.dirname(__file__), 'plugins'))
        media_src = Phonon.MediaSource(file_path)
        self.file_path = file_path
        media_obj = Phonon.MediaObject()
        media_obj.setCurrentSource(media_src)
        Phonon.createPath(media_obj, self)
        self.media_obj = media_obj
        audio_out = Phonon.AudioOutput(Phonon.VideoCategory)
        Phonon.createPath(media_obj, audio_out)
        self.audio_out = audio_out
        self.media_obj.setTickInterval(1000)
        self.media_obj.tick.connect(self.alAvanzar)
        self.media_obj.stateChanged.connect(self.alCambiarEstado)

    def alAvanzar(self, tiempo):
        pass

    def alCambiarEstado(self, new, old):
        pass

    def play(self):
        self.media_obj.play()

    def pause(self):
        self.media_obj.pause()

    def stop(self):
        self.media_obj.stop()

    def state(self):
        return self.media_obj.state()

En el código anterior vemos como una la clase player hereda de la clase Phonon.VideoWidget y tiene como atributos un MediaObject y un AudioOutput, los cuales sirven como control de reproducción y salida de audio respectivamente, y los tres son enlazados en el constructor de la clase en la linea 21 y 24.

Ahora veremos la nueva versión del reproductor, con las mejoras respectivas para implementar nuestro player de vídeo:
from PySide import QtCore, QtGui, phonon, QtUiTools
from PySide.phonon import Phonon

from videopanel import player


class Reproductor(QtGui.QWidget):

    def __init__(self):
        super(Reproductor, self).__init__(parent=None)
        self.setWindowTitle("Python Simple Player")
        self.gridLayout = QtGui.QGridLayout(self)
        loader = QtUiTools.QUiLoader()
        file = QtCore.QFile("repro.ui")
        file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(file, self)
        file.close()
        self.gridLayout.addWidget(self.ui, 0, 0, 1, 1)
        # seccion multimedia
        self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
        self.mediaObject = Phonon.MediaObject(self)
        Phonon.createPath(self.mediaObject, self.audioOutput)
        self.mediaObject.setTickInterval(1000)
        #
        self.ui.lcdAvance.display("00:00")
        QtCore.QObject.connect(
            self.ui.btnArchivo,
            QtCore.SIGNAL("clicked()"),
            self.openFile)
        QtCore.QObject.connect(
            self.ui.btnPlay,
            QtCore.SIGNAL("clicked()"),
            self.play)
        self.mediaObject.tick.connect(self.alAvanzar)
        self.mediaObject.stateChanged.connect(self.alCambiarEstado)
        #----------------------
        self.player = None

    def alAvanzar(self, tiempo):
        displayTime = QtCore.QTime(
            0, (tiempo / 60000) %
            60, (tiempo / 1000) %
            60)
        self.ui.lcdAvance.display(displayTime.toString('mm:ss'))

    def alCambiarEstado(self, new, old):
        if self.mediaObject.state() == Phonon.State.StoppedState:
            self.ui.btnPlay.setEnabled(True)
        elif self.mediaObject.state() == Phonon.State.PlayingState:
            self.ui.btnPlay.setText("||")  # .setEnabled(False)

    def play(self):
        if self.player:
            if self.player.state() == Phonon.State.PlayingState:
                self.player.pause()
                self.player.show()
                self.ui.btnPlay.setText(">")
            else:
                self.player.show()
                self.player.play()
                self.ui.lblNombre.setText(self.player.file_path)
                self.ui.btnPlay.setText("||")
        else:
            if self.mediaObject.state() == Phonon.State.PlayingState:
                self.mediaObject.pause()
                self.ui.btnPlay.setText(">")
            else:
                self.mediaObject.play()
                #///////////////////////////////////
                meta = self.mediaObject.metaData()
                print(meta)
                meta["TITLE"] = meta[
                    "TITLE"] if "TITLE" in meta else "Indefinido"
                meta["ARTIST"] = meta[
                    "ARTIST"] if "ARTIST" in meta else "SinNombre"
                label = "{TITLE} by {ARTIST}".format(**meta)
                self.ui.lblNombre.setText(label)
                #///////////////////////////////////
                self.ui.btnPlay.setText("||")

    def openFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self,
            u"Abrir Archivo",
            u".",
            u"Archivos de Audio (*.mp3 *.wav *.ogg);;Archivos de Video (*.mp4 *.mpg *.avi)")
        if fileName[1]:
            self.path = fileName[0]
            self.mediaObject.setCurrentSource(
                Phonon.MediaSource(self.path))
            self.ui.btnPlay.setText(">")
            self.ui.lblNombre.setText("...")
            self.ui.lcdAvance.display("00:00")
            if fileName[0][-3:] in "mp4 mpg avi".split():
                self.player = player(fileName[0])
                self.player.alAvanzar = self.alAvanzar
                self.player.alCambiarEstado = self.alCambiarEstado
            else:
                self.player = None

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    rep = Reproductor()
    rep.show()
    sys.exit(app.exec_())

Ahora lo primero que vemos es una modificación en el constructor, en el cual creamos una variable de clase llamada player que es nula (linea 38). Luego vemos en el método play una condición en la cual si el player es diferente de nulo llama al objeto player para que este inicie la reproducción o la detenga. Finalmente en el método openFile vemos que se verifica la extensión del archivo a reproducir, si es un archivo de vídeo la variable de clase player deja de ser nula y se convierte en una instancia de la clase player que ya habíamos hecho antes, de lo contrario sigue siendo nula.

Espero que les guste y que les sirva, dejo el código en GitHub, si tienen sugerencias o demás comenten.

martes, 30 de julio de 2013

Muy rápido, muy fácil, reproductor de audio con Python


En la entrada anterior pudimos ver cómo con muy poco código fue posible obtener un simple pero poderoso navegador web con el lenguaje de programación Python, PySide (Los bindings de Qt, uno de los  frameworks gráficos del lenguaje C++) y poco más de 70 líneas de código. En esta entrega veremos cómo usando el diseñador de interfaces y algo de código podemos hacer un muy simple reproductor de audio, el cual será extendido en próximas entregas.
Primero que todo es necesario decir que Qt ofrece una herramienta para diseñar interfaces de usuario. Esta se llama QtDesigner y viene incluida en el instalador de la libreria PySide de windows (para los entornos linux hay que instalarlo a mano). Esta herramienta es muy útil, puesto que con ella podemos hacer la interfaz y luego exportarla al lenguaje que vayamos a usar para realizar la aplicación (en la actualidad Qt tiene bindings para varios lenguajes, entre ellos Java, Ruby, Python y Php, entre otros), además de proveer un sistema de enlace entre objetos y métodos mediante eventos (en Qt se habla de slots y signals), lo cual facilita en cierta manera la tarea del programador. Esta herramienta es una herramienta WYSIWYG, la cual nos permite arrastrar, soltar y acomodar hasta obtener la interfaz como se desea. A continuación la imagen de la interfaz gráfica del reproductor:


La interfaz gráfica contiene unos cuantos QLabel para mostrar información, un QLCDNumber para mostrar cuánto tiempo ha transcurrido, un par de botones y unos espaciadores para conservar las proporciones al cambiar el tamaño de la ventana. A pesar de que sería posible "compilar" la interfaz a el lenguaje nativo con herramientas como el pyside-uic no lo vamos a hacer.

Ahora vamos a ver el código Python:

#!/usr/bin/env python

from PySide import QtCore, QtGui, phonon, QtUiTools
from PySide.phonon import Phonon

class Reproductor(QtGui.QWidget):

    def __init__(self):
        super(Reproductor, self).__init__(parent=None)
        self.setWindowTitle("Python Simple Player")
        self.gridLayout = QtGui.QGridLayout(self)
        loader = QtUiTools.QUiLoader()
        file = QtCore.QFile("repro.ui")
        file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(file, self)
        file.close()
        self.gridLayout.addWidget(self.ui, 0, 0, 1, 1)
        # seccion multimedia
        self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
        self.mediaObject = Phonon.MediaObject(self)
        Phonon.createPath(self.mediaObject, self.audioOutput)
        self.mediaObject.setTickInterval(1000)
        #
        self.ui.lcdAvance.display("00:00")
        QtCore.QObject.connect(
            self.ui.btnArchivo,
            QtCore.SIGNAL("clicked()"),
            self.openFile)
        QtCore.QObject.connect(
            self.ui.btnPlay,
            QtCore.SIGNAL("clicked()"),
            self.play)
        self.mediaObject.tick.connect(self.alAvanzar)
        self.mediaObject.stateChanged.connect(self.alCambiarEstado)

    def alAvanzar(self, tiempo):
        displayTime = QtCore.QTime(
            0, (tiempo / 60000) %
            60, (tiempo / 1000) %
            60)
        self.ui.lcdAvance.display(displayTime.toString('mm:ss'))

    def alCambiarEstado(self, new, old):
        if self.mediaObject.state() == Phonon.State.StoppedState:
            self.ui.btnPlay.setEnabled(True)
        elif self.mediaObject.state() == Phonon.State.PlayingState:
            self.ui.btnPlay.setText("||")  # .setEnabled(False)

    def play(self):
        if self.mediaObject.state() == Phonon.State.PlayingState:
            self.mediaObject.pause()
            self.ui.btnPlay.setText(">")
        else:
            self.mediaObject.play()
            #///////////////////////////////////
            meta = self.mediaObject.metaData()
            meta["TITLE"] = meta["TITLE"] if "TITLE" in meta else "Indefinido"
            meta["ARTIST"] = meta[
                "ARTIST"] if "ARTIST" in meta else "SinNombre"
            label = "{TITLE} by {ARTIST}".format(**meta)
            self.ui.lblNombre.setText(label)
            #///////////////////////////////////
            self.ui.btnPlay.setText("||")

    def openFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self,
            u"Abrir Archivo",
            u"/",
            u"Archivos de Audio (*.mp3 *.wav *.ogg)")
        if fileName[1]:
            self.path = fileName[0]
            self.mediaObject.setCurrentSource(
                Phonon.MediaSource(self.path))
            self.ui.btnPlay.setText(">")
            self.ui.lblNombre.setText("...")
            self.ui.lcdAvance.display("00:00")

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    rep = Reproductor()
    rep.show()
    sys.exit(app.exec_())

En el código anterior podemos ver cómo mediante el uso del modulo QtUiTools es posible usar la clase QUiLoader para cargar en tiempo de ejecución el archivo de interfaz gráfica (repro.ui), y obtenemos un objeto QWidget, el cual contiene toda la jerarquía de objetos que habíamos hecho en el diseñador de interfaces, los cuales quedan listos para ser usados a gusto. Después de esto acoplamos el QWidget obtenido mediante un gridLayout. Seguidamente creamos las instancias de la salida de audio (audioOutput) y del reproductor (mediaObject), los cuales son enlazados mediante la instrucción:
Phonon.createPath(self.mediaObject, self.audioOutput)
Esta instrucción enlaza el reproductor con la salida de audio. Después de esto se establece cada cuánto se va a lanzar una señal de actualización (es para que pueda medirse el tiempo transcurrido y hacer cosas de acuerdo a este) mediante el tickInterval, el cual se establece cada 1000 milisegundos (un segundo) y se conectan objetos como botones a los eventos correspondientes para que antes de finalizar el constructor se enlacen las señales del reproductor a los métodos encargados de modificar el avance y cambiar la etiqueta del botón de reproducción.
Los métodos siguientes se encargan de cambiar el valor de las etiquetas, de iniciar o detener la reproducción o cambiar el título de la pista.

 El código correspondiente esta en GitHub para quienes estén interesados.