initial fluent-widgets ui

This commit is contained in:
2025-08-14 18:45:16 +08:00
parent 746e83ab23
commit 4c66886257
1198 changed files with 805339 additions and 0 deletions

View File

@ -0,0 +1,319 @@
# coding:utf-8
from PySide6.QtCore import Qt, Signal, QSize, QPropertyAnimation, QPoint
from PySide6.QtGui import QPixmap, QPainter, QColor
from PySide6.QtWidgets import QWidget, QGraphicsOpacityEffect, QHBoxLayout, QVBoxLayout
from ..common.icon import FluentIcon
from ..common.style_sheet import isDarkTheme, FluentStyleSheet
from ..components.widgets.button import TransparentToolButton
from ..components.widgets.tool_tip import ToolTipFilter
from ..components.widgets.slider import Slider
from ..components.widgets.label import CaptionLabel
from ..components.widgets.flyout import Flyout, FlyoutViewBase, PullUpFlyoutAnimationManager
from .media_player import MediaPlayer, MediaPlayerBase
class MediaPlayBarButton(TransparentToolButton):
""" Media play bar button """
def _postInit(self):
super()._postInit()
self.installEventFilter(ToolTipFilter(self, 1000))
self.setFixedSize(30, 30)
self.setIconSize(QSize(16, 16))
class PlayButton(MediaPlayBarButton):
""" Play button """
def _postInit(self):
super()._postInit()
self.setIconSize(QSize(14, 14))
self.setPlay(False)
def setPlay(self, isPlay: bool):
if isPlay:
self.setIcon(FluentIcon.PAUSE_BOLD)
self.setToolTip(self.tr('Pause'))
else:
self.setIcon(FluentIcon.PLAY_SOLID)
self.setToolTip(self.tr('Play'))
class VolumeView(FlyoutViewBase):
""" Volume view """
def __init__(self, parent=None):
super().__init__(parent)
self.muteButton = MediaPlayBarButton(FluentIcon.VOLUME, self)
self.volumeSlider = Slider(Qt.Horizontal, self)
self.volumeLabel = CaptionLabel('30', self)
self.volumeSlider.setRange(0, 100)
self.volumeSlider.setFixedWidth(208)
self.setFixedSize(295, 64)
h = self.height()
self.muteButton.move(10, h//2-self.muteButton.height()//2)
self.volumeSlider.move(45, 21)
def setMuted(self, isMute: bool):
if isMute:
self.muteButton.setIcon(FluentIcon.MUTE)
self.muteButton.setToolTip(self.tr('Unmute'))
else:
self.muteButton.setIcon(FluentIcon.VOLUME)
self.muteButton.setToolTip(self.tr('Mute'))
def setVolume(self, volume: int):
self.volumeSlider.setValue(volume)
self.volumeLabel.setNum(volume)
self.volumeLabel.adjustSize()
tr = self.volumeLabel.fontMetrics().boundingRect(str(volume))
self.volumeLabel.move(self.width() - 20 - tr.width(), self.height()//2 - tr.height()//2)
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
if isDarkTheme():
painter.setBrush(QColor(46, 46, 46))
painter.setPen(QColor(0, 0, 0, 20))
else:
painter.setBrush(QColor(248, 248, 248))
painter.setPen(QColor(0, 0, 0, 10))
painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 8, 8)
class VolumeButton(MediaPlayBarButton):
""" Volume button """
volumeChanged = Signal(int)
mutedChanged = Signal(bool)
def _postInit(self):
super()._postInit()
self.volumeView = VolumeView(self)
self.volumeFlyout = Flyout(self.volumeView, self.window(), False)
self.setMuted(False)
self.volumeFlyout.hide()
self.volumeView.muteButton.clicked.connect(lambda: self.mutedChanged.emit(not self.isMuted))
self.volumeView.volumeSlider.valueChanged.connect(self.volumeChanged)
self.clicked.connect(self._showVolumeFlyout)
def setMuted(self, isMute: bool):
self.isMuted = isMute
self.volumeView.setMuted(isMute)
if isMute:
self.setIcon(FluentIcon.MUTE)
else:
self.setIcon(FluentIcon.VOLUME)
def setVolume(self, volume: int):
self.volumeView.setVolume(volume)
def _showVolumeFlyout(self):
if self.volumeFlyout.isVisible():
return
pos = PullUpFlyoutAnimationManager(self.volumeFlyout).position(self)
self.volumeFlyout.exec(pos)
class MediaPlayBarBase(QWidget):
""" Play bar base class """
def __init__(self, parent=None):
super().__init__(parent=parent)
self.player = None # type: MediaPlayerBase
self.playButton = PlayButton(self)
self.volumeButton = VolumeButton(self)
self.progressSlider = Slider(Qt.Horizontal, self)
self.opacityEffect = QGraphicsOpacityEffect(self)
self.opacityAni = QPropertyAnimation(self.opacityEffect, b'opacity')
self.opacityEffect.setOpacity(1)
self.opacityAni.setDuration(250)
self.setGraphicsEffect(self.opacityEffect)
FluentStyleSheet.MEDIA_PLAYER.apply(self)
self.playButton.clicked.connect(self.togglePlayState)
def setMediaPlayer(self, player: MediaPlayerBase):
""" set media player """
self.player = player
self.player.durationChanged.connect(self.progressSlider.setMaximum)
self.player.positionChanged.connect(self._onPositionChanged)
self.player.mediaStatusChanged.connect(self._onMediaStatusChanged)
self.player.volumeChanged.connect(self.volumeButton.setVolume)
self.player.mutedChanged.connect(self.volumeButton.setMuted)
# don't use valueChanged, otherwise the sound will be interupted
self.progressSlider.sliderMoved.connect(self.player.setPosition)
self.progressSlider.clicked.connect(self.player.setPosition)
self.volumeButton.volumeChanged.connect(self.player.setVolume)
self.volumeButton.mutedChanged.connect(self.player.setMuted)
self.player.setVolume(30)
def fadeIn(self):
self.opacityAni.setStartValue(self.opacityEffect.opacity())
self.opacityAni.setEndValue(1)
self.opacityAni.start()
def fadeOut(self):
self.opacityAni.setStartValue(self.opacityEffect.opacity())
self.opacityAni.setEndValue(0)
self.opacityAni.start()
def play(self):
self.player.play()
def pause(self):
self.player.pause()
def stop(self):
self.player.stop()
def setVolume(self, volume: int):
""" Sets the volume of player """
self.player.setVolume(volume)
def setPosition(self, position: int):
""" Sets the position of media in ms """
self.player.setPosition(position)
def _onPositionChanged(self, position: int):
self.progressSlider.setValue(position)
def _onMediaStatusChanged(self, status):
self.playButton.setPlay(self.player.isPlaying())
def togglePlayState(self):
""" toggle the play state of media player """
if self.player.isPlaying():
self.player.pause()
else:
self.player.play()
self.playButton.setPlay(self.player.isPlaying())
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
if isDarkTheme():
painter.setBrush(QColor(46, 46, 46))
painter.setPen(QColor(0, 0, 0, 20))
else:
painter.setBrush(QColor(248, 248, 248))
painter.setPen(QColor(0, 0, 0, 10))
painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 8, 8)
class SimpleMediaPlayBar(MediaPlayBarBase):
""" simple media play bar """
def __init__(self, parent=None):
super().__init__(parent)
self.hBoxLayout = QHBoxLayout(self)
self.hBoxLayout.setContentsMargins(10, 4, 10, 4)
self.hBoxLayout.setSpacing(6)
self.hBoxLayout.addWidget(self.playButton, 0, Qt.AlignLeft)
self.hBoxLayout.addWidget(self.progressSlider, 1)
self.hBoxLayout.addWidget(self.volumeButton, 0)
self.setFixedHeight(48)
self.setMediaPlayer(MediaPlayer(self))
def addButton(self, button: MediaPlayBarButton):
""" add button to the right side of play bar """
self.hBoxLayout.addWidget(button, 0)
class StandardMediaPlayBar(MediaPlayBarBase):
""" Standard media play bar """
def __init__(self, parent=None):
super().__init__(parent)
self.vBoxLayout = QVBoxLayout(self)
self.timeLayout = QHBoxLayout()
self.buttonLayout = QHBoxLayout()
self.leftButtonContainer = QWidget()
self.centerButtonContainer = QWidget()
self.rightButtonContainer = QWidget()
self.leftButtonLayout = QHBoxLayout(self.leftButtonContainer)
self.centerButtonLayout = QHBoxLayout(self.centerButtonContainer)
self.rightButtonLayout = QHBoxLayout(self.rightButtonContainer)
self.skipBackButton = MediaPlayBarButton(FluentIcon.SKIP_BACK, self)
self.skipForwardButton = MediaPlayBarButton(FluentIcon.SKIP_FORWARD, self)
self.currentTimeLabel = CaptionLabel('0:00:00', self)
self.remainTimeLabel = CaptionLabel('0:00:00', self)
self.__initWidgets()
def __initWidgets(self):
self.setFixedHeight(102)
self.vBoxLayout.setSpacing(6)
self.vBoxLayout.setContentsMargins(5, 9, 5, 9)
self.vBoxLayout.addWidget(self.progressSlider, 1, Qt.AlignTop)
self.vBoxLayout.addLayout(self.timeLayout)
self.timeLayout.setContentsMargins(10, 0, 10, 0)
self.timeLayout.addWidget(self.currentTimeLabel, 0, Qt.AlignLeft)
self.timeLayout.addWidget(self.remainTimeLabel, 0, Qt.AlignRight)
self.vBoxLayout.addStretch(1)
self.vBoxLayout.addLayout(self.buttonLayout, 1)
self.buttonLayout.setContentsMargins(0, 0, 0, 0)
self.leftButtonLayout.setContentsMargins(4, 0, 0, 0)
self.centerButtonLayout.setContentsMargins(0, 0, 0, 0)
self.rightButtonLayout.setContentsMargins(0, 0, 4, 0)
self.leftButtonLayout.addWidget(self.volumeButton, 0, Qt.AlignLeft)
self.centerButtonLayout.addWidget(self.skipBackButton)
self.centerButtonLayout.addWidget(self.playButton)
self.centerButtonLayout.addWidget(self.skipForwardButton)
self.buttonLayout.addWidget(self.leftButtonContainer, 0, Qt.AlignLeft)
self.buttonLayout.addWidget(self.centerButtonContainer, 0, Qt.AlignHCenter)
self.buttonLayout.addWidget(self.rightButtonContainer, 0, Qt.AlignRight)
self.setMediaPlayer(MediaPlayer(self))
self.skipBackButton.clicked.connect(lambda: self.skipBack(10000))
self.skipForwardButton.clicked.connect(lambda: self.skipForward(30000))
def skipBack(self, ms: int):
""" Back up for specified milliseconds """
self.player.setPosition(self.player.position()-ms)
def skipForward(self, ms: int):
""" Fast forward specified milliseconds """
self.player.setPosition(self.player.position()+ms)
def _onPositionChanged(self, position: int):
super()._onPositionChanged(position)
self.currentTimeLabel.setText(self._formatTime(position))
self.remainTimeLabel.setText(self._formatTime(self.player.duration() - position))
def _formatTime(self, time: int):
time = int(time / 1000)
s = time % 60
m = int(time / 60) % 60
h = int(time / 3600)
return f'{h}:{m:02}:{s:02}'