Source code for qtframework.widgets.buttons

"""Button widget components."""

from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING

from PySide6.QtCore import Property, QSize, Qt, Signal
from PySide6.QtWidgets import QPushButton


if TYPE_CHECKING:
    from PySide6.QtGui import QIcon
    from PySide6.QtWidgets import QWidget


[docs] class ButtonVariant(Enum): """Button variant styles.""" PRIMARY = "primary" SECONDARY = "secondary" SUCCESS = "success" WARNING = "warning" DANGER = "danger" INFO = "info" OUTLINE = "outline" TEXT = "text"
[docs] class ButtonSize(Enum): """Button size options. COMPACT: Matches input field height, ideal for buttons next to QLineEdit/QComboBox SMALL: Small standalone button (28px height) MEDIUM: Default button size (36px height) LARGE: Large emphasis button (44px height) """ COMPACT = "compact" SMALL = "small" MEDIUM = "medium" LARGE = "large"
[docs] class Button(QPushButton): """Enhanced button widget.""" variant_changed = Signal(str) size_changed = Signal(str) def __init__( self, text: str = "", parent: QWidget | None = None, *, variant: ButtonVariant = ButtonVariant.PRIMARY, size: ButtonSize = ButtonSize.MEDIUM, icon: QIcon | None = None, object_name: str | None = None, ) -> None: """Initialize button. Args: text: Button text parent: Parent widget variant: Button variant size: Button size icon: Button icon object_name: Object name for styling """ super().__init__(text, parent) if object_name: self.setObjectName(object_name) self._variant = variant self._size = size if icon: from PySide6.QtGui import QIcon as QIconClass if isinstance(icon, QIconClass): self.setIcon(icon) self._apply_variant() self._apply_size() def _get_variant(self) -> str: """Get button variant. Returns: Variant name """ return self._variant.value def _set_variant(self, value: str) -> None: """Set button variant. Args: value: Variant name """ try: variant = ButtonVariant(value) if self._variant != variant: self._variant = variant self._apply_variant() self.variant_changed.emit(value) except ValueError: pass variant = Property(str, _get_variant, _set_variant) def _get_size(self) -> str: """Get button size. Returns: Size name """ return self._size.value def _set_size(self, value: str) -> None: """Set button size. Args: value: Size name """ try: size = ButtonSize(value) if self._size != size: self._size = size self._apply_size() self.size_changed.emit(value) except ValueError: pass buttonSize = Property(str, _get_size, _set_size) # noqa: N815 def _apply_variant(self) -> None: """Apply variant styling.""" self.setProperty("variant", self._variant.value) self.style().unpolish(self) self.style().polish(self) self.update() def _apply_size(self) -> None: """Apply size styling.""" self.setProperty("size", self._size.value) if self._size == ButtonSize.COMPACT: # Match input field height with minimal padding # Use size constraints instead of setStyleSheet to preserve theme styling self.setMinimumWidth(60) self.setMinimumHeight(0) self.setMaximumHeight(22) # Very compact to fit in menubar else: size_map = { ButtonSize.SMALL: (80, 28), ButtonSize.MEDIUM: (100, 36), ButtonSize.LARGE: (120, 44), } min_width, height = size_map[self._size] self.setMinimumWidth(min_width) self.setFixedHeight(height) self.style().unpolish(self) self.style().polish(self) self.update()
[docs] class IconButton(QPushButton): """Icon-only button widget.""" def __init__( self, icon: QIcon, parent: QWidget | None = None, *, size: QSize | None = None, tooltip: str = "", object_name: str | None = None, ) -> None: """Initialize icon button. Args: icon: Button icon parent: Parent widget size: Icon size tooltip: Tooltip text object_name: Object name for styling """ super().__init__(parent) if object_name: self.setObjectName(object_name) self.setIcon(icon) self.setFlat(True) size = size or QSize(32, 32) self.setIconSize(size) self.setFixedSize(size.width() + 8, size.height() + 8) if tooltip: self.setToolTip(tooltip) self.setCursor(Qt.CursorShape.PointingHandCursor) self.setProperty("class", "icon-button")
[docs] class CloseButton(QPushButton): """Standardized close button widget.""" def __init__( self, parent: QWidget | None = None, *, size: int = 20, style: str = "default", ) -> None: """Initialize close button. Args: parent: Parent widget size: Button size in pixels style: Button style ('default', 'light', 'dark') """ super().__init__("×", parent) self.setFixedSize(size, size) self.setFlat(True) self.setCursor(Qt.CursorShape.PointingHandCursor) self.setProperty("class", "close-button") self.setProperty("style", style) # Apply inline styles based on style parameter # Style will be applied through theme system # Keep minimal styles that won't conflict with theme self.setStyleSheet(""" QPushButton { border: none; font-size: 16px; font-weight: bold; background: transparent; border-radius: 10px; padding: 5px; } """)
# default style will use theme stylesheet
[docs] class ToggleButton(QPushButton): """Toggle button widget.""" toggled_on = Signal() toggled_off = Signal() def __init__( self, text: str = "", parent: QWidget | None = None, *, checked: bool = False, on_text: str | None = None, off_text: str | None = None, object_name: str | None = None, ) -> None: """Initialize toggle button. Args: text: Button text parent: Parent widget checked: Initial checked state on_text: Text when toggled on off_text: Text when toggled off object_name: Object name for styling """ super().__init__(text, parent) if object_name: self.setObjectName(object_name) self._on_text = on_text or text self._off_text = off_text or text self.setCheckable(True) self.toggled.connect(self._on_toggled) self.setProperty("class", "toggle-button") self.setChecked(checked) # Set checked last, after attributes are initialized def _on_toggled(self, checked: bool) -> None: """Handle toggle state change. Args: checked: New checked state """ self._update_text() if checked: self.toggled_on.emit() else: self.toggled_off.emit() def _update_text(self) -> None: """Update button text based on state.""" if self.isChecked(): self.setText(self._on_text) else: self.setText(self._off_text)
[docs] def setChecked(self, checked: bool) -> None: """Set checked state. Args: checked: Checked state """ super().setChecked(checked) self._update_text()