From 771f9d75612b60ec629f3742d28947e7a63cb590 Mon Sep 17 00:00:00 2001 From: ChinaIceF <1489423523@qq.com> Date: Fri, 25 Oct 2024 19:18:42 +0800 Subject: [PATCH 1/4] refactor: opt naming style in `parser.py` --- .../components/page_icons/page_icons.py | 6 ++-- siui/core/globals.py | 2 +- siui/gui/icons/parser.py | 28 +++++++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) 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 8688e40..67c0318 100644 --- a/examples/Gallery for siui/components/page_icons/page_icons.py +++ b/examples/Gallery for siui/components/page_icons/page_icons.py @@ -77,7 +77,7 @@ def __init__(self, *args, **kwargs): self.package_selection_combobox = SiComboBox(self) self.package_selection_combobox.resize(256, 32) self.package_selection_combobox.addOption("所有图标包", (None,)) - for package_name in SiGlobal.siui.iconpack.get_class_names(): + for package_name in SiGlobal.siui.iconpack.getClassNames(): self.package_selection_combobox.addOption(package_name) self.package_selection_combobox.valueChanged.connect(self.on_package_changed) self.package_selection_combobox.menu().setShowIcon(False) @@ -156,7 +156,7 @@ def __init__(self, *args, **kwargs): def on_package_changed(self, package_name): if package_name == (None,): package_name = None - self.icon_dict = SiGlobal.siui.iconpack.get_dict(package_name) + self.icon_dict = SiGlobal.siui.iconpack.getDict(package_name) self.load_icon_page_to(0) def on_search_text_changed(self, text): @@ -200,7 +200,7 @@ def load_icon_page_to(self, page_index, fade_ani=True): svg_button.colorGroup().assign(SiColor.BUTTON_OFF, svg_button.getColor(SiColor.INTERFACE_BG_C)) svg_button.attachment().setSvgSize(32, 32) - svg_button.attachment().load(icon_pack.get_from_data(value, self.getColor(SiColor.SVG_NORMAL))) + svg_button.attachment().load(icon_pack.getFromData(value, self.getColor(SiColor.SVG_NORMAL))) svg_button.setFixedSize(96, 96) svg_button.setHint( f"{key}
" diff --git a/siui/core/globals.py b/siui/core/globals.py index 5f4cfb9..99e5d6b 100644 --- a/siui/core/globals.py +++ b/siui/core/globals.py @@ -21,7 +21,7 @@ class SiliconUIGlobal: # 值为 SVG信息的 bytes icons = {} iconpack = GlobalIconPack() - iconpack.set_default_color(colors.fromToken(SiColor.SVG_NORMAL)) + iconpack.setDefaultColor(colors.fromToken(SiColor.SVG_NORMAL)) # 样式表字典,储存所有动态样式表 # 值为字符串 diff --git a/siui/gui/icons/parser.py b/siui/gui/icons/parser.py index 3604d6c..fb1f9b5 100644 --- a/siui/gui/icons/parser.py +++ b/siui/gui/icons/parser.py @@ -1,5 +1,7 @@ import os +from PyQt5.QtCore import QByteArray + class GlobalIconPack: current_module_path = os.path.dirname(os.path.abspath(__file__)) @@ -16,16 +18,20 @@ def __init__(self): # load internal icon packages self.reload_internals() - def set_default_color(self, code): + def setDefaultColor(self, code) -> None: self.default_color = code - def reload_internals(self): + @property + def defaultColor(self) -> str: + return self.default_color + + def reload_internals(self) -> None: for package_filename in os.listdir(self.package_folder_path): full_path = os.path.join(self.package_folder_path, package_filename) if os.path.isfile(full_path): self.load_from_file(full_path) - def load_from_file(self, path): + def load_from_file(self, path) -> None: class_name = os.path.basename(path) self.append_class(class_name) with open(path, encoding="utf-8") as file: @@ -39,24 +45,28 @@ def load_from_file(self, path): icon_name, icon_data = line.split("////") self.append(icon_name, icon_data, class_name) - def append_class(self, class_name, force=False): + def append_class(self, class_name, force=False) -> None: if class_name in self.icons_classified.keys() and (force is False): raise ValueError(f"Class name {class_name} is already exist.") self.icons_classified[class_name] = {} - def append(self, name, data, class_name: str = "__unclassified__"): + def append(self, name, data, class_name: str = "__unclassified__") -> None: self.icons[name] = data self.icons_classified[class_name][name] = data - def get(self, name, color_code: str = None): + def get(self, name, color_code: str = None) -> bytes: color_code = self.default_color if color_code is None else color_code return self.icons[name].replace("<<>>", color_code).encode() - def get_from_data(self, data, color_code: str = None): + def getFromData(self, data, color_code: str = None) -> bytes: color_code = self.default_color if color_code is None else color_code return data.replace("<<>>", color_code).encode() - def get_dict(self, class_name=None): + def getByteArray(self, name, color_code: str = None) -> QByteArray: + svg_bytes = self.get(name, color_code) + return QByteArray(svg_bytes) + + def getDict(self, class_name=None) -> dict: """ Get dictionary of an icon package. If class name is assigned, returns the specific package dictionary. @@ -67,5 +77,5 @@ def get_dict(self, class_name=None): else: return self.icons_classified[class_name] - def get_class_names(self): + def getClassNames(self) -> dict.keys: return self.icons_classified.keys() From 5963a09e46683664dd0a6e6dbb8a7b9ba8766b48 Mon Sep 17 00:00:00 2001 From: ChinaIceF <1489423523@qq.com> Date: Fri, 25 Oct 2024 21:28:27 +0800 Subject: [PATCH 2/4] feat: make `SiColor.toArray` now accepts color format --- siui/core/color.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/siui/core/color.py b/siui/core/color.py index d89950b..bc2d352 100644 --- a/siui/core/color.py +++ b/siui/core/color.py @@ -103,14 +103,24 @@ def RGB_to_RGBA(code: str): @classmethod def toArray(cls, - code: str): + code: str, + c_format: str = "argb"): """ transform `#AARRGGBB` or `#RRGGBB` into `array(A, R, G, B, dtype=int16)` """ code = cls.RGB_to_RGBA(code) code = code.lstrip("#") a, r, g, b = int(code[0:2], 16), int(code[2:4], 16), int(code[4:6], 16), int(code[6:8], 16) - return numpy.array([a, r, g, b], dtype=numpy.int16) + + c_format = c_format.lower() + if c_format not in ["rgba", "argb", "rgb"]: + raise ValueError(f"{c_format} is not a valid format (rgba, argb, rgb)") + if c_format == "rgba": + return numpy.array([r, g, b, a], dtype=numpy.int16) + if c_format == "argb": + return numpy.array([a, r, g, b], dtype=numpy.int16) + if c_format == "rgb": + return numpy.array([r, g, b], dtype=numpy.int16) @staticmethod def toCode(value: Union[numpy.ndarray, list], force_rgba=False): @@ -125,7 +135,6 @@ def toCode(value: Union[numpy.ndarray, list], force_rgba=False): if (force_rgba is True) or (a != 255): return f"#{int(a):02X}{int(r):02X}{int(g):02X}{int(b):02X}" - else: return f"#{int(r):02X}{int(g):02X}{int(b):02X}" From 390c45b17872a1b9af50994f4c64887a67c6a484 Mon Sep 17 00:00:00 2001 From: ChinaIceF <1489423523@qq.com> Date: Fri, 25 Oct 2024 21:28:49 +0800 Subject: [PATCH 3/4] refactor: use `PushButtonStyleData` as dataclass of SiPushButton --- siui/components/button.py | 104 +++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/siui/components/button.py b/siui/components/button.py index 763b737..eaad806 100644 --- a/siui/components/button.py +++ b/siui/components/button.py @@ -2,6 +2,8 @@ # replace button once it's done. Now it's draft, code may be ugly and verbose temporarily. from __future__ import annotations +from dataclasses import dataclass + from PyQt5.QtCore import QEvent, QRect, QRectF, Qt from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPaintEvent from PyQt5.QtWidgets import QPushButton, QWidget @@ -10,70 +12,83 @@ from siui.gui import SiFont +@dataclass +class PushButtonStyleData: + idle_color = SiColor.toArray("#00FFFFFF") + hover_color = SiColor.toArray("#10FFFFFF") + click_color = SiColor.toArray("#40FFFFFF") + background_color = SiColor.toArray("#2d2932", "rgba") + button_color = SiColor.toArray("#4C4554", "rgba") + border_radius: int = 4 + border_inner_radius: int = 3 + border_height: int = 3 + + class SiPushButtonRefactor(QPushButton): def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) + self.style_data = PushButtonStyleData() self._initStyle() - 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.setTarget(self.style_data.idle_color) + self.animation.setCurrent(self.style_data.idle_color) self.animation.ticked.connect(self.animate) self.clicked.connect(self._onButtonClicked) + def _initStyle(self): + self.setFont(SiFont.tokenized(GlobalFont.S_NORMAL)) + self.setStyleSheet("color: #DFDFDF;") + @classmethod def withText(cls, text: str, parent: QWidget | None = None) -> "SiPushButton": - cls = cls(parent) - cls.setText(text) - return cls + obj = cls(parent) + obj.setText(text) + return obj @classmethod def withIcon(cls, icon: QIcon, parent: QWidget | None = None) -> "SiPushButton": - cls = cls(parent) - cls.setIcon(icon) - return cls + obj = cls(parent) + obj.setIcon(icon) + return obj @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;") - + obj = cls(parent) + obj.setText(text) + obj.setIcon(QIcon(icon)) + return obj @property def bottomBorderHeight(self) -> int: - return round(3) + return self.style_data.border_height - @staticmethod - def _drawBackgroundPath(rect: QRect) -> QPainterPath: + @property + def styleData(self) -> PushButtonStyleData: + return self.style_data + + def _drawBackgroundPath(self, rect: QRect) -> QPainterPath: + radius = self.style_data.border_radius path = QPainterPath() - path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height()), 4, 4) + path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height()), radius, radius) return path def _drawBackgroundRect(self, painter: QPainter, rect: QRect) -> None: - painter.setBrush(QColor("#2D2932")) + painter.setBrush(QColor(*self.style_data.background_color)) painter.drawPath(self._drawBackgroundPath(rect)) def _drawButtonPath(self, rect: QRect) -> QPainterPath: + radius = self.style_data.border_inner_radius path = QPainterPath() - path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height() - self.bottomBorderHeight), 3, 3) + path.addRoundedRect(QRectF(0, 0, rect.width(), rect.height() - self.bottomBorderHeight), radius, radius) return path def _drawButtonRect(self, painter: QPainter, rect: QRect) -> None: - painter.setBrush(QColor("#4C4554")) + painter.setBrush(QColor(*self.style_data.button_color)) painter.drawPath(self._drawButtonPath(rect)) def _drawHighLightRect(self, painter: QPainter, rect: QRect) -> None: @@ -86,22 +101,25 @@ def _drawTextRect(self, painter: QPainter, rect: QRect) -> None: painter.drawText(rect, Qt.AlignCenter, self.text()) def _onButtonClicked(self) -> None: - self.animation.setCurrent(self.click_color) + self.animation.setCurrent(self.style_data.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_() + tool_tip_window = SiGlobal.siui.windows.get("TOOL_TIP") + if tool_tip_window is not None and self.toolTip() != "": + tool_tip_window.setNowInsideOf(self) + tool_tip_window.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_() + tool_tip_window = SiGlobal.siui.windows.get("TOOL_TIP") + if tool_tip_window is not None and self.toolTip() != "": + tool_tip_window.setNowInsideOf(None) + tool_tip_window.hide_() def _updateToolTip(self) -> None: - if SiGlobal.siui.windows["TOOL_TIP"].nowInsideOf() == self: - SiGlobal.siui.windows["TOOL_TIP"].setText(self.toolTip()) + tool_tip_window = SiGlobal.siui.windows.get("TOOL_TIP") + if tool_tip_window is not None and tool_tip_window.nowInsideOf() == self: + tool_tip_window.setText(self.toolTip()) def animate(self, _) -> None: self.update() @@ -110,6 +128,12 @@ def setToolTip(self, tooltip) -> None: super().setToolTip(tooltip) self._updateToolTip() + def setButtonColor(self, code: str) -> None: + self.style_data.button_color = SiColor.toArray(code, "rgba") + + def setBackgroundColor(self, code: str) -> None: + self.style_data.background_color = SiColor.toArray(code, "rgba") + def event(self, event): if event.type() == QEvent.ToolTip: return True # 忽略工具提示事件 @@ -117,14 +141,14 @@ def event(self, event): def enterEvent(self, event) -> None: super().enterEvent(event) - self.animation.setTarget(self.hover_color) + self.animation.setTarget(self.style_data.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.setTarget(self.style_data.idle_color) self.animation.start() self._hideToolTip() @@ -138,7 +162,7 @@ def paintEvent(self, event: QPaintEvent) -> None: painter.setPen(Qt.PenStyle.NoPen) rect = self.rect() - text_rect = QRect(0, 0, self.width(), self.height() - 4) + text_rect = QRect(0, 0, self.width(), self.height() - self.style_data.border_height - 1) self._drawBackgroundRect(painter, rect) self._drawButtonRect(painter, rect) self._drawHighLightRect(painter, rect) From 3b04105c950e3a357cb0c410daf45f0b6b2d6c83 Mon Sep 17 00:00:00 2001 From: ChinaIceF <1489423523@qq.com> Date: Sat, 26 Oct 2024 11:42:57 +0800 Subject: [PATCH 4/4] refactor: abst `PushButtonStyleData` --- siui/components/button.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/siui/components/button.py b/siui/components/button.py index eaad806..74136e1 100644 --- a/siui/components/button.py +++ b/siui/components/button.py @@ -130,9 +130,23 @@ def setToolTip(self, tooltip) -> None: def setButtonColor(self, code: str) -> None: self.style_data.button_color = SiColor.toArray(code, "rgba") + self.update() def setBackgroundColor(self, code: str) -> None: self.style_data.background_color = SiColor.toArray(code, "rgba") + self.update() + + def setBorderRadius(self, r: int) -> None: + self.style_data.border_radius = r + self.update() + + def setBorderInnerRadius(self, r: int) -> None: + self.style_data.border_inner_radius = r + self.update() + + def setBorderHeight(self, h: int) -> None: + self.style_data.border_height = h + self.update() def event(self, event): if event.type() == QEvent.ToolTip: