CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyqt-fluent-widgets

A fluent design widgets library based on PyQt5 providing modern Windows 11-style UI components

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

multimedia.mddocs/

Multimedia Components

Media player widgets, video display, and audio/video control components with modern playback interfaces. These components provide comprehensive multimedia functionality with fluent design integration.

Capabilities

Media Player

Core media player widget with comprehensive playback controls and modern interface design.

class MediaPlayer(QWidget):
    def __init__(self, parent=None): ...
    def setMedia(self, media: Union[str, QUrl, QMediaContent]): ...
    def media(self) -> QMediaContent: ...
    def play(self): ...
    def pause(self): ...
    def stop(self): ...
    def setPosition(self, position: int): ...
    def position(self) -> int: ...
    def setVolume(self, volume: int): ...
    def volume(self) -> int: ...
    def duration(self) -> int: ...
    def state(self) -> QMediaPlayer.State: ...
    
    # Signals
    positionChanged = pyqtSignal(int)
    durationChanged = pyqtSignal(int)
    stateChanged = pyqtSignal(QMediaPlayer.State)
    volumeChanged = pyqtSignal(int)

class MediaPlayerBase(QWidget):
    def __init__(self, parent=None): ...
    def setMediaPlayer(self, player: QMediaPlayer): ...
    def mediaPlayer(self) -> QMediaPlayer: ...

Usage Example:

from qfluentwidgets import MediaPlayer
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl

# Create media player
media_player = MediaPlayer(self)
media_player.setFixedSize(600, 400)

# Load media file
media_file = "path/to/video.mp4"
media_player.setMedia(media_file)

# Connect to signals
media_player.positionChanged.connect(self.on_position_changed)
media_player.durationChanged.connect(self.on_duration_changed)
media_player.stateChanged.connect(self.on_state_changed)

def on_position_changed(self, position):
    # Update progress slider
    progress_slider.setValue(position)

def on_duration_changed(self, duration):
    # Set slider range
    progress_slider.setRange(0, duration)
    duration_label.setText(self.format_time(duration))

def on_state_changed(self, state):
    if state == QMediaPlayer.PlayingState:
        play_button.setText("Pause")
    else:
        play_button.setText("Play")

# Control playback
play_button.clicked.connect(media_player.play)
pause_button.clicked.connect(media_player.pause)
stop_button.clicked.connect(media_player.stop)

Video Display

Dedicated video display widget for rendering video content with proper aspect ratio handling.

class VideoWidget(QVideoWidget):
    def __init__(self, parent=None): ...
    def setAspectRatioMode(self, mode: Qt.AspectRatioMode): ...
    def aspectRatioMode(self) -> Qt.AspectRatioMode: ...
    def setFullScreen(self, fullScreen: bool): ...
    def isFullScreen(self) -> bool: ...
    def sizeHint(self) -> QSize: ...

Usage Example:

from qfluentwidgets import VideoWidget, MediaPlayer
from PyQt5.QtCore import Qt

# Create video display
video_widget = VideoWidget(self)
video_widget.setAspectRatioMode(Qt.KeepAspectRatio)
video_widget.setMinimumSize(320, 240)

# Create media player and connect to video widget
player = QMediaPlayer(self)
player.setVideoOutput(video_widget)

# Create media player UI
media_player = MediaPlayer(self)
media_player.setMediaPlayer(player)

# Layout video and controls
layout = QVBoxLayout(self)
layout.addWidget(video_widget, 1)  # Video takes most space
layout.addWidget(media_player, 0)  # Controls at bottom

# Fullscreen toggle
def toggle_fullscreen():
    video_widget.setFullScreen(not video_widget.isFullScreen())

fullscreen_btn.clicked.connect(toggle_fullscreen)

# Handle escape key to exit fullscreen
def keyPressEvent(self, event):
    if event.key() == Qt.Key_Escape and video_widget.isFullScreen():
        video_widget.setFullScreen(False)
    super().keyPressEvent(event)

Media Control Bars

Specialized control bars with playback buttons, progress tracking, and volume controls.

class StandardMediaPlayBar(QWidget):
    def __init__(self, parent=None): ...
    def setMediaPlayer(self, player: QMediaPlayer): ...
    def setPlayButtonVisible(self, visible: bool): ...
    def setVolumeButtonVisible(self, visible: bool): ...
    def setProgressSliderVisible(self, visible: bool): ...
    def setTimeLabelsVisible(self, visible: bool): ...

class SimpleMediaPlayBar(QWidget):
    def __init__(self, parent=None): ...
    def setMediaPlayer(self, player: QMediaPlayer): ...
    def setCompactMode(self, compact: bool): ...

class MediaPlayBarButton(QToolButton):
    def __init__(self, parent=None): ...
    def __init__(self, icon: Union[FluentIconBase, QIcon, str], parent=None): ...
    def setPlayIcon(self, icon: Union[FluentIconBase, QIcon, str]): ...
    def setPauseIcon(self, icon: Union[FluentIconBase, QIcon, str]): ...
    def setPlaying(self, playing: bool): ...

Usage Example:

from qfluentwidgets import (StandardMediaPlayBar, SimpleMediaPlayBar, 
                           MediaPlayBarButton, FluentIcon as FIF)

# Standard media control bar
standard_bar = StandardMediaPlayBar(self)
standard_bar.setMediaPlayer(player)

# Customize visibility
standard_bar.setPlayButtonVisible(True)
standard_bar.setVolumeButtonVisible(True)
standard_bar.setProgressSliderVisible(True)
standard_bar.setTimeLabelsVisible(True)

# Simple media control bar
simple_bar = SimpleMediaPlayBar(self)
simple_bar.setMediaPlayer(player)
simple_bar.setCompactMode(True)

# Custom media buttons
play_pause_btn = MediaPlayBarButton(self)
play_pause_btn.setPlayIcon(FIF.PLAY)
play_pause_btn.setPauseIcon(FIF.PAUSE)

stop_btn = MediaPlayBarButton(FIF.STOP, self)
next_btn = MediaPlayBarButton(FIF.NEXT, self)
prev_btn = MediaPlayBarButton(FIF.PREVIOUS, self)

# Connect custom buttons
play_pause_btn.clicked.connect(self.toggle_playback)
stop_btn.clicked.connect(player.stop)
next_btn.clicked.connect(self.next_track)
prev_btn.clicked.connect(self.previous_track)

def toggle_playback(self):
    if player.state() == QMediaPlayer.PlayingState:
        player.pause()
        play_pause_btn.setPlaying(False)
    else:
        player.play()
        play_pause_btn.setPlaying(True)

Complete Media Player Example

from qfluentwidgets import *
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist
from PyQt5.QtCore import QUrl, QTimer

class FluentMediaPlayer(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi()
        self.setupMediaPlayer()
        self.setupConnections()
    
    def setupUi(self):
        # Main layout
        layout = QVBoxLayout(self)
        
        # Title bar
        title_bar = QHBoxLayout()
        self.title_label = TitleLabel("Media Player")
        self.minimize_btn = TransparentToolButton(FIF.MINIMIZE)
        self.close_btn = TransparentToolButton(FIF.CLOSE)
        
        title_bar.addWidget(self.title_label)
        title_bar.addStretch()
        title_bar.addWidget(self.minimize_btn)
        title_bar.addWidget(self.close_btn)
        
        # Video display area
        self.video_widget = VideoWidget(self)
        self.video_widget.setAspectRatioMode(Qt.KeepAspectRatio)
        self.video_widget.setMinimumSize(640, 360)
        
        # Control panel
        control_layout = QHBoxLayout()
        
        # Playback controls
        self.prev_btn = MediaPlayBarButton(FIF.PREVIOUS, self)
        self.play_pause_btn = MediaPlayBarButton(FIF.PLAY, self)
        self.stop_btn = MediaPlayBarButton(FIF.STOP, self)
        self.next_btn = MediaPlayBarButton(FIF.NEXT, self)
        
        # Progress controls
        self.position_slider = Slider(Qt.Horizontal, self)
        self.position_slider.setFixedHeight(20)
        
        # Time labels
        self.current_time_label = CaptionLabel("00:00")
        self.duration_label = CaptionLabel("00:00")
        
        # Volume controls
        self.volume_btn = MediaPlayBarButton(FIF.VOLUME, self)
        self.volume_slider = Slider(Qt.Horizontal, self)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(70)
        self.volume_slider.setFixedWidth(100)
        
        # Add to control layout
        control_layout.addWidget(self.prev_btn)
        control_layout.addWidget(self.play_pause_btn)
        control_layout.addWidget(self.stop_btn)
        control_layout.addWidget(self.next_btn)
        control_layout.addStretch()
        control_layout.addWidget(self.current_time_label)
        control_layout.addWidget(self.position_slider, 1)
        control_layout.addWidget(self.duration_label)
        control_layout.addStretch()
        control_layout.addWidget(self.volume_btn)
        control_layout.addWidget(self.volume_slider)
        
        # Playlist area
        self.playlist_widget = ListWidget(self)
        self.playlist_widget.setMaximumHeight(150)
        
        # Add to main layout
        layout.addLayout(title_bar)
        layout.addWidget(self.video_widget, 1)
        layout.addLayout(control_layout)
        layout.addWidget(self.playlist_widget)
    
    def setupMediaPlayer(self):
        # Media player
        self.player = QMediaPlayer(self)
        self.player.setVideoOutput(self.video_widget)
        
        # Playlist
        self.playlist = QMediaPlaylist(self)
        self.player.setPlaylist(self.playlist)
        
        # Position update timer
        self.position_timer = QTimer(self)
        self.position_timer.timeout.connect(self.update_position)
        self.position_timer.start(100)  # Update every 100ms
    
    def setupConnections(self):
        # Player signals
        self.player.stateChanged.connect(self.on_state_changed)
        self.player.positionChanged.connect(self.on_position_changed)
        self.player.durationChanged.connect(self.on_duration_changed)
        self.player.volumeChanged.connect(self.on_volume_changed)
        
        # Control connections
        self.play_pause_btn.clicked.connect(self.toggle_playback)
        self.stop_btn.clicked.connect(self.player.stop)
        self.prev_btn.clicked.connect(self.playlist.previous)
        self.next_btn.clicked.connect(self.playlist.next)
        
        # Slider connections
        self.position_slider.sliderPressed.connect(self.on_position_slider_pressed)
        self.position_slider.sliderReleased.connect(self.on_position_slider_released)
        self.volume_slider.valueChanged.connect(self.player.setVolume)
        
        # Playlist connections
        self.playlist_widget.itemDoubleClicked.connect(self.play_selected_item)
    
    def add_media_file(self, file_path: str):
        """Add media file to playlist"""
        media_content = QMediaContent(QUrl.fromLocalFile(file_path))
        self.playlist.addMedia(media_content)
        
        # Add to playlist widget
        file_name = os.path.basename(file_path)
        self.playlist_widget.addItem(file_name)
    
    def toggle_playback(self):
        """Toggle between play and pause"""
        if self.player.state() == QMediaPlayer.PlayingState:
            self.player.pause()
        else:
            self.player.play()
    
    def on_state_changed(self, state):
        """Handle player state changes"""
        if state == QMediaPlayer.PlayingState:
            self.play_pause_btn.setIcon(FIF.PAUSE.icon())
        else:
            self.play_pause_btn.setIcon(FIF.PLAY.icon())
    
    def on_position_changed(self, position):
        """Handle position changes"""
        if not self.position_slider.isSliderDown():
            self.position_slider.setValue(position)
        
        self.current_time_label.setText(self.format_time(position))
    
    def on_duration_changed(self, duration):
        """Handle duration changes"""
        self.position_slider.setRange(0, duration)
        self.duration_label.setText(self.format_time(duration))
    
    def on_volume_changed(self, volume):
        """Handle volume changes"""
        self.volume_slider.setValue(volume)
        
        # Update volume icon
        if volume == 0:
            self.volume_btn.setIcon(FIF.MUTE.icon())
        elif volume < 50:
            self.volume_btn.setIcon(FIF.VOLUME.icon())
        else:
            self.volume_btn.setIcon(FIF.VOLUME.icon())
    
    def on_position_slider_pressed(self):
        """Handle position slider press"""
        self.position_timer.stop()
    
    def on_position_slider_released(self):
        """Handle position slider release"""
        self.player.setPosition(self.position_slider.value())
        self.position_timer.start()
    
    def play_selected_item(self, item):
        """Play selected playlist item"""
        row = self.playlist_widget.row(item)
        self.playlist.setCurrentIndex(row)
        self.player.play()
    
    def format_time(self, milliseconds):
        """Format time in MM:SS format"""
        seconds = milliseconds // 1000
        minutes = seconds // 60
        seconds = seconds % 60
        return f"{minutes:02d}:{seconds:02d}"
    
    def update_position(self):
        """Update position display"""
        if self.player.state() == QMediaPlayer.PlayingState:
            position = self.player.position()
            if not self.position_slider.isSliderDown():
                self.position_slider.setValue(position)

# Usage
media_player = FluentMediaPlayer()
media_player.add_media_file("video1.mp4")
media_player.add_media_file("audio1.mp3")
media_player.show()

Advanced Features

Custom Media Controls

class CustomMediaControls(QWidget):
    def __init__(self, player: QMediaPlayer, parent=None):
        super().__init__(parent)
        self.player = player
        self.setupUi()
        
    def setupUi(self):
        layout = QHBoxLayout(self)
        
        # Playback speed control
        speed_combo = ComboBox(self)
        speed_combo.addItems(["0.5x", "0.75x", "1.0x", "1.25x", "1.5x", "2.0x"])
        speed_combo.setCurrentText("1.0x")
        speed_combo.currentTextChanged.connect(self.change_playback_speed)
        
        # Quality selection
        quality_combo = ComboBox(self)
        quality_combo.addItems(["Auto", "1080p", "720p", "480p", "360p"])
        
        # Fullscreen button
        fullscreen_btn = ToolButton(FIF.FULL_SCREEN, self)
        fullscreen_btn.clicked.connect(self.toggle_fullscreen)
        
        layout.addWidget(QLabel("Speed:"))
        layout.addWidget(speed_combo)
        layout.addWidget(QLabel("Quality:"))
        layout.addWidget(quality_combo)
        layout.addStretch()
        layout.addWidget(fullscreen_btn)
    
    def change_playback_speed(self, speed_text):
        speed = float(speed_text.replace('x', ''))
        self.player.setPlaybackRate(speed)

Playlist Management

class PlaylistManager:
    def __init__(self, playlist: QMediaPlaylist, list_widget: ListWidget):
        self.playlist = playlist
        self.list_widget = list_widget
        self.media_files = []
    
    def add_files(self, file_paths: List[str]):
        for file_path in file_paths:
            self.add_file(file_path)
    
    def add_file(self, file_path: str):
        media = QMediaContent(QUrl.fromLocalFile(file_path))
        self.playlist.addMedia(media)
        self.media_files.append(file_path)
        
        # Add to UI
        file_name = os.path.basename(file_path)
        self.list_widget.addItem(file_name)
    
    def remove_current(self):
        current_index = self.playlist.currentIndex()
        if current_index >= 0:
            self.playlist.removeMedia(current_index)
            self.media_files.pop(current_index)
            self.list_widget.takeItem(current_index)
    
    def shuffle(self):
        self.playlist.shuffle()
    
    def set_repeat_mode(self, mode: QMediaPlaylist.PlaybackMode):
        self.playlist.setPlaybackMode(mode)

Error Handling and Validation

def handle_media_errors(self):
    """Handle media player errors"""
    self.player.error.connect(self.on_media_error)
    
def on_media_error(self, error):
    """Handle media playback errors"""
    error_messages = {
        QMediaPlayer.NoError: "No error",
        QMediaPlayer.ResourceError: "Resource error - file not found or corrupted",
        QMediaPlayer.FormatError: "Format error - unsupported media format", 
        QMediaPlayer.NetworkError: "Network error - connection failed",
        QMediaPlayer.AccessDeniedError: "Access denied - insufficient permissions",
        QMediaPlayer.ServiceMissingError: "Service missing - codec not available"
    }
    
    message = error_messages.get(error, f"Unknown error: {error}")
    
    # Show error dialog
    error_dialog = MessageBox("Media Error", message, self)
    error_dialog.exec()

Best Practices

Performance Optimization

  1. Use appropriate video resolution for display size
  2. Implement buffering indicators for network streams
  3. Handle large playlists efficiently with lazy loading
  4. Optimize UI updates during playback

User Experience

  1. Provide keyboard shortcuts for common actions
  2. Remember user preferences (volume, position, etc.)
  3. Support drag-and-drop for adding media files
  4. Implement proper fullscreen handling

Accessibility

  1. Add tooltips to all control buttons
  2. Support keyboard navigation through controls
  3. Provide audio descriptions when available
  4. Respect system accessibility settings

Install with Tessl CLI

npx tessl i tessl/pypi-pyqt-fluent-widgets

docs

buttons.md

dialog-notification.md

display-widgets.md

index.md

input-controls.md

layout-animation.md

list-view-widgets.md

material-effects.md

menu-command.md

multimedia.md

settings-config.md

theme-styling.md

window-navigation.md

tile.json