diff --git a/examples/Gallery for siui/components/page_icons/page_icons.py b/examples/Gallery for siui/components/page_icons/page_icons.py index 35432ce..da21341 100644 --- a/examples/Gallery for siui/components/page_icons/page_icons.py +++ b/examples/Gallery for siui/components/page_icons/page_icons.py @@ -14,7 +14,6 @@ from siui.components.combobox import SiComboBox from siui.components.page import SiPage from siui.core.color import SiColor -from siui.core.effect import SiQuickEffect from siui.core.globals import SiGlobal from siui.core.silicon import Si @@ -68,7 +67,6 @@ def __init__(self, *args, **kwargs): self.package_operation_container = SiDenseHContainer(self) self.package_operation_container.setFixedHeight(48) self.package_operation_container.setAlignment(Qt.AlignCenter) - # SiQuickEffect.applyDropShadowOn(self.package_operation_container, (0, 0, 0, 80), blur_radius=32) self.package_selection_description = SiLabel(self) self.package_selection_description.setStyleSheet(f"color: {self.colorGroup().fromToken(SiColor.TEXT_D)}") diff --git a/siui/components/widgets/container.py b/siui/components/widgets/container.py index 5e8c696..e4f1d7c 100644 --- a/siui/components/widgets/container.py +++ b/siui/components/widgets/container.py @@ -1,14 +1,17 @@ import random -import time from typing import Union from PyQt5.Qt import QColor from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtWidgets import QGraphicsDropShadowEffect +from PyQt5.QtWidgets import QGraphicsDropShadowEffect, QWidget -from siui.components.widgets.label import SiLabel from siui.components.widgets.abstracts.container import ABCSiDividedContainer from siui.components.widgets.abstracts.widget import SiWidget +from siui.components.widgets.label import SiLabel + + +class PlaceHolderWidget(QWidget): + pass class ABCDenseContainer(SiWidget): @@ -76,6 +79,14 @@ def setSpacing(self, spacing: int): def widgets(self): raise NotImplementedError() + @staticmethod + def get_widget_except_placeholders(widgets): + no_placeholders = [] + for widget in widgets: + if isinstance(widget, PlaceHolderWidget) is False: + no_placeholders.append(widget) + return no_placeholders + def __enter__(self): return self @@ -102,7 +113,7 @@ def addPlaceholder(self, length, side="left", index=10000): :param index: 插入位置 :return: """ - new_label = SiLabel(self) + new_label = PlaceHolderWidget(self) new_label.setVisible(False) new_label.resize(length, 0) self.addWidget(new_label, side=side, index=index) @@ -140,13 +151,14 @@ def getSpareSpace(self): def widgets(self, side=None): if side is None: - return self.widgets_left + self.widgets_right + widgets = self.widgets_left + self.widgets_right elif side == "left": - return self.widgets_left + widgets = self.widgets_left elif side == "right": - return self.widgets_right + widgets = self.widgets_right else: raise ValueError(f"Unexpected side: {side}") + return self.get_widget_except_placeholders(widgets) def removeWidget(self, widget): """ @@ -287,7 +299,7 @@ def addPlaceholder(self, length, side="top", index=10000): :param index: 插入位置 :return: """ - new_label = SiLabel(self) + new_label = PlaceHolderWidget(self) new_label.setVisible(False) new_label.resize(0, length) self.addWidget(new_label, side=side, index=index) @@ -349,13 +361,14 @@ def getSpareSpace(self): def widgets(self, side=None): if side is None: - return self.widgets_top + self.widgets_bottom + widgets = self.widgets_top + self.widgets_bottom elif side == "top": - return self.widgets_top + widgets = self.widgets_top elif side == "bottom": - return self.widgets_bottom + widgets = self.widgets_bottom else: raise ValueError(f"Unexpected side: {side}") + return self.get_widget_except_placeholders(widgets) def removeWidget(self, widget): """ diff --git a/siui/components/widgets/scrollarea.py b/siui/components/widgets/scrollarea.py index c0cfd3d..ae87bcd 100644 --- a/siui/components/widgets/scrollarea.py +++ b/siui/components/widgets/scrollarea.py @@ -1,6 +1,6 @@ from siui.components.widgets.abstracts.widget import SiWidget from siui.components.widgets.label import SiDraggableLabel, SiLabel -from siui.core.animation import SiExpAnimation +from siui.core.animation import SiExpAnimation, SiExpAccelerateAnimation from siui.core.globals import SiGlobal from siui.core.silicon import Si @@ -31,7 +31,7 @@ def __init__(self, *args, **kwargs): self.scroll_bar_horizontal.dragged.connect(self._scroll_horizontal_handler) # 定义滚动动画,为了让所有控件都能用上滚动动画 - self.widget_scroll_animation = SiExpAnimation(self) + self.widget_scroll_animation = SiExpAccelerateAnimation(self) self.widget_scroll_animation.setFactor(1/9) self.widget_scroll_animation.setBias(1) self.widget_scroll_animation.setCurrent([0, 0]) diff --git a/siui/core/animation/__init__.py b/siui/core/animation/__init__.py index 27bcf0b..d855e65 100644 --- a/siui/core/animation/__init__.py +++ b/siui/core/animation/__init__.py @@ -1,5 +1,5 @@ from siui.core.animation import abstract -from siui.core.animation.animation import SiCounterAnimation, SiExpAnimation # noqa: F401 +from siui.core.animation.animation import SiCounterAnimation, SiExpAccelerateAnimation, SiExpAnimation # noqa: F401 from siui.core.animation.group import SiAnimationGroup # noqa: F401 diff --git a/siui/core/animation/animation.py b/siui/core/animation/animation.py index b384651..e86c5da 100644 --- a/siui/core/animation/animation.py +++ b/siui/core/animation/animation.py @@ -1,12 +1,10 @@ import numpy -import siui.core.animation.abstract as abstract +from .abstract import ABCSiAnimation, Curve -class SiExpAnimation(abstract.ABCSiAnimation): - """ - 级数动画类,每次动画的进行步长都与当前进度有关 - """ +class SiExpAnimation(ABCSiAnimation): + """ 级数动画类,每次动画的进行步长都与当前进度有关 """ def __init__(self, parent=None): super().__init__(parent) @@ -18,7 +16,6 @@ def setFactor(self, factor: float): """ Set the factor of the animation. :param factor: number between 0 and 1 - :return: """ self.factor = factor @@ -26,7 +23,6 @@ def setBias(self, bias: float): """ Set the factor of the animation. :param bias: positive float number - :return: """ if bias <= 0: raise ValueError(f"Bias is expected to be positive but met {bias}") @@ -44,10 +40,7 @@ def _step_length(self): return arr def isCompleted(self): - """ - To check whether we meet the point that the animation should stop - :return: bool - """ + """ To check whether we meet the point that the animation should stop """ return (self._distance() == 0).all() def _process(self): @@ -66,14 +59,90 @@ def _process(self): self.ticked.emit(self.current_) -class SiCounterAnimation(abstract.ABCSiAnimation): +class SiExpAccelerateAnimation(SiExpAnimation): + def __init__(self, parent=None): + super().__init__(parent) + + self.accelerate_function = lambda x: x ** 1.6 + self.step_length_bound = 0 + self.frame_counter = 0 + + def setAccelerateFunction(self, function): + self.accelerate_function = function + + def setStepLengthBound(self, bound): + self.step_length_bound = bound + + def refreshStepLengthBound(self): + self.setStepLengthBound(min(self.accelerate_function(self.frame_counter), 10000)) # prevent getting too large + + def _step_length(self): + dis = self._distance() + if (abs(dis) <= self.bias).all() is True: + return dis + + cut = numpy.array(abs(dis) <= self.bias, dtype="int8") + arr = numpy.clip(abs(dis) * self.factor + self.bias, 0, self.step_length_bound) # 基本指数动画运算 + arr = arr * (numpy.array(dis > 0, dtype="int8") * 2 - 1) # 确定动画方向 + arr = arr * (1 - cut) + dis * cut # 对于差距小于偏置的项,直接返回差距 + return arr + + def _process(self): + self.frame_counter += 1 + self.refreshStepLengthBound() + super()._process() + + def stop(self, delay=None): + super().stop(delay) + self.frame_counter = 0 + self.refreshStepLengthBound() + + +class SiSqrExpAnimation(ABCSiAnimation): + def __init__(self, parent=None): + super().__init__(parent) + + self.mean_rate = 0.5 + self.base = 1/2 + self.peak = 10 + self.bias = 1 + raise NotImplementedError() + + def setMeanRate(self, mean_rate): + self.mean_rate = mean_rate + + def setBase(self, base): + self.base = base + + def setPeak(self, peak): + self.peak = peak + + def setBias(self, bias): + self.bias = bias + + def _step_length(self): + dis = self._distance() + if (abs(dis) <= self.bias).all() is True: + return dis + + cut = numpy.array(abs(dis) <= self.bias, dtype="int8") + arr = abs(dis) * self.factor + self.bias # 基本指数动画运算 + arr = arr * (numpy.array(dis > 0, dtype="int8") * 2 - 1) # 确定动画方向 + arr = arr * (1 - cut) + dis * cut # 对于差距小于偏置的项,直接返回差距 + return arr + + def isCompleted(self): + return (self._distance() == 0).all() + + +class SiCounterAnimation(ABCSiAnimation): def __init__(self, parent=None): super().__init__(parent) self.duration = 1000 # 动画总时长,单位毫秒 self.reversed = False # 是否倒序运行动画 self.counter_addend = self._get_addend() - self.curve = abstract.Curve.LINEAR + self.curve = Curve.LINEAR def setReversed(self, reversed_): """