Skip to content

Commit

Permalink
refactor: impl hover and click animation of SiPushButton based on pai…
Browse files Browse the repository at this point in the history
…ntEvent
  • Loading branch information
ChinaIceF committed Oct 25, 2024
1 parent b845f62 commit fc3324e
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
SiTitledWidgetGroup,
SiWidget,
)
from siui.components.button import SiPushButtonRefactor
from siui.components.combobox import SiComboBox
from siui.components.menu import SiMenu
from siui.components.page import SiPage
Expand Down Expand Up @@ -258,6 +259,11 @@ def __init__(self, *args, **kwargs):
container_push_buttons = SiDenseHContainer(self)
container_push_buttons.setFixedHeight(32)

# self.debug_new_button = SiPushButtonRefactor(self)
# self.debug_new_button.resize(128, 32)
# self.debug_new_button.setText("新按钮")
# self.debug_new_button.setHint("我是工具提示")

self.demo_push_button_normal = SiPushButton(self)
self.demo_push_button_normal.resize(128, 32)
self.demo_push_button_normal.attachment().setText("普通按钮")
Expand All @@ -271,6 +277,7 @@ def __init__(self, *args, **kwargs):
self.demo_push_button_long_press.resize(128, 32)
self.demo_push_button_long_press.attachment().setText("长按按钮")

# container_push_buttons.addWidget(self.debug_new_button)
container_push_buttons.addWidget(self.demo_push_button_normal)
container_push_buttons.addWidget(self.demo_push_button_transition)
container_push_buttons.addWidget(self.demo_push_button_long_press)
Expand Down
136 changes: 136 additions & 0 deletions examples/Gallery for siui/test_new_button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# NOTE This is the refactor of button component. It's working in progress. It will
# replace button once it's done. Now it's draft, code may be ugly and verbose temporarily.
from __future__ import annotations

import os

from PyQt5.QtCore import QRect, QRectF, Qt
from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPaintEvent
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

from siui.core import GlobalFont, SiColor, SiExpAnimation
from siui.gui import SiFont

os.environ["QT_SCALE_FACTOR"] = str(2)


class SiPushButton(QPushButton):
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent)

self.idle_color = SiColor.toArray("#00FFFFFF")
self.hover_color = SiColor.toArray("#10FFFFFF")
self.click_color = SiColor.toArray("#40FFFFFF")

self.animation = SiExpAnimation(self)
self.animation.setFactor(1/8)
self.animation.setBias(0.2)
self.animation.setTarget(self.idle_color)
self.animation.setCurrent(self.idle_color)
self.animation.ticked.connect(self.animate)

self.clicked.connect(self._onButtonClicked)

@classmethod
def withText(cls, text: str, parent: QWidget | None = None) -> "SiPushButton":
cls = cls(parent)
cls.setText(text)
return cls

@classmethod
def withIcon(cls, icon: QIcon, parent: QWidget | None = None) -> "SiPushButton":
cls = cls(parent)
cls.setIcon(icon)
return cls

def withTextAndIcon(cls, text: str, icon: str, parent: QWidget | None = None) -> "SiPushButton":
cls = cls(parent)
cls.setText(text)
cls.setIcon(icon)
return cls

@property
def bottomBorderHeight(self) -> int:
return round(3)

@staticmethod
def _drawBackgroundPath(rect: QRect) -> QPainterPath:
path = QPainterPath()
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height()), 4, 4)
return path

def _drawBackgroundRect(self, painter: QPainter, rect: QRect) -> None:
painter.setBrush(QColor("#2D2932"))
painter.drawPath(self._drawBackgroundPath(rect))

def _drawButtonPath(self, rect: QRect) -> QPainterPath:
path = QPainterPath()
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height() - self.bottomBorderHeight), 3, 3)
return path

def _drawButtonRect(self, painter: QPainter, rect: QRect) -> None:
painter.setBrush(QColor("#4C4554"))
painter.drawPath(self._drawButtonPath(rect))

def _drawHighLightRect(self, painter: QPainter, rect: QRect) -> None:
painter.setBrush(QColor(SiColor.toCode(self.animation.current_)))
painter.drawPath(self._drawButtonPath(rect))

def _onButtonClicked(self) -> None:
self.animation.setCurrent(self.click_color)
self.animation.start()

def animate(self, _) -> None:
self.update()

def enterEvent(self, event) -> None:
super().enterEvent(event)
self.animation.setTarget(self.hover_color)
self.animation.start()

def leaveEvent(self, event) -> None:
super().leaveEvent(event)
self.animation.setTarget(self.idle_color)
self.animation.start()

def resizeEvent(self, event) -> None:
super().resizeEvent(event)

def paintEvent(self, event: QPaintEvent) -> None:
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setRenderHint(QPainter.TextAntialiasing)

painter.setPen(Qt.PenStyle.NoPen)
rect = self.rect()
self._drawBackgroundRect(painter, rect)
self._drawButtonRect(painter, rect)
self._drawHighLightRect(painter, rect)

text_rect = QRect(0, 0, self.width(), self.height() - 4)
painter.setPen(QColor(239, 239, 239)) # 设置文本颜色
painter.setFont(self.font()) # 设置字体和大小
painter.drawText(text_rect, Qt.AlignCenter, self.text()) # 在按钮中心绘制文本
painter.end()


class Window(QWidget):
def __init__(self) -> None:
super().__init__()
self.resize(600, 800)
self.setStyleSheet("background-color: #332E38")

self.btn = SiPushButton(self)
self.btn.setFixedSize(128, 64)
self.btn.setFont(SiFont.tokenized(GlobalFont.S_NORMAL))
self.btn.setText("你好")

self.main_layout = QVBoxLayout(self)
self.main_layout.addWidget(self.btn, alignment=Qt.AlignmentFlag.AlignCenter)


if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
app.exec()
64 changes: 57 additions & 7 deletions siui/components/button.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
# NOTE This is the refactor of button component. It's working in progress. It will
# replace button once it's done. Now it's draft, code may be ugly and verbose temporarily.
from __future__ import annotations

import os

from PyQt5.QtCore import QRect, QRectF, Qt
from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPaintEvent
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

from siui.core import GlobalFont, SiColor, SiExpAnimation
from siui.gui import SiFont

os.environ["QT_SCALE_FACTOR"] = str(2)


class SiPushButton(QPushButton):
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent)

self.idle_color = SiColor.toArray("#00FFFFFF")
self.hover_color = SiColor.toArray("#10FFFFFF")
self.click_color = SiColor.toArray("#40FFFFFF")

self.animation = SiExpAnimation(self)
self.animation.setFactor(1/8)
self.animation.setBias(0.2)
self.animation.setTarget(self.idle_color)
self.animation.setCurrent(self.idle_color)
self.animation.ticked.connect(self.animate)

self.clicked.connect(self._onButtonClicked)

@classmethod
def withText(cls, text: str, parent: QWidget | None = None) -> "SiPushButton":
cls = cls(parent)
Expand All @@ -30,12 +51,12 @@ def withTextAndIcon(cls, text: str, icon: str, parent: QWidget | None = None) ->

@property
def bottomBorderHeight(self) -> int:
return round(self.rect().height() * 0.125)
return round(3)

@staticmethod
def _drawBackgroundPath(rect: QRect) -> QPainterPath:
path = QPainterPath()
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height()), 40, 40)
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height()), 4, 4)
return path

def _drawBackgroundRect(self, painter: QPainter, rect: QRect) -> None:
Expand All @@ -44,37 +65,66 @@ def _drawBackgroundRect(self, painter: QPainter, rect: QRect) -> None:

def _drawButtonPath(self, rect: QRect) -> QPainterPath:
path = QPainterPath()
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height() - self.bottomBorderHeight), 40, 40)
path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height() - self.bottomBorderHeight), 3, 3)
return path

def _drawBbuttonRect(self, painter: QPainter, rect: QRect) -> None:
def _drawButtonRect(self, painter: QPainter, rect: QRect) -> None:
painter.setBrush(QColor("#4C4554"))
painter.drawPath(self._drawButtonPath(rect))

def _drawHighLightRect(self, painter: QPainter, rect: QRect) -> None:
painter.setBrush(QColor(SiColor.toCode(self.animation.current_)))
painter.drawPath(self._drawButtonPath(rect))

def _onButtonClicked(self) -> None:
self.animation.setCurrent(self.click_color)
self.animation.start()

def animate(self, _) -> None:
self.update()

def enterEvent(self, event) -> None:
super().enterEvent(event)
self.animation.setTarget(self.hover_color)
self.animation.start()

def leaveEvent(self, event) -> None:
super().leaveEvent(event)
self.animation.setTarget(self.idle_color)
self.animation.start()

def resizeEvent(self, event) -> None:
super().resizeEvent(event)

def paintEvent(self, event: QPaintEvent) -> None:
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setRenderHint(QPainter.TextAntialiasing)

painter.setPen(Qt.PenStyle.NoPen)
rect = self.rect()
self._drawBackgroundRect(painter, rect)
self._drawBbuttonRect(painter, rect)
self._drawButtonRect(painter, rect)
self._drawHighLightRect(painter, rect)

text_rect = QRect(0, 0, self.width(), self.height() - 4)
painter.setPen(QColor(239, 239, 239)) # 设置文本颜色
painter.setFont(self.font()) # 设置字体和大小
painter.drawText(text_rect, Qt.AlignCenter, self.text()) # 在按钮中心绘制文本
painter.end()


class Window(QWidget):
def __init__(self) -> None:
super().__init__()
self.resize(600, 800)
self.setStyleSheet("background-color: #332E38")

self.btn = SiPushButton(self)
self.btn.setFixedSize(2400, 320)
self.btn.setFixedSize(128, 64)
self.btn.setFont(SiFont.tokenized(GlobalFont.S_NORMAL))
self.btn.setText("你好")

self.main_layout = QVBoxLayout(self)
self.main_layout.addWidget(self.btn, alignment=Qt.AlignmentFlag.AlignCenter)

Expand All @@ -83,4 +133,4 @@ def __init__(self) -> None:
app = QApplication([])
window = Window()
window.show()
app.exec()
app.exec()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from siui.components import SiDenseVContainer, SiLabel
from siui.components.widgets.expands import SiVExpandWidget
from siui.core import Si, SiColor, SiExpAccelerateAnimation
from siui.core import Si, SiColor, SiExpAccelerateAnimation, SiQuickEffect, SiGlobal
from siui.gui import SiFont

from ..layer import SiLayer
Expand All @@ -15,8 +15,8 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.background = SiLabel(self)
self.background.setFixedStyleSheet("border-radius: 8px")
self.background.setColor(SiColor.trans(self.getColor(SiColor.INTERFACE_BG_A), 0.8))
self.background.setFixedStyleSheet("border-radius: 12px")
self.background.setColor(SiColor.trans(self.getColor(SiColor.INTERFACE_BG_A), 0.9))

def resizeEvent(self, event):
super().resizeEvent(event)
Expand All @@ -37,8 +37,8 @@ def __init__(self, *args, **kwargs):
self.title.setSiliconWidgetFlag(Si.AdjustSizeOnTextChanged)

self.subtitle = SiLabel(self)
self.subtitle.setFont(SiFont.getFont(size=16, weight=QFont.Weight.Normal))
self.subtitle.setTextColor(self.getColor(SiColor.TEXT_D))
self.subtitle.setFont(SiFont.getFont(size=15, weight=QFont.Weight.DemiBold))
self.subtitle.setTextColor(self.getColor(SiColor.TEXT_THEME))
self.subtitle.setAlignment(Qt.AlignCenter)
self.subtitle.setFixedHeight(16)
self.subtitle.setSiliconWidgetFlag(Si.AdjustSizeOnTextChanged)
Expand All @@ -50,7 +50,7 @@ def __init__(self, *args, **kwargs):

self.tip = SiLabel(self)
self.tip.setFont(SiFont.getFont(size=12, weight=QFont.Weight.Normal))
self.tip.setTextColor(self.getColor(SiColor.TEXT_D))
self.tip.setTextColor(self.getColor(SiColor.TEXT_C))
self.tip.setAlignment(Qt.AlignCenter)
self.tip.setFixedHeight(16)
self.tip.setSiliconWidgetFlag(Si.AdjustSizeOnTextChanged)
Expand Down Expand Up @@ -90,17 +90,22 @@ def on_opacity_changed(self, opacity):
effect.setOpacity(opacity)
self.setGraphicsEffect(effect)

def fadeIn(self):
def emerge(self):
self.expandTo(1)
self.setOpacityTo(1)
self.animation_opacity.start()

def fadeOut(self):
self.fade_out_timer.start()

def resizeEvent(self, event):
super().resizeEvent(event)

def setContent(self, title, subtitle, tip, emerge=True):
self.title.setText(title)
self.subtitle.setText(subtitle)
self.tip.setText(tip)
if emerge:
self.emerge()


class LayerOverLays(SiLayer):
def __init__(self, *args, **kwargs):
Expand All @@ -113,9 +118,10 @@ def __init__(self, *args, **kwargs):

def resizeEvent(self, event):
super().resizeEvent(event)
self.state_change_overlay.move((self.width() - self.state_change_overlay.width()) // 2, int((self.height() - self.state_change_overlay.height()) * 0.785))
self.state_change_overlay.fadeIn()
self.state_change_overlay.fadeOut()
self.state_change_overlay.title.setText("设置窗口大小")
self.state_change_overlay.tip.setText("无快捷键")
self.state_change_overlay.subtitle.setText(f"{self.width()}x{self.height()}")
self.state_change_overlay.move((self.width() - self.state_change_overlay.width()) // 2,
int((self.height() - self.state_change_overlay.height()) * 0.785))

self.state_change_overlay.setContent(
"设置窗口大小", f"{self.width()}×{self.height()}", "无快捷键"
)

0 comments on commit fc3324e

Please sign in to comment.