Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Consulta sobre: Reproductor de video simple con PyQt 4 y 5
#1
Hola como estan todos?

Estuve viendo el ejemplo que mostraban en el siguiente link:

http://recursospython.com/codigos-de-fue...eo-pyqt-4/

Lo eh probado en mi placa de desarrollo Raspberry y funciona perfecto, pero queria agregarle algunas funciones:

- Mostrar el tiempo transcurrido y el total
- Funcion para detectar el final del video

Leí la documentación de phonon pero lamentablemente esta en c++ y mi desarrollo es bajo python3, si bien de c++ se bastante de python no, entonces no supe como migrar el código.

Alguien podría darme una mano con el código? 

Gracias de antemano.

Acá dejo el código original:

Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       videoplayer.py
#
#       Copyright 2016 Recursos Python - www.recursospython.com
#
#


import sys

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import (QApplication, QHBoxLayout, QMainWindow,
                        QWidget, QPushButton, QVBoxLayout)
from PyQt4.phonon import Phonon


# Ruta del archivo.
VIDEO_PATH = "video.wmv"


class MainWindow(QMainWindow):
   
   def __init__(self):
       QMainWindow.__init__(self)
       
       # Controles principales para organizar la ventana.
       self.widget = QWidget(self)
       self.layout = QVBoxLayout()
       self.bottom_layout = QHBoxLayout()
       
       # Control de reproducción de video de Qt.
       self.video_player = Phonon.VideoPlayer(
           Phonon.VideoCategory, self)
       
       # Botones de reproducción y pausa.
       self.play_button = QPushButton("Pausa", self)
       self.stop_button = QPushButton("Detener", self)
       
       # Deslizadores para el volumen y transición del video.
       self.seek_slider = Phonon.SeekSlider(self)
       self.volume_slider = Phonon.VolumeSlider(self)
       
       # Acomodar controles en la pantalla.
       self.layout.addWidget(self.video_player)
       self.layout.addLayout(self.bottom_layout)
       self.bottom_layout.addWidget(self.play_button)
       self.bottom_layout.addWidget(self.stop_button)
       self.bottom_layout.addWidget(self.volume_slider)
       self.layout.addWidget(self.seek_slider)
       
       # Conectar los eventos con sus correspondientes funciones.
       self.play_button.clicked.connect(self.play_clicked)
       self.stop_button.clicked.connect(self.stop_clicked)
       self.video_player.mediaObject().stateChanged.connect(
           self.state_changed)
       
       # Se utiliza installEventFilter() para capturar eventos
       # del mouse en el control de video que utiliza internamente
       # la clase Phonon.VideoPlayer.
       self.video_player.videoWidget().installEventFilter(self)
       
       # Personalizar la ventana.
       self.setWindowTitle("Reproductor de video")
       self.resize(800, 600)
       self.layout.setMargin(0)
       self.bottom_layout.setMargin(0)
       self.widget.setLayout(self.layout)
       self.setCentralWidget(self.widget)
       
       # Reproducir el archivo.
       self.video_player.play(Phonon.MediaSource(VIDEO_PATH))
       
       # Conectar el control de volumen y transición de video
       # con las clases correspondientes.
       self.seek_slider.setMediaObject(self.video_player.mediaObject())
       self.volume_slider.setAudioOutput(
           self.video_player.audioOutput())
   
   def play_clicked(self):
       """
       Comenzar o resumir la reproducción.
       """
       if (self.video_player.mediaObject().state() in
           (Phonon.PausedState, Phonon.StoppedState)):
           self.video_player.play()
       else:
           self.video_player.pause()
   
   def stop_clicked(self):
       """
       Detener la reproducción.
       """
       self.video_player.stop()
   
   def state_changed(self, newstate, oldstate):
       """
       Actualizar el texto de los botones de reproducción y pausa.
       """
       states = {
           Phonon.PausedState: "Resumir",
           Phonon.PlayingState: "Pausa",
           Phonon.StoppedState: "Reproducir"
       }
       self.play_button.setText(states[newstate])
       self.stop_button.setEnabled(newstate != Phonon.StoppedState)
   
   def eventFilter(self, obj, event):
       """
       Establecer o remover pantalla completa al obtener
       el evento MouseButtonDblClick.
       """
       if event.type() == QEvent.MouseButtonDblClick:
           obj.setFullScreen(not obj.isFullScreen())
       return False


if __name__ == "__main__":
   app = QApplication(sys.argv)
   window = MainWindow()
   window.show()
   sys.exit(app.exec_())
Responder
#2
Hola, ¿cómo estás?

Generalmente cuando trabajes con PyQt o PySide (ambas implementaciones de Qt para Python) te encontrarás con que la documentación es bastante limitada. Es preferible siempre remitirse a la documentación de Qt, por más que tenga el formato de C++, los nombres de las funciones en la mayoría de los casos se mantienen y la información es más completa.

Respecto a tu problema, como bien indicaste puedes utilizar la funciones currentTime y totalTime para obtener el tiempo transcurrido y el tiempo total. Por ejemplo:

Código:
print(self.video_player.currentTime())  # Tiempo transcurrido.
print(self.video_player.totalTime())  # Tiempo total.

Ambas funciones retornan milisegundos. Para convertirlo a una representación legible, como hh:mm:ss, puedes usar la siguiente función.

Código:
def from_milliseconds(ms):
    x = ms / 1000
    seconds = x % 60
    x /= 60
    minutes = x % 60
    x /= 60
    hours = x % 24
   return "{:02}:{:02}:{:02}".format(hours, minutes, seconds)

De modo que el código anterior quedaría así:

Código:
print(from_milliseconds(self.video_player.currentTime()))
print(from_milliseconds(self.video_player.totalTime()))

Por otro lado, puedes indicarle al reproductor de video que emita una señal cuando la reproducción ha llegado al final. Al final de la función __init__:

Código:
       self.video_player.mediaObject().finished.connect(self.finished)

Y luego añades la siguiente función a la clase:

Código:
    def finished(self):
        print("Terminado.")

Un saludo.
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder
#3
Hola Francisco!


Antes que nada gracias por la pronta respuesta!

Estuve probando la programacion que me pasaste y la función finished funciona perfecto! La otra función (from_milliseconds) no pude hacerla funcionar, me devuelve el siguiente error:

Código:
Traceback (most recent call last):
 File "/home/pi/Desktop/Nueva Programacion/video2.py", line 84, in <module>
   window = MainWindow()
 File "/home/pi/Desktop/Nueva Programacion/video2.py", line 63, in __init__
   print(from_milliseconds(self.video_player.currentTime()))
NameError: name 'from_milliseconds' is not defined


Seguramente tenga que ver con el scope de esa función no? sinceramente no termino de entender el scope de python3 pero bueno con el tiempo comprenderé la sintaxys, teneme paciencia.


Por otro lado por lo que vi seguramente sea necesario un bucle o un for in o un setInterval nose, para poder recibir constantemente el tiempo transcurrido no?

Por si te es de ayuda aca dejo mi código, le eh sacado lo que no necesitaba y agregue lo que me pasaste:
Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       videoplayer.py
#
#       Copyright 2016 Recursos Python - www.recursospython.com
#
#


import sys

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import (QApplication, QHBoxLayout, QMainWindow, QWidget, QPushButton, QVBoxLayout)
from PyQt4.phonon import Phonon

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import phonon


# Ruta del archivo.
#VIDEO_PATH = "video.wmv"
VIDEO_PATH = "1.avi"


class MainWindow(QMainWindow):
   
   def __init__(self):
       QMainWindow.__init__(self)
   
       # Controles principales para organizar la ventana.
       self.widget = QWidget(self)
       self.layout = QVBoxLayout()
       self.bottom_layout = QHBoxLayout()
       
       # Control de reproducción de video de Qt.
       self.video_player = Phonon.VideoPlayer(Phonon.VideoCategory, self)
       

       # Acomodar controles en la pantalla.
       self.layout.addWidget(self.video_player)

       
       # Se utiliza installEventFilter() para capturar eventos
       # del mouse en el control de video que utiliza internamente
       # la clase Phonon.VideoPlayer.
       self.video_player.videoWidget().installEventFilter(self)
       
       # Personalizar la ventana.
       self.setWindowTitle("::Reproductor de video::")
       self.resize(400, 200)
       self.layout.setMargin(0)#borde del video
       self.bottom_layout.setMargin(0)
       self.widget.setLayout(self.layout)
       self.setCentralWidget(self.widget)
       
       # Reproducir el archivo.
       self.video_player.play(Phonon.MediaSource(VIDEO_PATH))

       self.video_player.mediaObject().finished.connect(self.finished)

       print(from_milliseconds(self.video_player.currentTime()))
       print(from_milliseconds(self.video_player.totalTime()))
       
       #PLAY TO VIDEO
       #self.video_player.play()
 
       #Funcion para contar tiempo video
   def from_milliseconds(ms):
       x = ms/1000
       seconds = x%60
       x /= 60
       minutes = x%60
       x /= 60
       hours = x%24
       return "{:02}:{:02}:{:02}:".format(hours, minutes, seconds)
   def finished(self):
       print("terminado.")
       print(self.video_player.totalTime())

if __name__ == "__main__":
   app = QApplication(sys.argv)
   window = MainWindow()
   #window.showFullScreen()
   window.show()
   sys.exit(app.exec_())


Gracias de nuevo!
Responder
#4
Estuve probando muchos codigos, esto es para que veas que no soy un "comodo" que espera que le solucionen todo.

Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       videoplayer.py
#
#       Copyright 2016 Recursos Python - www.recursospython.com
#
#


import sys

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import (QApplication, QHBoxLayout, QMainWindow, QWidget, QPushButton, QVBoxLayout)
from PyQt4.phonon import Phonon

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from PyQt4 import phonon
from PyQt4 import QtGui
from PyQt4 import QtCore

import threading

# Ruta del archivo.
#VIDEO_PATH = "video.wmv"
VIDEO_PATH = "1.avi"


class MainWindow(QMainWindow):
       
   def __init__(self):
       QMainWindow.__init__(self)
       
       # Controles principales para organizar la ventana.
       self.widget = QWidget(self)
       self.layout = QVBoxLayout()
       self.bottom_layout = QHBoxLayout()
       
       # Control de reproducción de video de Qt.
       self.video_player = Phonon.VideoPlayer(Phonon.VideoCategory, self)
       

       # Acomodar controles en la pantalla.
       self.layout.addWidget(self.video_player)

       
       # Se utiliza installEventFilter() para capturar eventos
       # del mouse en el control de video que utiliza internamente
       # la clase Phonon.VideoPlayer.
       self.video_player.videoWidget().installEventFilter(self)
       
       # Personalizar la ventana.
       self.setWindowTitle("::Reproductor de video::")
       self.resize(400, 200)
       self.layout.setMargin(0)#borde del video
       self.bottom_layout.setMargin(0)
       self.widget.setLayout(self.layout)
       self.setCentralWidget(self.widget)

       # Reproducir el archivo.
       self.video_player.play(Phonon.MediaSource(VIDEO_PATH))
       self.video_player.seek(12000)
       
       self.video_player.mediaObject().finished.connect(self.finished)
       
       print(from_milliseconds(self.video_player.currentTime()))
       #print(from_milliseconds(self.video_player.totalTime()))
       self.ca = self.video_player.currentTime()
       setInterval(self,foo,1)
       #setInterval(foo,1)
   
       
       #PLAY TO VIDEO
       #self.video_player.play()
   def finished(self):
       print("terminado.")
       print(self.video_player.totalTime())
       self.video_player.play()
       self.video_player.seek(3000)
       
       print(from_milliseconds(self.video_player.totalTime()))
       
#Funcion para contar tiempo video
def from_milliseconds(ms):
   x = ms/1000
   seconds = x%60
   x /= 60
   minutes = x%60
   x /= 60
   hours = x%24
   return "{:02}:{:02}:{:02}:".format(hours, minutes, seconds)

def setInterval(this,func,time):
   e = threading.Event()
   while not e.wait(time):
       func(this)
def foo(this):
       print(this.ca)
       print("hello")    
   

if __name__ == "__main__":
   app = QApplication(sys.argv)
   window = MainWindow()
   #window.showFullScreen()
   window.show()
   sys.exit(app.exec_())

Esto seguramente este mal porque la ultima parte del codigo creo que la estoy dejando fuera de la clase pero aun asi logre hacer una función del tipo setInterval como lo hace JavaScript, le envió también un parametro con el scope de la clase principal pero el resultado de totalTime() por algún motivo es siempre 0.
 
Seguramente debo estar cerca pero aun no lo logro, estos son lo momentos cuando uno quiere revolear todo pero así me paso con c++, action script, php, entre otros lenguajes hasta que le tome la mano.

Te agradezco si me corregís los errores, principalmente del post anterior. Quiero aprender!!!

Muchas gracias!
Responder
#5
Hola. Pudiste resolver todo muy bien. Está bien que la función from_milliseconds esté fuera de la clase ya que tiene un funcionamiento autónomo, es decir, podría usarse por más que la ventana no se haya creado o bien sin usar Qt en lo absoluto. De todas formas esto es una convención para separar partes de tu código y tampoco habría estado mal que la hayas colocado dentro de la clase (habrías de llamarla como self.from_milliseconds).

Por otro lado, el reproductor de Qt provee una señal llamada tick que se emite cada una cierta cantidad de milisegundos y que tiene como propósito justamente permitir al usuario actualizar datos del video como, por ejemplo, el tiempo transcurrido (tal vez en un QLabel).

El código parcial de la clase sería el siguiente.

Código:
class MainWindow(QMainWindow):
   
   def __init__(self):
       QMainWindow.__init__(self)
       
       # Controles principales para organizar la ventana.
       self.widget = QWidget(self)
       self.layout = QVBoxLayout()
       self.bottom_layout = QHBoxLayout()
       
       # Control de reproducción de video de Qt.
       self.video_player = Phonon.VideoPlayer(
           Phonon.VideoCategory, self)
       
       # Botones de reproducción y pausa.
       self.play_button = QPushButton("Pausa", self)
       self.stop_button = QPushButton("Detener", self)
       
       # Deslizadores para el volumen y transición del video.
       self.seek_slider = Phonon.SeekSlider(self)
       self.volume_slider = Phonon.VolumeSlider(self)
       
       # Acomodar controles en la pantalla.
       self.layout.addWidget(self.video_player)
       self.layout.addLayout(self.bottom_layout)
       self.bottom_layout.addWidget(self.play_button)
       self.bottom_layout.addWidget(self.stop_button)
       self.bottom_layout.addWidget(self.volume_slider)
       self.layout.addWidget(self.seek_slider)
       
       # Conectar los eventos con sus correspondientes funciones.
       self.play_button.clicked.connect(self.play_clicked)
       self.stop_button.clicked.connect(self.stop_clicked)
       self.video_player.mediaObject().stateChanged.connect(
           self.state_changed)
       
       # Se utiliza installEventFilter() para capturar eventos
       # del mouse en el control de video que utiliza internamente
       # la clase Phonon.VideoPlayer.
       self.video_player.videoWidget().installEventFilter(self)
       
       # Personalizar la ventana.
       self.setWindowTitle("Reproductor de video")
       self.resize(800, 600)
       self.layout.setMargin(0)
       self.bottom_layout.setMargin(0)
       self.widget.setLayout(self.layout)
       self.setCentralWidget(self.widget)
       
       # Reproducir el archivo.
       self.video_player.play(Phonon.MediaSource(VIDEO_PATH))
       
       # Conectar el control de volumen y transición de video
       # con las clases correspondientes.
       self.seek_slider.setMediaObject(self.video_player.mediaObject())
       self.volume_slider.setAudioOutput(
           self.video_player.audioOutput())
       
       self.video_player.mediaObject().setTickInterval(1000)
       self.video_player.mediaObject().tick.connect(self.update_time)
       self.video_player.mediaObject().finished.connect(self.finished)
   
   def update_time(self):
       print(from_milliseconds(self.video_player.currentTime()))
       print(from_milliseconds(self.video_player.totalTime()))
   
   def finished(self):
       print("Terminado.")

Utilizamos setTickInterval(1000) para que Qt emita la señal cada 1 segundo.

Saludos.
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder
#6
Hola Francisco!

Nuevamente muchas gracias!!!
No puedo entender como sabes que la función tick se llama de esa forma, sinceramente asombrado!

Tengo un problema que no encontre solucion, la funcion que me pasaste para pasar de milesimas a segundos y horas me muestra el siguiente resultado:

Cita:0.0027580555555555557:0.16548333333333334:9.929:

Le estoy errando en algo?

Así quedo el código!
Código:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       videoplayer.py
#
#       Copyright 2016 Recursos Python - www.recursospython.com
#
#


import sys

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import (QApplication, QHBoxLayout, QMainWindow, QWidget, QPushButton, QVBoxLayout)
from PyQt4.phonon import Phonon

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from PyQt4 import phonon
from PyQt4 import QtGui
from PyQt4 import QtCore

import threading

# Ruta del archivo.
#VIDEO_PATH = "video.wmv"
VIDEO_PATH = "1.avi"


class MainWindow(QMainWindow):
       
   def __init__(self):
       QMainWindow.__init__(self)
       
       # Controles principales para organizar la ventana.
       self.widget = QWidget(self)
       self.layout = QVBoxLayout()
       self.bottom_layout = QHBoxLayout()
       
       # Control de reproducción de video de Qt.
       self.video_player = Phonon.VideoPlayer(Phonon.VideoCategory, self)
       

       # Acomodar controles en la pantalla.
       self.layout.addWidget(self.video_player)

       
       # Se utiliza installEventFilter() para capturar eventos
       # del mouse en el control de video que utiliza internamente
       # la clase Phonon.VideoPlayer.
       self.video_player.videoWidget().installEventFilter(self)
       
       # Personalizar la ventana.
       self.setWindowTitle("::Reproductor de video::")
       self.resize(400, 200)
       self.layout.setMargin(0)#borde del video
       self.bottom_layout.setMargin(0)
       self.widget.setLayout(self.layout)
       self.setCentralWidget(self.widget)

       # Reproducir el archivo.
       self.video_player.play(Phonon.MediaSource(VIDEO_PATH))
       #self.video_player.seek(12000)

       self.video_player.mediaObject().setTickInterval(1000)
       self.video_player.mediaObject().tick.connect(self.update_time)
       self.video_player.mediaObject().finished.connect(self.finished)
       
       #print(from_milliseconds(self.video_player.currentTime()))
       #print(from_milliseconds(self.video_player.totalTime()))
       #self.ca = self.video_player.currentTime()
       #setInterval(self,foo,1)
       #setInterval(foo,1)

   def update_time(self):
       print(self.video_player.currentTime())
       print(from_milliseconds(self.video_player.currentTime()))
       print(from_milliseconds(self.video_player.totalTime()))
       
       #PLAY TO VIDEO
       #self.video_player.play()
   def finished(self):
       print("terminado.")
       #print(self.video_player.totalTime())
       self.video_player.play()
       self.video_player.seek(3000)
   
#Funcion para contar tiempo video
def from_milliseconds(ms):
   x = ms/1000
   seconds = x%60
   x /= 60
   minutes = x%60
   x /= 60
   hours = x%24
   return "{:02}:{:02}:{:02}:".format(hours, minutes, seconds)
   

if __name__ == "__main__":
   app = QApplication(sys.argv)
   window = MainWindow()
   #window.showFullScreen()
   window.show()
   sys.exit(app.exec_())

Por otro lado, estas ultimas lineas:
Código:
if __name__ == "__main__":
   app = QApplication(sys.argv)
   window = MainWindow()
   #window.showFullScreen()
   window.show()
   sys.exit(app.exec_())

Son el final de una clase o q? porque no puedo poner codigo debajo, quise poner una nueva funcion pero deja de funcionar la parte de arriba?

Usaste alguna vez OMXPlayer?

Muchas gracias!
Responder
#7
Hola nuevamente! Efectivamente el problema es que en Python 2 (versión con la cual probé el código) la operación de división siempre retorna un entero, olvidándose de los decimales. En cambio, en Python 3 por defecto la división retorna un número de coma flotante, y produce el efecto que mostraste. Para evitar esto, simplemente utilizamos // en lugar de /.

Código:
def from_milliseconds(ms):
    x = ms // 1000
    seconds = x % 60
    x //= 60
    minutes = x % 60
    x //= 60
    hours = x % 24
    return "{:02}:{:02}:{:02}".format(hours, minutes, seconds)

Con respecto a las últimas líneas, eso es más bien el "punto de entrada" de la aplicación (__name__ es igual a "__main__" cuando el archivo es ejecutado y no importado). Es posible definir funciones u otros objetos luego debajo de él, pero no serán visibles para todo el código anterior.

Lamentablemente nunca utilicé OMXPlayer y mi experiencia con Raspberry Pi es nula Sad.

Un saludo.
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder
#8
Funciono todo de manera impecable!!!

Muchas gracias Francisco! 

Abz!
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)