Skip to content
This repository was archived by the owner on Jun 28, 2025. It is now read-only.

Commit da39c54

Browse files
committed
feat: ui improved
1 parent 00072fd commit da39c54

16 files changed

Lines changed: 6382 additions & 44 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ __pycache__/
1212
Plain_Craft_Launcher_2/data/*.log
1313

1414
# Debugging Files
15-
Plain_Craft_Launcher_2/.minecraft/
15+
**/.minecraft/

Plain_Craft_Launcher_2/Application.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
from PyQt5.QtWidgets import *
23
import sys
34

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
# -*- coding: utf-8 -*-
2+
from PyQt5.QtWidgets import QPushButton
3+
from PyQt5.QtSvg import QSvgWidget
4+
from PyQt5.QtCore import Qt, QSize, QPropertyAnimation, QEasingCurve, QRect
5+
from PyQt5.QtGui import QColor, QPainter, QPen, QBrush
6+
7+
class MyRoundButton(QPushButton):
8+
"""圆形 SVG 图标按钮
9+
10+
一个可以显示 SVG 图标的圆形按钮,支持悬停效果和点击效果
11+
"""
12+
13+
def __init__(self, parent=None, svg_path="", size=(64, 64), tooltip="",
14+
svg_size=None,
15+
border_width=1, border_color=QColor(255, 255, 255, 0),
16+
svg_color=QColor(255, 255, 255, 255),
17+
margin=(0, 0, 0, 0),
18+
padding=(0, 0, 0, 0)):
19+
"""初始化圆形 SVG 按钮
20+
21+
Args:
22+
parent: 父控件
23+
svg_path: SVG 文件路径
24+
size: 按钮大小,默认为 (64, 64)
25+
tooltip: 鼠标悬停提示文本
26+
svg_size: SVG 图标大小,默认与按钮大小相同
27+
border_width: 边框宽度,默认为 1
28+
border_color: 边框颜色,默认为透明
29+
svg_color: SVG 图标颜色,默认为白色
30+
margin: SVG 图标外边距,格式为 (左, 上, 右, 下),默认为 (0, 0, 0, 0)
31+
padding: SVG 图标内边距,格式为 (左, 上, 右, 下),默认为 (0, 0, 0, 0)
32+
"""
33+
super().__init__(parent)
34+
35+
# 设置按钮属性
36+
self.setFixedSize(*size)
37+
self.setToolTip(tooltip)
38+
self.setCursor(Qt.PointingHandCursor)
39+
40+
# 设置样式
41+
self.setStyleSheet("""
42+
QPushButton {
43+
background-color: transparent;
44+
border: none;
45+
}
46+
QPushButton:hover {
47+
background-color: transparent;
48+
}
49+
QPushButton:pressed {
50+
background-color: transparent;
51+
}
52+
""")
53+
54+
# 创建 SVG 控件
55+
self.svg_widget = QSvgWidget(self)
56+
57+
# 加载 SVG 文件
58+
self.svg_widget.load(svg_path)
59+
60+
# 设置 SVG 控件大小和位置
61+
self.svg_size = svg_size if svg_size is not None else size
62+
self.margin = margin # 设置外边距 (左, 上, 右, 下)
63+
self.padding = padding # 设置内边距 (左, 上, 右, 下)
64+
self.centerSvg() # 居中 SVG 图标
65+
66+
self.setSvgColor(svg_color)
67+
68+
# 设置圆形边框属性
69+
self.border_color = border_color
70+
self.border_width = border_width
71+
72+
# 添加状态跟踪
73+
self.is_hovered = False
74+
self.is_pressed = False
75+
76+
# 设置背景颜色(默认透明)
77+
self.bg_color = QColor(255, 255, 255, 0)
78+
79+
def centerSvg(self):
80+
"""居中显示 SVG 图标,考虑边距和内边距设置
81+
82+
当 padding 值为负值时,不做压缩而是直接在对应的位置裁剪
83+
"""
84+
# 计算 SVG 控件的位置,使其在按钮中居中
85+
button_width, button_height = self.width(), self.height()
86+
svg_width, svg_height = self.svg_size
87+
left_margin, top_margin, right_margin, bottom_margin = self.margin
88+
left_padding, top_padding, right_padding, bottom_padding = self.padding
89+
90+
# 计算可用空间(考虑外边距)
91+
available_width = button_width - left_margin - right_margin
92+
available_height = button_height - top_margin - bottom_margin
93+
94+
# 处理 padding 值
95+
# 对于正值 padding,减小 SVG 显示大小
96+
# 对于负值 padding,不减小 SVG 显示大小,而是在后续步骤中通过位置调整实现裁剪效果
97+
width_reduction = max(0, left_padding) + max(0, right_padding)
98+
height_reduction = max(0, top_padding) + max(0, bottom_padding)
99+
100+
# 计算实际 SVG 显示大小
101+
actual_svg_width = min(svg_width - width_reduction, available_width)
102+
actual_svg_height = min(svg_height - height_reduction, available_height)
103+
104+
# 确保 SVG 尺寸不为负值
105+
actual_svg_width = max(actual_svg_width, 0)
106+
actual_svg_height = max(actual_svg_height, 0)
107+
108+
# 计算基础居中位置(考虑外边距)
109+
base_x = left_margin + (available_width - actual_svg_width) / 2
110+
base_y = top_margin + (available_height - actual_svg_height) / 2
111+
112+
# 应用负值 padding 的裁剪效果
113+
# 负值的左/上 padding 会使 SVG 向左/上移动,实现左/上裁剪
114+
# 负值的右/下 padding 不影响位置,但会在后续步骤中扩大 SVG 尺寸
115+
x_offset = min(0, left_padding) # 负值左 padding 导致向左偏移
116+
y_offset = min(0, top_padding) # 负值上 padding 导致向上偏移
117+
118+
# 计算最终位置(应用偏移)
119+
final_x = base_x + x_offset
120+
final_y = base_y + y_offset
121+
122+
# 计算最终尺寸(考虑负值 padding 导致的尺寸扩大)
123+
# 负值的右/下 padding 会使 SVG 向右/下扩展,实现右/下裁剪
124+
width_expansion = abs(min(0, left_padding)) + abs(min(0, right_padding))
125+
height_expansion = abs(min(0, top_padding)) + abs(min(0, bottom_padding))
126+
127+
final_width = actual_svg_width + width_expansion
128+
final_height = actual_svg_height + height_expansion
129+
130+
# 确保不超出按钮边界
131+
final_width = min(final_width, button_width - left_margin - right_margin)
132+
final_height = min(final_height, button_height - top_margin - bottom_margin)
133+
134+
# 设置 SVG 控件的几何位置
135+
self.svg_widget.setGeometry(
136+
int(final_x),
137+
int(final_y),
138+
int(final_width),
139+
int(final_height)
140+
)
141+
142+
def setMargin(self, margin):
143+
"""设置 SVG 图标的外边距
144+
145+
Args:
146+
margin: (左, 上, 右, 下) 边距值的元组
147+
"""
148+
self.margin = margin
149+
self.centerSvg() # 更新 SVG 位置
150+
151+
def setPadding(self, padding):
152+
"""设置 SVG 图标的内边距
153+
154+
Args:
155+
padding: (左, 上, 右, 下) 内边距值的元组
156+
"""
157+
self.padding = padding
158+
self.centerSvg() # 更新 SVG 位置
159+
160+
def resizeEvent(self, event):
161+
"""重写调整大小事件,确保 SVG 图标始终居中"""
162+
super().resizeEvent(event)
163+
self.centerSvg()
164+
165+
def setSvgColor(self, color):
166+
"""设置 SVG 的颜色(仅当 SVG 支持颜色修改时有效)
167+
168+
Args:
169+
color: QColor 对象或颜色名称字符串
170+
"""
171+
if isinstance(color, str):
172+
color = QColor(color)
173+
174+
# 通过样式表设置 SVG 颜色
175+
self.svg_widget.setStyleSheet(f"background-color: transparent; color: {color.name()};")
176+
177+
def setSvgPath(self, svg_path):
178+
"""更新 SVG 图标
179+
180+
Args:
181+
svg_path: SVG 文件路径
182+
"""
183+
# 加载 SVG 文件
184+
self.svg_widget.load(svg_path)
185+
186+
def setSvgSize(self, size):
187+
"""设置 SVG 图标的大小
188+
189+
Args:
190+
size: (width, height) 元组
191+
"""
192+
self.svg_size = size
193+
self.centerSvg() # 更新 SVG 位置
194+
195+
def setBorderColor(self, color):
196+
"""设置边框颜色
197+
198+
Args:
199+
color: QColor 对象或颜色名称字符串
200+
"""
201+
if isinstance(color, str):
202+
color = QColor(color)
203+
self.border_color = color
204+
self.update() # 触发重绘
205+
206+
def setBorderWidth(self, width):
207+
"""设置边框宽度
208+
209+
Args:
210+
width: 边框宽度(像素)
211+
"""
212+
self.border_width = width
213+
self.update() # 触发重绘
214+
215+
def enterEvent(self, event):
216+
"""鼠标进入事件"""
217+
self.is_hovered = True
218+
self.update() # 触发重绘
219+
super().enterEvent(event)
220+
221+
def leaveEvent(self, event):
222+
"""鼠标离开事件"""
223+
self.is_hovered = False
224+
self.update() # 触发重绘
225+
super().leaveEvent(event)
226+
227+
def mousePressEvent(self, event):
228+
"""鼠标按下事件"""
229+
if event.button() == Qt.LeftButton:
230+
self.is_pressed = True
231+
self.update() # 触发重绘
232+
super().mousePressEvent(event)
233+
234+
def mouseReleaseEvent(self, event):
235+
"""鼠标释放事件"""
236+
if event.button() == Qt.LeftButton:
237+
self.is_pressed = False
238+
self.update() # 触发重绘
239+
super().mouseReleaseEvent(event)
240+
241+
def paintEvent(self, event):
242+
"""重写绘制事件,添加圆形边框和背景"""
243+
# 不调用父类的绘制方法,完全自定义绘制
244+
245+
# 创建画笔
246+
painter = QPainter(self)
247+
painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿
248+
249+
# 根据状态设置背景颜色
250+
if self.is_pressed:
251+
# 按下状态 - 50% 透明度
252+
bg_color = QColor(255, 255, 255, 127)
253+
border_color = QColor(self.border_color)
254+
border_color.setAlpha(127)
255+
elif self.is_hovered:
256+
# 悬停状态 - 50% 透明度
257+
bg_color = QColor(255, 255, 255, 127)
258+
border_color = QColor(self.border_color)
259+
border_color.setAlpha(127)
260+
else:
261+
# 正常状态 - 完全透明
262+
bg_color = QColor(255, 255, 255, 0)
263+
border_color = QColor(self.border_color)
264+
265+
# 绘制背景
266+
painter.setBrush(QBrush(bg_color))
267+
268+
# 设置画笔属性
269+
pen = QPen(border_color)
270+
pen.setWidth(self.border_width)
271+
painter.setPen(pen)
272+
273+
# 绘制圆形边框和背景
274+
painter.drawEllipse(self.rect().adjusted(
275+
self.border_width // 2,
276+
self.border_width // 2,
277+
-self.border_width // 2,
278+
-self.border_width // 2
279+
))
280+
281+
def setHoverColor(self, color):
282+
"""设置鼠标悬停时的背景颜色
283+
284+
Args:
285+
color: 颜色值,可以是 rgba 格式
286+
"""
287+
if isinstance(color, str):
288+
color = QColor(color)
289+
self.hover_color = color
290+
291+
def setAnimated(self, animated=True):
292+
"""设置是否启用动画效果
293+
294+
Args:
295+
animated: 是否启用动画
296+
"""
297+
if animated:
298+
# 创建缩放动画
299+
self.animation = QPropertyAnimation(self, b"geometry")
300+
self.animation.setDuration(100)
301+
self.animation.setEasingCurve(QEasingCurve.OutCubic)
302+
303+
# 连接鼠标事件
304+
self._original_enter_event = self.enterEvent
305+
self._original_leave_event = self.leaveEvent
306+
307+
self.enterEvent = self._animated_enter_event
308+
self.leaveEvent = self._animated_leave_event
309+
else:
310+
# 移除动画效果
311+
if hasattr(self, '_original_enter_event') and hasattr(self, '_original_leave_event'):
312+
self.enterEvent = self._original_enter_event
313+
self.leaveEvent = self._original_leave_event
314+
315+
def _animated_enter_event(self, event):
316+
"""鼠标进入事件(带动画)"""
317+
# 调用原始的 enterEvent 来处理状态
318+
self._original_enter_event(event)
319+
320+
# 保存原始几何信息
321+
rect = self.geometry()
322+
323+
# 计算放大后的几何信息(放大 5%)
324+
center_x = rect.x() + rect.width() / 2
325+
center_y = rect.y() + rect.height() / 2
326+
new_width = rect.width() * 1.05
327+
new_height = rect.height() * 1.05
328+
new_x = center_x - new_width / 2
329+
new_y = center_y - new_height / 2
330+
331+
# 设置动画
332+
self.animation.setStartValue(rect)
333+
self.animation.setEndValue(QRect(new_x, new_y, new_width, new_height))
334+
self.animation.start()
335+
336+
def _animated_leave_event(self, event):
337+
"""鼠标离开事件(带动画)"""
338+
# 调用原始的 leaveEvent 来处理状态
339+
self._original_leave_event(event)
340+
341+
# 获取当前几何信息
342+
current_rect = self.geometry()
343+
344+
# 计算原始几何信息
345+
center_x = current_rect.x() + current_rect.width() / 2
346+
center_y = current_rect.y() + current_rect.height() / 2
347+
orig_width = current_rect.width() / 1.05
348+
orig_height = current_rect.height() / 1.05
349+
orig_x = center_x - orig_width / 2
350+
orig_y = center_y - orig_height / 2
351+
352+
# 设置动画
353+
self.animation.setStartValue(current_rect)
354+
self.animation.setEndValue(QRect(orig_x, orig_y, orig_width, orig_height))
355+
self.animation.start()

0 commit comments

Comments
 (0)