Skip to content

Commit

Permalink
Merge pull request #168 from ChinaIceF/reconst
Browse files Browse the repository at this point in the history
refactor: adopt dataclass in new `SiPushButton`
  • Loading branch information
ChinaIceF authored Oct 26, 2024
2 parents 132a818 + 3b04105 commit 9f913c1
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 56 deletions.
6 changes: 3 additions & 3 deletions examples/Gallery for siui/components/page_icons/page_icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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}<br>"
Expand Down
118 changes: 78 additions & 40 deletions siui/components/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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()
Expand All @@ -110,21 +128,41 @@ 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")
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:
return True # 忽略工具提示事件
return super().event(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()

Expand All @@ -138,7 +176,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)
Expand Down
15 changes: 12 additions & 3 deletions siui/core/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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}"

Expand Down
2 changes: 1 addition & 1 deletion siui/core/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

# 样式表字典,储存所有动态样式表
# 值为字符串
Expand Down
28 changes: 19 additions & 9 deletions siui/gui/icons/parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os

from PyQt5.QtCore import QByteArray


class GlobalIconPack:
current_module_path = os.path.dirname(os.path.abspath(__file__))
Expand All @@ -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:
Expand All @@ -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>>>", 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>>>", 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.
Expand All @@ -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()

0 comments on commit 9f913c1

Please sign in to comment.