A fluent design widgets library based on PyQt5 providing modern Windows 11-style UI components
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive form controls including text inputs, combo boxes, sliders, date/time pickers, and spinboxes with fluent design styling and advanced features. All input controls provide consistent user interaction patterns, validation support, and automatic theme integration.
Single and multi-line text input widgets with modern styling and specialized variants for different input types.
class LineEdit(QLineEdit):
def __init__(self, parent=None): ...
def setPlaceholderText(self, text: str): ...
def placeholderText(self) -> str: ...
class SearchLineEdit(LineEdit):
def __init__(self, parent=None): ...
def setSearchButtonVisible(self, visible: bool): ...
searchSignal = pyqtSignal(str)
clearSignal = pyqtSignal()
class PasswordLineEdit(LineEdit):
def __init__(self, parent=None): ...
def setEchoMode(self, mode: QLineEdit.EchoMode): ...
class TextEdit(QTextEdit):
def __init__(self, parent=None): ...
def setPlaceholderText(self, text: str): ...
class PlainTextEdit(QPlainTextEdit):
def __init__(self, parent=None): ...
def setPlaceholderText(self, text: str): ...
class TextBrowser(QTextBrowser):
def __init__(self, parent=None): ...Usage Example:
from qfluentwidgets import (LineEdit, SearchLineEdit, PasswordLineEdit,
TextEdit, PlainTextEdit)
# Basic text input
name_input = LineEdit(self)
name_input.setPlaceholderText("Enter your name")
name_input.textChanged.connect(self.on_name_changed)
# Search input with search button
search_input = SearchLineEdit(self)
search_input.setPlaceholderText("Search files...")
search_input.searchSignal.connect(self.perform_search)
search_input.clearSignal.connect(self.clear_search)
# Password input
password_input = PasswordLineEdit(self)
password_input.setPlaceholderText("Password")
password_input.setEchoMode(QLineEdit.Password)
# Multi-line text
description_input = TextEdit(self)
description_input.setPlaceholderText("Enter description here...")
description_input.setMaximumHeight(120)
# Plain text editor
code_editor = PlainTextEdit(self)
code_editor.setPlaceholderText("Enter code here...")
font = QFont("Consolas", 10)
code_editor.setFont(font)Dropdown selection controls with support for editable options, custom models, and various selection patterns.
class ComboBox(QComboBox):
def __init__(self, parent=None): ...
def setPlaceholderText(self, text: str): ...
def addItems(self, items: List[str]): ...
def setCurrentIndex(self, index: int): ...
def setCurrentText(self, text: str): ...
class EditableComboBox(ComboBox):
def __init__(self, parent=None): ...
def setCompleter(self, completer: QCompleter): ...
class ModelComboBox(ComboBox):
def __init__(self, parent=None): ...
def setModel(self, model: QAbstractItemModel): ...
class EditableModelComboBox(ModelComboBox):
def __init__(self, parent=None): ...Usage Example:
from qfluentwidgets import ComboBox, EditableComboBox, ModelComboBox
from PyQt5.QtCore import QStringListModel
# Basic combo box
language_combo = ComboBox(self)
language_combo.setPlaceholderText("Select language")
languages = ["Python", "JavaScript", "Java", "C++", "C#"]
language_combo.addItems(languages)
language_combo.setCurrentIndex(-1) # No selection
language_combo.currentTextChanged.connect(self.on_language_changed)
# Editable combo box
country_combo = EditableComboBox(self)
countries = ["United States", "Canada", "United Kingdom", "Germany"]
country_combo.addItems(countries)
country_combo.setPlaceholderText("Type or select country")
# Model-based combo box
model = QStringListModel(["Item 1", "Item 2", "Item 3"])
model_combo = ModelComboBox(self)
model_combo.setModel(model)
model_combo.setPlaceholderText("Choose from model")Interactive sliders for numeric value selection with smooth animations and custom styling.
class Slider(QSlider):
def __init__(self, orientation=Qt.Horizontal, parent=None): ...
def setRange(self, min: int, max: int): ...
def setValue(self, value: int): ...
def value(self) -> int: ...
class ClickableSlider(Slider):
def __init__(self, orientation=Qt.Horizontal, parent=None): ...
# Allows clicking anywhere on slider to set value
class HollowHandleStyle:
# Custom slider handle style with hollow appearance
passUsage Example:
from qfluentwidgets import Slider, ClickableSlider
from PyQt5.QtCore import Qt
# Horizontal slider
volume_slider = Slider(Qt.Horizontal, self)
volume_slider.setRange(0, 100)
volume_slider.setValue(50)
volume_slider.valueChanged.connect(self.set_volume)
# Vertical slider
brightness_slider = Slider(Qt.Vertical, self)
brightness_slider.setRange(0, 255)
brightness_slider.setValue(128)
# Clickable slider (jump to click position)
seek_slider = ClickableSlider(Qt.Horizontal, self)
seek_slider.setRange(0, 1000)
seek_slider.sliderPressed.connect(self.start_seeking)
seek_slider.sliderReleased.connect(self.stop_seeking)Numeric input controls with increment/decrement buttons and validation.
class SpinBox(QSpinBox):
def __init__(self, parent=None): ...
def setRange(self, min: int, max: int): ...
def setSingleStep(self, step: int): ...
class DoubleSpinBox(QDoubleSpinBox):
def __init__(self, parent=None): ...
def setRange(self, min: float, max: float): ...
def setDecimals(self, decimals: int): ...
class CompactSpinBox(SpinBox):
def __init__(self, parent=None): ...
# Smaller, more compact version
class CompactDoubleSpinBox(DoubleSpinBox):
def __init__(self, parent=None): ...Usage Example:
from qfluentwidgets import SpinBox, DoubleSpinBox, CompactSpinBox
# Integer spin box
quantity_spin = SpinBox(self)
quantity_spin.setRange(1, 100)
quantity_spin.setSingleStep(1)
quantity_spin.setValue(1)
quantity_spin.valueChanged.connect(self.update_quantity)
# Double precision spin box
price_spin = DoubleSpinBox(self)
price_spin.setRange(0.0, 999999.99)
price_spin.setDecimals(2)
price_spin.setSingleStep(0.01)
price_spin.setPrefix("$")
# Compact spin box for toolbars
compact_spin = CompactSpinBox(self)
compact_spin.setRange(1, 10)
compact_spin.setValue(5)Date and time selection widgets with calendar popups and various display formats.
class DateEdit(QDateEdit):
def __init__(self, parent=None): ...
def setDate(self, date: QDate): ...
def date(self) -> QDate: ...
class TimeEdit(QTimeEdit):
def __init__(self, parent=None): ...
def setTime(self, time: QTime): ...
def time(self) -> QTime: ...
class DateTimeEdit(QDateTimeEdit):
def __init__(self, parent=None): ...
def setDateTime(self, datetime: QDateTime): ...
def dateTime(self) -> QDateTime: ...
class CompactDateEdit(DateEdit): ...
class CompactTimeEdit(TimeEdit): ...
class CompactDateTimeEdit(DateTimeEdit): ...Usage Example:
from qfluentwidgets import DateEdit, TimeEdit, DateTimeEdit
from PyQt5.QtCore import QDate, QTime, QDateTime
# Date picker
birth_date = DateEdit(self)
birth_date.setDate(QDate.currentDate())
birth_date.setDisplayFormat("yyyy-MM-dd")
birth_date.dateChanged.connect(self.on_date_changed)
# Time picker
appointment_time = TimeEdit(self)
appointment_time.setTime(QTime.currentTime())
appointment_time.setDisplayFormat("hh:mm AP")
# Date and time picker
event_datetime = DateTimeEdit(self)
event_datetime.setDateTime(QDateTime.currentDateTime())
event_datetime.setDisplayFormat("yyyy-MM-dd hh:mm")Toggle switches for boolean settings and on/off states.
class SwitchButton(QWidget):
def __init__(self, parent=None, indicatorPos=IndicatorPosition.LEFT): ...
def setChecked(self, checked: bool): ...
def isChecked(self) -> bool: ...
def setText(self, text: str): ...
def text(self) -> str: ...
checkedChanged = pyqtSignal(bool)
class IndicatorPosition(Enum):
LEFT = 0
RIGHT = 1Usage Example:
from qfluentwidgets import SwitchButton, IndicatorPosition
# Basic switch
notifications_switch = SwitchButton(self)
notifications_switch.setText("Enable Notifications")
notifications_switch.setChecked(True)
notifications_switch.checkedChanged.connect(self.toggle_notifications)
# Switch with indicator on right
auto_save_switch = SwitchButton(self, IndicatorPosition.RIGHT)
auto_save_switch.setText("Auto Save")
auto_save_switch.setChecked(False)
# Multiple switches in settings
switches = [
("Dark Mode", self.toggle_dark_mode),
("Sound Effects", self.toggle_sound),
("Auto Updates", self.toggle_updates)
]
for text, callback in switches:
switch = SwitchButton(self)
switch.setText(text)
switch.checkedChanged.connect(callback)Enhanced check boxes with fluent design styling and tri-state support.
class CheckBox(QCheckBox):
def __init__(self, parent=None): ...
def __init__(self, text: str, parent=None): ...
def setTristate(self, tristate: bool = True): ...
def isTristate(self) -> bool: ...Usage Example:
from qfluentwidgets import CheckBox
from PyQt5.QtCore import Qt
# Basic checkbox
agree_checkbox = CheckBox("I agree to the terms and conditions", self)
agree_checkbox.stateChanged.connect(self.on_agreement_changed)
# Tri-state checkbox for hierarchical selection
parent_checkbox = CheckBox("Select All", self)
parent_checkbox.setTristate(True)
parent_checkbox.stateChanged.connect(self.on_parent_selection_changed)
# Child checkboxes
child_checkboxes = []
for item in ["Item 1", "Item 2", "Item 3"]:
checkbox = CheckBox(item, self)
checkbox.stateChanged.connect(self.update_parent_state)
child_checkboxes.append(checkbox)class LineEditButton(LineEdit):
def __init__(self, parent=None): ...
def addButton(self, button: QToolButton, position: QLineEdit.ActionPosition): ...Usage Example:
from qfluentwidgets import LineEditButton, FluentIcon as FIF
from PyQt5.QtWidgets import QToolButton
# Line edit with custom buttons
search_edit = LineEditButton(self)
search_edit.setPlaceholderText("Search...")
# Add search button
search_btn = QToolButton()
search_btn.setIcon(FIF.SEARCH.icon())
search_btn.clicked.connect(self.perform_search)
search_edit.addButton(search_btn, QLineEdit.TrailingPosition)
# Add clear button
clear_btn = QToolButton()
clear_btn.setIcon(FIF.CLOSE.icon())
clear_btn.clicked.connect(search_edit.clear)
search_edit.addButton(clear_btn, QLineEdit.TrailingPosition)# Numeric validation
spin_box = SpinBox(self)
spin_box.setRange(1, 100) # Automatically validates range
# Date validation
date_edit = DateEdit(self)
date_edit.setDateRange(QDate(2020, 1, 1), QDate(2030, 12, 31))
# Text length validation
line_edit = LineEdit(self)
line_edit.setMaxLength(50)from PyQt5.QtGui import QValidator
class EmailValidator(QValidator):
def validate(self, text: str, pos: int):
if "@" in text and "." in text.split("@")[-1]:
return QValidator.Acceptable, text, pos
return QValidator.Intermediate, text, pos
# Apply custom validator
email_edit = LineEdit(self)
email_edit.setValidator(EmailValidator())# Track input changes
def setup_form_validation(self):
self.required_fields = [name_edit, email_edit, phone_edit]
for field in self.required_fields:
field.textChanged.connect(self.validate_form)
def validate_form(self):
all_valid = all(field.text().strip() for field in self.required_fields)
self.submit_button.setEnabled(all_valid)# Set tab order for form controls
self.setTabOrder(name_edit, email_edit)
self.setTabOrder(email_edit, phone_edit)
self.setTabOrder(phone_edit, submit_button)# Set accessible names and descriptions
name_edit.setAccessibleName("Full Name")
email_edit.setAccessibleDescription("Enter your email address")# Visual feedback for validation
def on_email_validation(self, is_valid: bool):
if is_valid:
email_edit.setStyleSheet("border: 2px solid green;")
else:
email_edit.setStyleSheet("border: 2px solid red;")Install with Tessl CLI
npx tessl i tessl/pypi-pyqt-fluent-widgets