diff --git a/docs/coding_standard.md b/docs/coding_standard.md new file mode 100644 index 0000000..d2ce28b --- /dev/null +++ b/docs/coding_standard.md @@ -0,0 +1,47 @@ +# 代码规范 +创建于 2024.10.25 + +为了提高可读性和可维护性,新编写的 PyQt-SiliconUI 代码将遵守以下规范。 + +## 命名规范 +### 命名法 +* 变量名采用 snake_case。 +* 方法名沿袭 PyQt 特点,采用 lowerCamelCase +* 类名采用 UpperCamelCase +* 旗标类、枚举类和常量采用全大写命名 + +### 命名构成 +#### 变量名 +* 采用正常英文语序命名,例如 `day_counter`, `month_counter`, `year_counter` +* 具有大量语义类似,而类型不同的变量,将强调的类型提前作为前缀,例如 `container_name`, `label_name` +* 变量名与方法名冲突时,变量名后加 `_` 后缀,如 `self.name()`, `self.name_` + + +## 控件 / 组件类 +约定模版化的方法和其功能。 + +### _initWidget() +初始化组件中需要用到的控件,仅做声明,不包含样式表,几何信息等。 + +### _initStyle() +初始化样式表、字体、颜色等外观相关的属性到自身以及 `_initWidget()` 声明的控件中。 + +### _initLayout() +初始化布局。除了使用 `QLayout` 外,几何信息也在这里定义,包括位置和大小。 + +### _initAnimation() +初始化动画。包括 `QPropertyAnimation` 和 `SiAnimation`,在这里完成他们的初始化和信号绑定。 +例如: +```python +def _initAnimation(self): + 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) +``` + +### \_\_init\_\_() +* 初始化变量。 +* 依次调用 `_initWidget()` `_initStyle()` `_initLayout()` `_initAnimation()`。 \ No newline at end of file diff --git a/examples/Gallery for siui/components/page_widgets/page_widgets.py b/examples/Gallery for siui/components/page_widgets/page_widgets.py index cde2536..bb39175 100644 --- a/examples/Gallery for siui/components/page_widgets/page_widgets.py +++ b/examples/Gallery for siui/components/page_widgets/page_widgets.py @@ -259,10 +259,10 @@ 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.debug_new_button = SiPushButtonRefactor(self) + self.debug_new_button.resize(128, 32) + self.debug_new_button.setText("新按钮") + self.debug_new_button.setToolTip("我是工具提示") self.demo_push_button_normal = SiPushButton(self) self.demo_push_button_normal.resize(128, 32) @@ -277,7 +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.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) diff --git a/examples/Gallery for siui/test_new_button.py b/examples/Gallery for siui/test_new_button.py index 49eb841..2e707c7 100644 --- a/examples/Gallery for siui/test_new_button.py +++ b/examples/Gallery for siui/test_new_button.py @@ -121,9 +121,10 @@ def __init__(self) -> None: self.setStyleSheet("background-color: #332E38") self.btn = SiPushButton(self) - self.btn.setFixedSize(128, 64) + self.btn.setFixedSize(128, 32) self.btn.setFont(SiFont.tokenized(GlobalFont.S_NORMAL)) - self.btn.setText("你好") + self.btn.setText("我是按钮") + self.btn.clicked.connect(lambda: print("clicked!")) self.main_layout = QVBoxLayout(self) self.main_layout.addWidget(self.btn, alignment=Qt.AlignmentFlag.AlignCenter) diff --git a/siui/components/button.py b/siui/components/button.py index 49eb841..763b737 100644 --- a/siui/components/button.py +++ b/siui/components/button.py @@ -2,22 +2,20 @@ # 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.QtCore import QEvent, QRect, QRectF, Qt from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPaintEvent -from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget +from PyQt5.QtWidgets import QPushButton, QWidget -from siui.core import GlobalFont, SiColor, SiExpAnimation +from siui.core import GlobalFont, SiColor, SiExpAnimation, SiGlobal from siui.gui import SiFont -os.environ["QT_SCALE_FACTOR"] = str(2) - -class SiPushButton(QPushButton): +class SiPushButtonRefactor(QPushButton): def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) + self._initStyle() + self.idle_color = SiColor.toArray("#00FFFFFF") self.hover_color = SiColor.toArray("#10FFFFFF") self.click_color = SiColor.toArray("#40FFFFFF") @@ -43,12 +41,18 @@ def withIcon(cls, icon: QIcon, parent: QWidget | None = None) -> "SiPushButton": cls.setIcon(icon) return cls + @classmethod def withTextAndIcon(cls, text: str, icon: str, parent: QWidget | None = None) -> "SiPushButton": cls = cls(parent) cls.setText(text) cls.setIcon(icon) return cls + def _initStyle(self): + self.setFont(SiFont.tokenized(GlobalFont.S_NORMAL)) + self.setStyleSheet("color: #DFDFDF;") + + @property def bottomBorderHeight(self) -> int: return round(3) @@ -76,22 +80,53 @@ def _drawHighLightRect(self, painter: QPainter, rect: QRect) -> None: painter.setBrush(QColor(SiColor.toCode(self.animation.current_))) painter.drawPath(self._drawButtonPath(rect)) + def _drawTextRect(self, painter: QPainter, rect: QRect) -> None: + painter.setPen(self.palette().text().color()) + painter.setFont(self.font()) + painter.drawText(rect, Qt.AlignCenter, self.text()) + def _onButtonClicked(self) -> None: self.animation.setCurrent(self.click_color) self.animation.start() + def _showToolTip(self) -> None: + if self.toolTip() != "" and "TOOL_TIP" in SiGlobal.siui.windows: + SiGlobal.siui.windows["TOOL_TIP"].setNowInsideOf(self) + SiGlobal.siui.windows["TOOL_TIP"].show_() + + def _hideToolTip(self) -> None: + if self.toolTip() != "" and "TOOL_TIP" in SiGlobal.siui.windows: + SiGlobal.siui.windows["TOOL_TIP"].setNowInsideOf(None) + SiGlobal.siui.windows["TOOL_TIP"].hide_() + + def _updateToolTip(self) -> None: + if SiGlobal.siui.windows["TOOL_TIP"].nowInsideOf() == self: + SiGlobal.siui.windows["TOOL_TIP"].setText(self.toolTip()) + def animate(self, _) -> None: self.update() + def setToolTip(self, tooltip) -> None: + super().setToolTip(tooltip) + self._updateToolTip() + + def event(self, event): + if event.type() == QEvent.ToolTip: + return True # 忽略工具提示事件 + return super().event(event) + def enterEvent(self, event) -> None: super().enterEvent(event) self.animation.setTarget(self.hover_color) self.animation.start() + self._showToolTip() + self._updateToolTip() def leaveEvent(self, event) -> None: super().leaveEvent(event) self.animation.setTarget(self.idle_color) self.animation.start() + self._hideToolTip() def resizeEvent(self, event) -> None: super().resizeEvent(event) @@ -103,34 +138,8 @@ def paintEvent(self, event: QPaintEvent) -> None: painter.setPen(Qt.PenStyle.NoPen) rect = self.rect() + text_rect = QRect(0, 0, self.width(), self.height() - 4) 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() \ No newline at end of file + self._drawTextRect(painter, text_rect) diff --git a/siui/components/tooltip/tooltip.py b/siui/components/tooltip/tooltip.py index 72f00a7..4767e81 100644 --- a/siui/components/tooltip/tooltip.py +++ b/siui/components/tooltip/tooltip.py @@ -1,14 +1,10 @@ -""" -tooltip 模块 -实现工具提示 -""" + from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtGui import QColor, QCursor -from PyQt5.QtWidgets import QGraphicsDropShadowEffect +from PyQt5.QtGui import QCursor from siui.components.widgets.abstracts.widget import SiWidget from siui.components.widgets.label import SiLabel -from siui.core import GlobalFont, Si, SiGlobal +from siui.core import GlobalFont, Si, SiGlobal, SiQuickEffect from siui.gui import SiFont @@ -16,53 +12,61 @@ class ToolTipWindow(SiWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.completely_hid = False + """ 是否已经完全隐藏(透明度是不是0) """ + self.now_inside_of = None + """ 在哪个控件内部(最近一次被谁触发过显示事件) """ + self.margin = 8 + """ 周围给阴影预留的间隔空间 """ + self.shadow_size = 8 + """ 阴影大小 """ + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool | Qt.WindowTransparentForInput) self.setAttribute(Qt.WA_TranslucentBackground) + SiQuickEffect.applyDropShadowOn(self, (0, 0, 0, 128), blur_radius=int(self.shadow_size*1.5)) - self.completely_hid = False # 是否已经完全隐藏(透明度是不是0) - self.animationGroup().fromToken("opacity").finished.connect(self._completely_hid_signal_handler) - - self.margin = 8 # 周围给阴影预留的间隔空间 - self.shadow_size = 8 # 阴影 - - self.now_inside_of = None # 在哪个控件内部(最近一次被谁触发过显示事件) + self._initWidget() + self._initStyle() + self._initLayout() + self._initAnimation() - shadow = QGraphicsDropShadowEffect() - shadow.setColor(QColor(0, 0, 0, 128)) - shadow.setOffset(0, 0) - shadow.setBlurRadius(int(self.shadow_size * 1.5)) - self.setGraphicsEffect(shadow) - - # 跟踪鼠标的计时器,总处于启动状态 - self.tracker_timer = QTimer() - self.tracker_timer.setInterval(int(1000/60)) - self.tracker_timer.timeout.connect(self._refresh_position) - self.tracker_timer.start() + self.setText("", flash=False) # 通过输入空文本初始化大小 - # 背景颜色,可以用于呈现不同类型的信息 + def _initWidget(self): self.bg_label = SiLabel(self) - self.bg_label.move(self.margin, self.margin) - self.bg_label.setFixedStyleSheet("border-radius: 6px") + """背景颜色,可以用于呈现不同类型的信息""" - # 文字标签的父对象,防止文字超出界限 self.text_container = SiLabel(self) - self.text_container.move(self.margin, self.margin) + """文字标签的父对象,防止文字超出界限""" - # 文字标签,工具提示就在这里显示, self.text_label = SiLabel(self.text_container) + """文字标签,显示工具提示内容""" + + self.highlight_mask = SiLabel(self) + """高光遮罩,当信息刷新时会闪烁一下""" + + def _initStyle(self): + self.bg_label.setFixedStyleSheet("border-radius: 6px") self.text_label.setFixedStyleSheet("padding: 8px") self.text_label.setSiliconWidgetFlag(Si.InstantResize) self.text_label.setSiliconWidgetFlag(Si.AdjustSizeOnTextChanged) self.text_label.setFont(SiFont.tokenized(GlobalFont.S_NORMAL)) - - # 高光遮罩,当信息刷新时会闪烁一下 - self.highlight_mask = SiLabel(self) - self.highlight_mask.move(self.margin, self.margin) self.highlight_mask.setFixedStyleSheet("border-radius: 6px") self.highlight_mask.setColor("#00FFFFFF") - # 通过输入空文本初始化大小 - self.setText("", flash=False) + def _initLayout(self): + self.bg_label.move(self.margin, self.margin) + self.text_container.move(self.margin, self.margin) + self.highlight_mask.move(self.margin, self.margin) + + def _initAnimation(self): + self.tracker_timer = QTimer() # 跟踪鼠标的计时器 + self.tracker_timer.setInterval(int(1000/60)) + self.tracker_timer.timeout.connect(self._refresh_position) + self.tracker_timer.start() + + # 当透明度动画结束时处理隐藏与否 + self.animationGroup().fromToken("opacity").finished.connect(self._completely_hid_signal_handler) def reloadStyleSheet(self): self.bg_label.setColor(SiGlobal.siui.colors["TOOLTIP_BG"]) @@ -109,21 +113,13 @@ def setText(self, text, flash=True): self.flash() def _refresh_size(self): - """ - 用于设置大小动画结束值并启动动画 - :return: - """ - w, h = self.text_label.width(), self.text_label.height() - - # 让自身大小变为文字标签的大小加上阴影间距 - self.resizeTo(w + 2 * self.margin, h + 2 * self.margin) + """ 用于设置大小动画结束值并启动动画 """ + w = self.text_label.width() + h = self.text_label.height() + self.resizeTo(w + 2 * self.margin, h + 2 * self.margin) # 设为文字标签的大小加上阴影间距 def flash(self): - """ - 激活高光层动画,使高光层闪烁 - :return: - """ - # 刷新高亮层动画当前值和结束值,实现闪烁效果 + """ 激活高光层动画,使高光层闪烁 """ self.highlight_mask.setColor("#7FFFFFFF") self.highlight_mask.setColorTo("#00FFFFFF") diff --git a/siui/components/widgets/abstracts/label.py b/siui/components/widgets/abstracts/label.py index acaaaaf..4bf8913 100644 --- a/siui/components/widgets/abstracts/label.py +++ b/siui/components/widgets/abstracts/label.py @@ -1,5 +1,5 @@ -from PyQt5.QtCore import QPoint, pyqtSignal +from PyQt5.QtCore import QPoint, pyqtSignal, QEvent from PyQt5.QtWidgets import QLabel from siui.core import Si, SiAnimationGroup, SiColor, SiExpAnimation, SiGlobal, SiQuickEffect @@ -299,6 +299,7 @@ def setHint(self, text: str): :param text: tooltip content. Rich text is supported """ self.hint = text + self.setToolTip(text) # 把新的工具提示推送给工具提示窗口 if self.hint != "" and "TOOL_TIP" in SiGlobal.siui.windows: @@ -323,3 +324,8 @@ def leaveEvent(self, event): if self.hint != "" and "TOOL_TIP" in SiGlobal.siui.windows: SiGlobal.siui.windows["TOOL_TIP"].setNowInsideOf(None) SiGlobal.siui.windows["TOOL_TIP"].hide_() + + def event(self, event): + if event.type() == QEvent.ToolTip: + return True # 忽略工具提示事件 + return super().event(event) \ No newline at end of file