Столкнулся с проблемой обновления progressbar в Qt в python - PullRequest
0 голосов
/ 12 октября 2019

Написать плеер, и возник вопрос, когда я запускаю песню, я хочу чтобы прогрессбар обновлялся в то время, когда идет музыка, сделал цикл, кинул его в ветку, значения при обновлении передаются через сигнал в qml, но в чем топроблема, эти значения передаются только тогда, когда я нажимаю на эту кнопку, но никак не в реальном времени.

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
        }
    }

1 Ответ

0 голосов
/ 13 октября 2019

Код, предоставленный OP, понятен, поэтому я не буду анализировать его, поэтому я предложу решение с нуля.

В этом случае я создал оболочку для pygame.mixer.music, которая предоставляетсвойства источника, тома, текущего состояния и методов, предоставляемых через pyqtSlot, этот класс не обрабатывает логику вашего приложения, а является только ресурсом.

Логика вашего приложения должна обрабатываться вQML относительно состояния кнопки, и в этом случае нет необходимости создавать несколько кнопок, так как достаточно только одной, для которой вы изменяете текст.

Учитывая вышеизложенное, решение:

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
    }
}

Хотя самое простое решение - использовать аудиомодуль:

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
    }
}
...