pythonQt中遇到更新progressbar的问题

Faced with the problem of updating progressbar in Qt in python

写播放器,出现问题,当我播放歌曲时,我想在音乐播放时更新进度条,制作循环,将其放入线程,更新时的值通过信号在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
        }
    }

OP提供的代码是可以理解的,所以我会避免分析它,所以我会从头开始提出解决方案。

在这种情况下,我在 pygame.mixer.music 上创建了一个包装器,它公开了源的属性、卷、当前状态并具有通过 pyqtSlot 公开的方法,class 不处理您的应用程序的逻辑,但只是一种资源。

您的应用程序的逻辑必须在 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
    }
}