brisonus_app_eq/widget_file_list/card_widget.py

280 lines
9.4 KiB
Python
Raw Normal View History

2025-02-18 22:05:52 +08:00
from PySide6.QtWidgets import (QWidget, QListWidget, QStyledItemDelegate,
QApplication, QVBoxLayout, QMenu, QListWidgetItem,
QStyle)
from PySide6.QtCore import Qt, QSize, QRect, QPoint
from PySide6.QtGui import (QPainter, QPainterPath, QColor, QLinearGradient,
QPen, QFont, QIcon)
from dataclasses import dataclass
import sys
from datetime import date
import os
import subprocess
@dataclass
class CardData:
name: str
date: str
description: str
activated: bool = False
class CardItemDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
def paint(self, painter: QPainter, option, index):
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.save()
# 获取数据
data = index.data(Qt.ItemDataRole.UserRole)
# 绘制卡片背景
rect = option.rect
rect.adjust(8, 4, -8, -4)
# 创建圆角路径
path = QPainterPath()
path.addRoundedRect(rect, 8, 8)
# 绘制阴影
shadow_color = QColor(0, 0, 0, 30)
for i in range(5):
shadow_rect = rect.adjusted(0, i, 0, i)
shadow_path = QPainterPath()
shadow_path.addRoundedRect(shadow_rect, 8, 8)
painter.fillPath(shadow_path, shadow_color)
# 绘制卡片背景
gradient = QLinearGradient(rect.topLeft(), rect.bottomLeft())
if data.activated:
gradient.setColorAt(0, QColor(40, 70, 45))
gradient.setColorAt(1, QColor(45, 80, 50))
painter.fillPath(path, gradient)
painter.setPen(QPen(QColor(60, 180, 90), 2))
elif option.state & QStyle.State_Selected:
gradient.setColorAt(0, QColor(45, 45, 55))
gradient.setColorAt(1, QColor(55, 55, 65))
painter.fillPath(path, gradient)
painter.setPen(QPen(QColor(70, 130, 180), 2))
else:
painter.fillPath(path, QColor(35, 35, 40))
painter.setPen(QPen(QColor(60, 60, 65)))
painter.drawPath(path)
# 设置字体
name_font = QFont("Microsoft YaHei", 10)
name_font.setBold(True)
normal_font = QFont("Microsoft YaHei", 9)
# 计算文本区域
padding = 15
name_rect = rect.adjusted(padding, padding, -padding, 0)
name_rect.setHeight(25)
date_rect = QRect(name_rect)
date_rect.translate(0, name_rect.height())
desc_rect = QRect(date_rect)
desc_rect.translate(0, date_rect.height())
desc_rect.setHeight(40)
# 绘制文本
painter.setFont(name_font)
painter.setPen(Qt.GlobalColor.white)
painter.drawText(name_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, data.name)
painter.setFont(normal_font)
painter.setPen(QColor(180, 180, 180))
painter.drawText(date_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, data.date)
painter.setPen(QColor(160, 160, 160))
painter.drawText(desc_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop | Qt.TextFlag.TextWordWrap,
data.description)
painter.restore()
def sizeHint(self, option, index):
return QSize(380, 130)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setup_ui()
self.setup_style()
# 添加示例数据
for i in range(1, 11):
data = CardData(
name=f"项目 {i}",
date=date.today().strftime("%Y-%m-%d"),
description=f"这是项目 {i} 的详细描述信息,可以包含多行文本内容。这是一个较长的描述,用于测试换行效果。"
)
self.add_card_item(data)
def setup_ui(self):
self.setWindowTitle("卡片列表示例")
self.resize(400, 600)
layout = QVBoxLayout(self)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(0)
self.list_widget = QListWidget(self)
self.list_widget.setSpacing(10)
self.list_widget.setResizeMode(QListWidget.ResizeMode.Adjust)
self.list_widget.setUniformItemSizes(False)
self.list_widget.setViewMode(QListWidget.ViewMode.ListMode)
self.list_widget.setVerticalScrollMode(QListWidget.ScrollMode.ScrollPerPixel)
self.list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
delegate = CardItemDelegate(self.list_widget)
self.list_widget.setItemDelegate(delegate)
# 连接信号
self.list_widget.itemDoubleClicked.connect(self.on_item_double_clicked)
self.list_widget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.list_widget.customContextMenuRequested.connect(self.show_context_menu)
layout.addWidget(self.list_widget)
def setup_style(self):
self.setStyleSheet("""
QWidget {
background-color: #1e1e1e;
}
QListWidget {
background-color: #1e1e1e;
border: none;
outline: none;
}
QListWidget::item {
background-color: transparent;
padding: 4px;
}
QListWidget::item:selected {
background-color: transparent;
}
QScrollBar:vertical {
border: none;
background: #1e1e1e;
width: 8px;
margin: 0px;
}
QScrollBar::handle:vertical {
background: #404040;
min-height: 20px;
border-radius: 4px;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QMenu {
background-color: #2d2d2d;
border: 1px solid #404040;
padding: 5px;
}
QMenu::item {
background-color: transparent;
padding: 6px 25px;
border-radius: 4px;
color: #ffffff;
}
QMenu::item:selected {
background-color: #404040;
}
QMenu::separator {
height: 1px;
background: #404040;
margin: 5px 0px;
}
""")
def add_card_item(self, data: CardData):
item = QListWidgetItem(self.list_widget)
item.setData(Qt.ItemDataRole.UserRole, data)
item.setSizeHint(QSize(380, 120))
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable)
def on_item_double_clicked(self, item):
# 先将所有项目设置为未激活状态
for row in range(self.list_widget.count()):
current_item = self.list_widget.item(row)
data = current_item.data(Qt.ItemDataRole.UserRole)
data.activated = False
current_item.setData(Qt.ItemDataRole.UserRole, data)
# 设置当前点击项目为激活状态
data = item.data(Qt.ItemDataRole.UserRole)
data.activated = True
item.setData(Qt.ItemDataRole.UserRole, data)
self.list_widget.viewport().update()
def show_context_menu(self, pos: QPoint):
item = self.list_widget.itemAt(pos)
if not item:
return
menu = QMenu(self)
send_action = menu.addAction("发送到设备")
menu.addSeparator()
edit_action = menu.addAction("修改")
delete_action = menu.addAction("删除")
menu.addSeparator()
show_in_explorer_action = menu.addAction("在资源管理器中显示")
# 设置图标
send_action.setIcon(QIcon.fromTheme("document-send"))
edit_action.setIcon(QIcon.fromTheme("document-edit"))
delete_action.setIcon(QIcon.fromTheme("document-delete"))
show_in_explorer_action.setIcon(QIcon.fromTheme("folder"))
action = menu.exec(self.list_widget.viewport().mapToGlobal(pos))
if not action:
return
if action == send_action:
self.send_to_device(item)
elif action == edit_action:
self.edit_item(item)
elif action == delete_action:
self.delete_item(item)
elif action == show_in_explorer_action:
self.show_in_explorer(item)
def send_to_device(self, item):
data = item.data(Qt.ItemDataRole.UserRole)
print(f"发送到设备: {data.name}")
def edit_item(self, item):
data = item.data(Qt.ItemDataRole.UserRole)
print(f"编辑项目: {data.name}")
def delete_item(self, item):
row = self.list_widget.row(item)
self.list_widget.takeItem(row)
def show_in_explorer(self, item):
path = os.getcwd()
if sys.platform == 'win32':
subprocess.run(['explorer', '/select,', os.path.normpath(path)])
elif sys.platform == 'darwin':
subprocess.run(['open', '-R', path])
else:
subprocess.run(['xdg-open', os.path.dirname(path)])
if __name__ == '__main__':
app = QApplication(sys.argv)
# 设置字体
font = QFont("Microsoft YaHei", 9)
app.setFont(font)
window = MainWindow()
window.show()
sys.exit(app.exec())