0

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
        }
    }
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • where is this button ? Maybe you have indentions and code is inside event loop so it run it only when there is button_press event ? – furas Oct 12 '19 at 09:32
  • @furas The buttons themselves are in a qml file. If anything, I lost not a complete code – Ground_Gamer Oct 12 '19 at 09:35
  • I don't understand your code but Qt should have `QTimer` or similar class to run some function periodically and it would check music and update progressbar periodically - even without thread. But I'm not sure if `QTimer` doesn't use thread. – furas Oct 12 '19 at 09:42
  • 1
    @Ground_Gamer provide a [MRE] – eyllanesc Oct 12 '19 at 17:09

2 Answers2

0

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
    }
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
0
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

allvolload
  • 21
  • 2
  • Where is the "total amount of progress"? Maybe add an explanation to your answer: (1) What your code does? Loop from 0 to 100 with `for i in range(101)` then each iteration increases the progress-value with `self.pbar.setValue(i)`. – hc_dev Dec 09 '22 at 10:47