307 lines
9.2 KiB
Python
307 lines
9.2 KiB
Python
|
|
# coding:utf-8
|
||
|
|
from enum import Enum
|
||
|
|
|
||
|
|
from PySide6.QtCore import Qt, QSize, QRectF, QPoint
|
||
|
|
from PySide6.QtGui import QPainter, QPainterPath, QColor
|
||
|
|
from PySide6.QtWidgets import (QSpinBox, QDoubleSpinBox, QToolButton, QHBoxLayout,
|
||
|
|
QDateEdit, QDateTimeEdit, QTimeEdit, QVBoxLayout, QApplication)
|
||
|
|
|
||
|
|
from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme
|
||
|
|
from ...common.icon import FluentIconBase, Theme, getIconColor
|
||
|
|
from ...common.font import setFont
|
||
|
|
from ...common.color import FluentSystemColor, autoFallbackThemeColor
|
||
|
|
from .button import TransparentToolButton
|
||
|
|
from .line_edit import LineEditMenu
|
||
|
|
from .flyout import Flyout, FlyoutViewBase, FlyoutAnimationType
|
||
|
|
|
||
|
|
|
||
|
|
class SpinIcon(FluentIconBase, Enum):
|
||
|
|
""" Spin icon """
|
||
|
|
|
||
|
|
UP = "Up"
|
||
|
|
DOWN = "Down"
|
||
|
|
|
||
|
|
def path(self, theme=Theme.AUTO):
|
||
|
|
return f':/qfluentwidgets/images/spin_box/{self.value}_{getIconColor(theme)}.svg'
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class SpinButton(QToolButton):
|
||
|
|
|
||
|
|
def __init__(self, icon: SpinIcon, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.isPressed = False
|
||
|
|
self._icon = icon
|
||
|
|
self.setFixedSize(31, 23)
|
||
|
|
self.setIconSize(QSize(10, 10))
|
||
|
|
FluentStyleSheet.SPIN_BOX.apply(self)
|
||
|
|
|
||
|
|
def mousePressEvent(self, e):
|
||
|
|
self.isPressed = True
|
||
|
|
super().mousePressEvent(e)
|
||
|
|
|
||
|
|
def mouseReleaseEvent(self, e):
|
||
|
|
self.isPressed = False
|
||
|
|
super().mouseReleaseEvent(e)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
super().paintEvent(e)
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing |
|
||
|
|
QPainter.SmoothPixmapTransform)
|
||
|
|
|
||
|
|
if not self.isEnabled():
|
||
|
|
painter.setOpacity(0.36)
|
||
|
|
elif self.isPressed:
|
||
|
|
painter.setOpacity(0.7)
|
||
|
|
|
||
|
|
self._icon.render(painter, QRectF(10, 6.5, 11, 11))
|
||
|
|
|
||
|
|
|
||
|
|
class CompactSpinButton(QToolButton):
|
||
|
|
""" Compact spin button """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.setFixedSize(26, 33)
|
||
|
|
self.setCursor(Qt.IBeamCursor)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
|
||
|
|
x = (self.width() - 10) / 2
|
||
|
|
s = 9
|
||
|
|
|
||
|
|
SpinIcon.UP.render(painter, QRectF(x, self.height() / 2 - s + 1, s, s))
|
||
|
|
SpinIcon.DOWN.render(painter, QRectF(x, self.height() / 2 , s, s))
|
||
|
|
|
||
|
|
|
||
|
|
class SpinFlyoutView(FlyoutViewBase):
|
||
|
|
""" Spin flyout view """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.upButton = TransparentToolButton(SpinIcon.UP, self)
|
||
|
|
self.downButton = TransparentToolButton(SpinIcon.DOWN, self)
|
||
|
|
self.vBoxLayout = QVBoxLayout(self)
|
||
|
|
|
||
|
|
self.upButton.setFixedSize(36, 36)
|
||
|
|
self.downButton.setFixedSize(36, 36)
|
||
|
|
self.upButton.setIconSize(QSize(13, 13))
|
||
|
|
self.downButton.setIconSize(QSize(13, 13))
|
||
|
|
|
||
|
|
self.vBoxLayout.setContentsMargins(6, 6, 6, 6)
|
||
|
|
self.vBoxLayout.setSpacing(0)
|
||
|
|
self.vBoxLayout.addWidget(self.upButton)
|
||
|
|
self.vBoxLayout.addWidget(self.downButton)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
|
||
|
|
painter.setBrush(
|
||
|
|
QColor(46, 46, 46) if isDarkTheme() else QColor(249, 249, 249))
|
||
|
|
painter.setPen(
|
||
|
|
QColor(0, 0, 0, 51) if isDarkTheme() else QColor(0, 0, 0, 15))
|
||
|
|
|
||
|
|
rect = self.rect().adjusted(1, 1, -1, -1)
|
||
|
|
painter.drawRoundedRect(rect, 8, 8)
|
||
|
|
|
||
|
|
|
||
|
|
class SpinBoxBase:
|
||
|
|
""" Spin box ui """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self._isError = False
|
||
|
|
self.lightFocusedBorderColor = QColor()
|
||
|
|
self.darkFocusedBorderColor = QColor()
|
||
|
|
|
||
|
|
self.hBoxLayout = QHBoxLayout(self)
|
||
|
|
|
||
|
|
self.setProperty('transparent', True)
|
||
|
|
FluentStyleSheet.SPIN_BOX.apply(self)
|
||
|
|
self.setButtonSymbols(QSpinBox.NoButtons)
|
||
|
|
self.setFixedHeight(33)
|
||
|
|
setFont(self)
|
||
|
|
|
||
|
|
self.setAttribute(Qt.WA_MacShowFocusRect, False)
|
||
|
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||
|
|
self.customContextMenuRequested.connect(self._showContextMenu)
|
||
|
|
|
||
|
|
def isError(self):
|
||
|
|
return self._isError
|
||
|
|
|
||
|
|
def setError(self, isError: bool):
|
||
|
|
""" set the error status """
|
||
|
|
if isError == self.isError():
|
||
|
|
return
|
||
|
|
|
||
|
|
self._isError = isError
|
||
|
|
self.update()
|
||
|
|
|
||
|
|
def setReadOnly(self, isReadOnly: bool):
|
||
|
|
super().setReadOnly(isReadOnly)
|
||
|
|
self.setSymbolVisible(not isReadOnly)
|
||
|
|
|
||
|
|
def setSymbolVisible(self, isVisible: bool):
|
||
|
|
""" set whether the spin symbol is visible """
|
||
|
|
self.setProperty("symbolVisible", isVisible)
|
||
|
|
self.setStyle(QApplication.style())
|
||
|
|
|
||
|
|
def setCustomFocusedBorderColor(self, light, dark):
|
||
|
|
""" set the border color in focused status
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
light, dark: str | QColor | Qt.GlobalColor
|
||
|
|
border color in light/dark theme mode
|
||
|
|
"""
|
||
|
|
self.lightFocusedBorderColor = QColor(light)
|
||
|
|
self.darkFocusedBorderColor = QColor(dark)
|
||
|
|
self.update()
|
||
|
|
|
||
|
|
def focusedBorderColor(self):
|
||
|
|
if self.isError():
|
||
|
|
return FluentSystemColor.CRITICAL_FOREGROUND.color()
|
||
|
|
|
||
|
|
return autoFallbackThemeColor(self.lightFocusedBorderColor, self.darkFocusedBorderColor)
|
||
|
|
|
||
|
|
def _showContextMenu(self, pos):
|
||
|
|
menu = LineEditMenu(self.lineEdit())
|
||
|
|
menu.exec_(self.mapToGlobal(pos))
|
||
|
|
|
||
|
|
def _drawBorderBottom(self):
|
||
|
|
if not self.hasFocus():
|
||
|
|
return
|
||
|
|
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
painter.setPen(Qt.NoPen)
|
||
|
|
|
||
|
|
path = QPainterPath()
|
||
|
|
w, h = self.width(), self.height()
|
||
|
|
path.addRoundedRect(QRectF(0, h-10, w, 10), 5, 5)
|
||
|
|
|
||
|
|
rectPath = QPainterPath()
|
||
|
|
rectPath.addRect(0, h-10, w, 8)
|
||
|
|
path = path.subtracted(rectPath)
|
||
|
|
|
||
|
|
painter.fillPath(path, self.focusedBorderColor())
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
super().paintEvent(e)
|
||
|
|
self._drawBorderBottom()
|
||
|
|
|
||
|
|
|
||
|
|
class InlineSpinBoxBase(SpinBoxBase):
|
||
|
|
""" Inline spin box base """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.upButton = SpinButton(SpinIcon.UP, self)
|
||
|
|
self.downButton = SpinButton(SpinIcon.DOWN, self)
|
||
|
|
|
||
|
|
self.hBoxLayout.setContentsMargins(0, 4, 4, 4)
|
||
|
|
self.hBoxLayout.setSpacing(5)
|
||
|
|
self.hBoxLayout.addWidget(self.upButton, 0, Qt.AlignRight)
|
||
|
|
self.hBoxLayout.addWidget(self.downButton, 0, Qt.AlignRight)
|
||
|
|
self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||
|
|
|
||
|
|
self.upButton.clicked.connect(self.stepUp)
|
||
|
|
self.downButton.clicked.connect(self.stepDown)
|
||
|
|
|
||
|
|
def setSymbolVisible(self, isVisible: bool):
|
||
|
|
super().setSymbolVisible(isVisible)
|
||
|
|
self.upButton.setVisible(isVisible)
|
||
|
|
self.downButton.setVisible(isVisible)
|
||
|
|
|
||
|
|
def setAccelerated(self, on: bool):
|
||
|
|
super().setAccelerated(on)
|
||
|
|
self.upButton.setAutoRepeat(on)
|
||
|
|
self.downButton.setAutoRepeat(on)
|
||
|
|
|
||
|
|
|
||
|
|
class CompactSpinBoxBase(SpinBoxBase):
|
||
|
|
""" Compact spin box base """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.compactSpinButton = CompactSpinButton(self)
|
||
|
|
self.spinFlyoutView = SpinFlyoutView(self)
|
||
|
|
self.spinFlyout = Flyout(self.spinFlyoutView, self, False)
|
||
|
|
|
||
|
|
self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
|
||
|
|
self.hBoxLayout.addWidget(self.compactSpinButton, 0, Qt.AlignRight)
|
||
|
|
self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||
|
|
|
||
|
|
self.compactSpinButton.clicked.connect(self._showFlyout)
|
||
|
|
self.spinFlyoutView.upButton.clicked.connect(self.stepUp)
|
||
|
|
self.spinFlyoutView.downButton.clicked.connect(self.stepDown)
|
||
|
|
|
||
|
|
self.spinFlyout.hide()
|
||
|
|
|
||
|
|
def setAccelerated(self, on: bool):
|
||
|
|
super().setAccelerated(on)
|
||
|
|
self.spinFlyoutView.upButton.setAutoRepeat(on)
|
||
|
|
self.spinFlyoutView.downButton.setAutoRepeat(on)
|
||
|
|
|
||
|
|
def focusInEvent(self, e):
|
||
|
|
super().focusInEvent(e)
|
||
|
|
self._showFlyout()
|
||
|
|
|
||
|
|
def setSymbolVisible(self, isVisible: bool):
|
||
|
|
super().setSymbolVisible(isVisible)
|
||
|
|
self.compactSpinButton.setVisible(isVisible)
|
||
|
|
|
||
|
|
def _showFlyout(self):
|
||
|
|
if self.spinFlyout.isVisible() or self.isReadOnly():
|
||
|
|
return
|
||
|
|
|
||
|
|
y = int(self.compactSpinButton.height() / 2 - 46)
|
||
|
|
pos = self.compactSpinButton.mapToGlobal(QPoint(-12, y))
|
||
|
|
|
||
|
|
self.spinFlyout.exec(pos, FlyoutAnimationType.FADE_IN)
|
||
|
|
|
||
|
|
|
||
|
|
class SpinBox(InlineSpinBoxBase, QSpinBox):
|
||
|
|
""" Spin box """
|
||
|
|
|
||
|
|
|
||
|
|
class CompactSpinBox(CompactSpinBoxBase, QSpinBox):
|
||
|
|
""" Compact spin box """
|
||
|
|
|
||
|
|
|
||
|
|
class DoubleSpinBox(InlineSpinBoxBase, QDoubleSpinBox):
|
||
|
|
""" Double spin box """
|
||
|
|
|
||
|
|
|
||
|
|
class CompactDoubleSpinBox(CompactSpinBoxBase, QDoubleSpinBox):
|
||
|
|
""" Compact double spin box """
|
||
|
|
|
||
|
|
|
||
|
|
class TimeEdit(InlineSpinBoxBase, QTimeEdit):
|
||
|
|
""" Time edit """
|
||
|
|
|
||
|
|
|
||
|
|
class CompactTimeEdit(CompactSpinBoxBase, QTimeEdit):
|
||
|
|
""" Compact time edit """
|
||
|
|
|
||
|
|
|
||
|
|
class DateTimeEdit(InlineSpinBoxBase, QDateTimeEdit):
|
||
|
|
""" Date time edit """
|
||
|
|
|
||
|
|
|
||
|
|
class CompactDateTimeEdit(CompactSpinBoxBase, QDateTimeEdit):
|
||
|
|
""" Compact date time edit """
|
||
|
|
|
||
|
|
|
||
|
|
class DateEdit(InlineSpinBoxBase, QDateEdit):
|
||
|
|
""" Date edit """
|
||
|
|
|
||
|
|
|
||
|
|
class CompactDateEdit(CompactSpinBoxBase, QDateEdit):
|
||
|
|
""" Compact date edit """
|
||
|
|
|