Faced with the problem of updating progressbar in Qt in python

Question:

Write player, and emerged question, when I launching song, I want to progressbar was updated in the time when goes music, made cycle, threw his in thread, values on renewal are transmitted through signal in qml, but in than the problem, these values are transmitted only then when I click on this button, but raze not in real time.

Main.py

progressMusicSignal = Signal(float, arguments=['progressMusic'])

@Slot('float')
def setValue(self, flagTrue):
    global thread, que
    if flagTrue == 1:
        que = queue.Queue()
        thread = Thread(target=lambda ques, arg1: ques.put(progressBarMusic(arg1)), args=(que, flagTrue),
                                daemon=True)
        thread.start()
        result = que.get()
        self.progressMusicSignal.emit(result)
    elif flagTrue == 2:
        thread.join()

def playMusic(flagMusic=0):
    if flagMusic == 1:
        pygame.mixer.music.load(PATHLESS + MUSICFILEWAV)
        pygame.mixer.music.play()
    if flagMusic == 2:
        pygame.mixer.music.pause()
    if flagMusic == 3:
        pygame.mixer.music.unpause()

def progressBarMusic(flagTrue):
    if flagTrue == 1:
        while True:
            song = pygame.mixer.Sound(PATHLESS + MUSICFILEWAV)
            getLengthMusic = pygame.mixer.Sound.get_length(song)
            milSec = pygame.mixer.music.get_pos()
            operationLength = getLengthMusic // 10
            print(operationLength)
            sec = milSec // 1000
            secRes = milSec // 100
            print(secRes)
            operationSecPercent = (secRes / operationLength) / 100
            print(operationSecPercent)
            if sec != getLengthMusic:
                return operationSecPercent      

Main.qml

RoundButton {
    id: plauPauseBtn
    x: 370
    y: 15
    width: 50
    height: 50
    text: "u25b7"
    enabled: true
    opacity: 1.0
    font.weight: Font.ExtraBold
    font.capitalization: Font.MixedCase
    font.strikeout: false
    font.underline: false
    font.italic: false
    display: AbstractButton.TextBesideIcon
    font.bold: false
    font.pointSize: 14
    font.family: "Tahoma"
    onClicked: {
        plauPauseBtn.opacity = 0.0;
        plauPauseBtn.enabled = false;
        stopPauseBtn.opacity = 1.0;
        stopPauseBtn.enabled = true;
        con.playMusicInt(1)
        con.setValue(1)
    }
}

RoundButton {
    id: stopPauseBtn
    x: 370
    y: 15
    width: 50
    height: 50
    text: "||"
    enabled: false
    opacity: 0.0
    bottomPadding: 13
    font.weight: Font.ExtraBold
    font.capitalization: Font.MixedCase
    font.strikeout: false
    font.underline: false
    font.italic: false
    display: AbstractButton.TextBesideIcon
    font.bold: false
    font.pointSize: 7
    font.family: "Tahoma"
    onClicked: {
        con.playMusicInt(2)
        con.setValue(2)
        stopPauseBtn.opacity = 0.0;
        stopPauseBtn.enabled = false;
        playAgainBtn.opacity = 1.0;
        playAgainBtn.enabled = true;
    }
}

    RoundButton {
        id: playAgainBtn
        x: 370
        y: 15
        width: 50
        height: 50
        text: "u25b7"
        enabled: false
        opacity: 0.0
        bottomPadding: 13
        font.weight: Font.ExtraBold
        font.capitalization: Font.MixedCase
        font.strikeout: false
        font.underline: false
        font.italic: false
        display: AbstractButton.TextBesideIcon
        font.bold: false
        font.pointSize: 14
        font.family: "Tahoma"
        onClicked: {
            con.playMusicInt(3)
            con.setValue(1)
            playAgainBtn.opacity = 0.0;
            playAgainBtn.enabled = false;
            stopPauseBtn.opacity = 1.0;
            stopPauseBtn.enabled = true;
        }
    }

    ProgressBar {
        id: musicProgressBar
        x: 0
        y: 0
        width: 800
        height: 5
        indeterminate: false
        value: 0.0
    }
        Connections {
            target: con
            onProgressMusicSignal: {
                musicProgressBar.value = progressMusic
        }
    }
Asked By: Ground_Gamer

||

Answers:

The code provided by the OP is understandable, so I will avoid analyzing it, so I will propose a solution from scratch.

In this case I have created a wrapper on pygame.mixer.music that exposes the properties of the source, the volume, the current state and has methods exposed through pyqtSlot, that class does not handle the logic of your application but is only a resource .

The logic of your application must be handled in QML regarding the state of the button, and in that case it is not necessary to create several buttons since only one in which you change the text is enough.

Considering the above, the solution is:

main.py

import os
import math

import pygame

from PyQt5 import QtCore, QtGui, QtQml


class PyGameSound(QtCore.QObject):
    sourceChanged = QtCore.pyqtSignal()
    volumeChanged = QtCore.pyqtSignal()
    stateChanged = QtCore.pyqtSignal()
    notifyIntervalChanged = QtCore.pyqtSignal()
    progressChanged = QtCore.pyqtSignal()

    error = QtCore.pyqtSignal(str, arguments=["message"])

    class State:
        PlayingState, PausedState, StoppedState = range(3)

    QtCore.Q_ENUMS(State)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.destroyed.connect(self.on_destroyed)
        pygame.mixer.init()

        self._source = ""
        self._notifyInterval = 1000
        self._progress = 0.0
        self._volume = 1.0

        self._notify_timer = QtCore.QTimer(self, timeout=self.on_notify_callback)

        self._state = PyGameSound.State.StoppedState


    @QtCore.pyqtProperty(State, notify=stateChanged)
    def state(self):
        return self._state

    def _update_state(self, state):
        self._state = state
        self.stateChanged.emit()

    def on_notify_callback(self):
        if self.source:
            try:
                song = pygame.mixer.Sound(self.source)
                total = song.get_length()
                pos = pygame.mixer.music.get_pos()
                if pos >= 0:
                    percentage = pos / (total * 1000.0)
                    if math.isclose(
                        percentage, 1.0, abs_tol=self.notifyInterval / 1000.0
                    ):
                        percentage = 1.0
                    self.progress = percentage
            except pygame.error as message:
                self.error.emit(str(message))

    @QtCore.pyqtProperty(str, notify=sourceChanged)
    def source(self):
        return self._source

    @source.setter
    def source(self, source):
        try:
            pygame.mixer.music.load(source)
        except pygame.error as message:
            self.error.emit(str(message))
            source = ""
        if self._source != source:
            self._source = source
            self.sourceChanged.emit()

    @QtCore.pyqtProperty(float, notify=volumeChanged)
    def volume(self):
        return pygame.mixer.music.get_volume()

    @volume.setter
    def volume(self, volume):
        pygame.mixer.music.set_volume(volume)
        self.volumeChanged.emit()

    @QtCore.pyqtProperty(int, notify=notifyIntervalChanged)
    def notifyInterval(self):
        return self._notifyInterval

    @notifyInterval.setter
    def notifyInterval(self, interval):
        if self._notifyInterval != interval:
            self._notifyInterval = interval
            is_active = self._notify_timer.isActive()
            if is_active:
                self._notify_timer.stop()
            self._notify_timer.setInterval(self._notifyInterval)
            if is_active:
                self._notify_timer.start()

    @QtCore.pyqtProperty(float, notify=progressChanged)
    def progress(self):
        return self._progress

    @progress.setter
    def progress(self, progress):
        self._progress = progress
        self.progressChanged.emit()

    @QtCore.pyqtSlot()
    def play(self):
        try:
            pygame.mixer.music.play()
            self._notify_timer.start()
        except pygame.error as message:
            self.error.emit(str(message))
            return
        self._update_state(PyGameSound.State.PlayingState)

    @QtCore.pyqtSlot()
    def unpause(self):
        pygame.mixer.music.unpause()
        self._notify_timer.start()
        self._update_state(PyGameSound.State.PlayingState)

    @QtCore.pyqtSlot()
    def pause(self):
        pygame.mixer.music.pause()
        self._notify_timer.stop()
        self._update_state(PyGameSound.State.PausedState)

    @QtCore.pyqtSlot()
    def stop(self):
        pygame.mixer.music.stop()
        self._notify_timer.stop()
        self._update_state(PyGameSound.State.StoppedState)

    def on_destroyed(self):
        pygame.mixer.quit()


if __name__ == "__main__":
    import sys

    current_dir = os.path.dirname(os.path.realpath(__file__))

    QtQml.qmlRegisterType(PyGameSound, "PyGame", 1, 0, "PyGameSound")

    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()

    filename = os.path.join(current_dir, "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.5

import PyGame 1.0

ApplicationWindow{
    visible: true
    width: 640
    height: 480

    PyGameSound{
        id: sound
        notifyInterval: 10
        source: "/path/of/music.wav"
        volume: 1.0
        onError: console.log(message)
    }

    RoundButton {
        id: play_pause_button
        x: 370
        y: 15
        width: 50
        height: 50
        text: "u25b7"
        display: AbstractButton.TextBesideIcon

        font {
            weight: Font.ExtraBold
            capitalization: Font.MixedCase
            strikeout: false
            pointSize: 14
            family: "Tahoma"
            bold: false
            underline: false
            italic: false
        }
        onClicked: {
            if(sound.state == PyGameSound.StoppedState){
                sound.play()
                play_pause_button.text = "||"
            }
            else if(sound.state == PyGameSound.PlayingState){
                sound.pause()
                play_pause_button.text = "u25b7"
            }
            else if(sound.state == PyGameSound.PausedState){
                sound.unpause()
                play_pause_button.text = "||"
            }
        }
    }

    ProgressBar {
        id: musicProgressBar
        width: parent.width
        height: 5
        indeterminate: false
        value: sound.progress
    }
}

Although the simplest solution is to use the Audio module:

from PyQt5 import QtCore, QtGui, QtQml


if __name__ == "__main__":
    import os
    import sys

    current_dir = os.path.dirname(os.path.realpath(__file__))
    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()

    filename = os.path.join(current_dir, "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.5
import QtMultimedia 5.13

ApplicationWindow{
    visible: true
    width: 640
    height: 480
    Audio{
        id: sound
        notifyInterval: 10
        source: "/path/of/music.wav"
    }

    RoundButton {
        id: play_pause_button
        x: 370
        y: 15
        width: 50
        height: 50
        text: "u25b7"
        display: AbstractButton.TextBesideIcon

        font {
            weight: Font.ExtraBold
            capitalization: Font.MixedCase
            strikeout: false
            pointSize: 14
            family: "Tahoma"
            bold: false
            underline: false
            italic: false
        }
        onClicked: {
            if(sound.playbackState == Audio.StoppedState){
                sound.play()
                play_pause_button.text = "||"
            }
            else if(sound.playbackState == Audio.PlayingState){
                sound.pause()
                play_pause_button.text = "u25b7"
            }
            else if(sound.playbackState == Audio.PausedState){
                sound.play()
                play_pause_button.text = "||"
            }
        }
    }

    ProgressBar {
        id: musicProgressBar
        width: parent.width
        height: 5
        indeterminate: false
        value: sound.position/sound.duration
    }
}
Answered By: eyllanesc
from PyQt5.QtWidgets import * 
from PyQt5.QtGui import * 
from PyQt5.QtCore import * 
import sys
import time

class Example(QWidget):

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

    # calling initUI method
    self.initUI()

# method for creating widgets
def initUI(self):

    # creating progress bar
    self.pbar = QProgressBar(self)

    # setting its geometry
    self.pbar.setGeometry(30, 40, 200, 25)

    # creating push button
    self.btn = QPushButton('Start', self)

    # changing its position
    self.btn.move(40, 80)

    # adding action to push button
    self.btn.clicked.connect(self.doAction)

    # setting window geometry
    self.setGeometry(300, 300, 280, 170)

    # setting window action
    self.setWindowTitle("Python")

    # showing all the widgets
    self.show()

# when button is pressed this method is being called
def doAction(self):

    # setting for loop to set value of progress bar
    for i in range(101):

        # slowing down the loop
        time.sleep(0.05)

        # setting value to progress bar
        self.pbar.setValue(i)

# main method
if __name__ == '__main__':
  
  
# create pyqt5 app
App = QApplication(sys.argv)

# create the instance of our Window
window = Example()

# start the app
sys.exit(App.exec())

I have not found a code where the "amount" of progress would be known and could be filled in as a proportion of the total, but it is also possible at the end of each part to simply +1 to the total amount of progress

Answered By: allvolload
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.