Enhance export mapping to image with block badges and sidebar legend
- Добавлен детальный рендеринг маппинга при экспорте: - Рисуются информационные плашки (badges) для первого и последнего светодиода в каждом блоке, показывающие ID точки и её локальный индекс в линии. - Справа от маппинга выводится боковая панель с легендой линий и блоков. - В легенде под цветом каждой линии выводятся блоки и диапазоны индексов их светодиодов (например, Блок: 0-186).
This commit is contained in:
parent
31b9028fc2
commit
59586baca1
173
canvas.py
173
canvas.py
@ -1499,30 +1499,167 @@ class LedCanvas(QGraphicsView):
|
||||
if rect.isEmpty():
|
||||
return False
|
||||
|
||||
# 1. Add temporary badges for start/end points of each block
|
||||
from PySide6.QtGui import QFont, QPen, QBrush, QColor
|
||||
from PySide6.QtWidgets import QGraphicsRectItem, QGraphicsSimpleTextItem
|
||||
|
||||
def create_badge(text: str, bg_color: QColor, pos: QPointF, offset_y: float) -> QGraphicsRectItem:
|
||||
text_item = QGraphicsSimpleTextItem(text)
|
||||
font = QFont("Outfit", 8)
|
||||
font.setBold(True)
|
||||
text_item.setFont(font)
|
||||
text_item.setBrush(QBrush(Qt.white))
|
||||
|
||||
rect = text_item.boundingRect()
|
||||
px, py = 6, 4
|
||||
w = rect.width() + px * 2
|
||||
h = rect.height() + py * 2
|
||||
|
||||
rect_item = QGraphicsRectItem(-w/2, -h/2, w, h)
|
||||
rect_item.setBrush(QBrush(bg_color))
|
||||
rect_item.setPen(QPen(Qt.white, 1.2))
|
||||
rect_item.setZValue(100)
|
||||
|
||||
text_item.setParentItem(rect_item)
|
||||
text_item.setPos(-rect.width()/2 - rect.left(), -rect.height()/2 - rect.top())
|
||||
|
||||
rect_item.setPos(pos.x(), pos.y() + offset_y)
|
||||
return rect_item
|
||||
|
||||
temp_items = []
|
||||
for line in self.document.lines:
|
||||
current_offset = 0
|
||||
for block in line.blocks:
|
||||
if not block.point_ids:
|
||||
continue
|
||||
block_len = len(block.point_ids)
|
||||
first_pid = block.point_ids[0]
|
||||
last_pid = block.point_ids[-1]
|
||||
|
||||
start_idx = current_offset
|
||||
end_idx = current_offset + block_len - 1
|
||||
current_offset += block_len
|
||||
|
||||
# Get scene positions
|
||||
if first_pid in self.items_by_id:
|
||||
pos_start = self.items_by_id[first_pid].pos()
|
||||
point_start = self.document.points[first_pid]
|
||||
if last_pid == first_pid:
|
||||
text_start = f"S=E: ID {point_start.source_id} (#{start_idx})"
|
||||
badge_start = create_badge(text_start, QColor("#3b82f6"), pos_start, offset_y=-18) # Blue badge
|
||||
else:
|
||||
text_start = f"S: ID {point_start.source_id} (#{start_idx})"
|
||||
badge_start = create_badge(text_start, QColor("#10b981"), pos_start, offset_y=-18) # Green badge
|
||||
scene.addItem(badge_start)
|
||||
temp_items.append(badge_start)
|
||||
|
||||
if last_pid in self.items_by_id and last_pid != first_pid:
|
||||
pos_end = self.items_by_id[last_pid].pos()
|
||||
point_end = self.document.points[last_pid]
|
||||
text_end = f"E: ID {point_end.source_id} (#{end_idx})"
|
||||
badge_end = create_badge(text_end, QColor("#ef4444"), pos_end, offset_y=18) # Red badge
|
||||
scene.addItem(badge_end)
|
||||
temp_items.append(badge_end)
|
||||
|
||||
# 2. Render setup
|
||||
target_height = 2000
|
||||
mapping_width = 2000
|
||||
sidebar_width = 800
|
||||
total_width = mapping_width + sidebar_width
|
||||
|
||||
image = QImage(total_width, target_height, QImage.Format.Format_ARGB32)
|
||||
|
||||
# Draw background for the mapping area
|
||||
bg_brush = scene.backgroundBrush()
|
||||
mapping_bg = bg_brush.color() if bg_brush.style() != Qt.NoBrush else QColor("#2b2b2b" if self._theme == "dark" else "#ffffff")
|
||||
|
||||
painter = QPainter(image)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.setRenderHint(QPainter.TextAntialiasing)
|
||||
|
||||
# Fill the entire image with mapping background initially
|
||||
image.fill(mapping_bg)
|
||||
|
||||
# Adjust scene rect with padding for mapping render
|
||||
padding = max(rect.width(), rect.height()) * 0.05
|
||||
rect.adjust(-padding, -padding, padding, padding)
|
||||
|
||||
target_width = 2048
|
||||
aspect_ratio = rect.height() / rect.width()
|
||||
target_height = int(target_width * aspect_ratio)
|
||||
# Render the scene mapping area on the left
|
||||
scene.render(painter, QRectF(0, 0, mapping_width, target_height), rect)
|
||||
|
||||
if target_height > 4096:
|
||||
target_height = 4096
|
||||
target_width = int(target_height / aspect_ratio)
|
||||
# 3. Draw sidebar legend on the right
|
||||
sidebar_rect = QRectF(mapping_width, 0, sidebar_width, target_height)
|
||||
|
||||
# Fill sidebar background
|
||||
painter.setPen(Qt.NoPen)
|
||||
sidebar_bg = QColor("#1e293b" if self._theme == "dark" else "#f8fafc")
|
||||
painter.setBrush(QBrush(sidebar_bg))
|
||||
painter.drawRect(sidebar_rect)
|
||||
|
||||
# Draw vertical divider line
|
||||
painter.setPen(QPen(QColor("#475569" if self._theme == "dark" else "#cbd5e1"), 2))
|
||||
painter.drawLine(mapping_width, 0, mapping_width, target_height)
|
||||
|
||||
# Fonts
|
||||
font_title = QFont("Outfit", 20)
|
||||
font_title.setBold(True)
|
||||
font_line = QFont("Outfit", 14)
|
||||
font_line.setBold(True)
|
||||
font_block = QFont("Outfit", 11)
|
||||
|
||||
# Title
|
||||
painter.setFont(font_title)
|
||||
painter.setPen(QColor("#ffffff" if self._theme == "dark" else "#0f172a"))
|
||||
title_rect = QRectF(mapping_width + 40, 40, sidebar_width - 80, 50)
|
||||
painter.drawText(title_rect, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft, "ЛЕГЕНДА МАППИНГА")
|
||||
|
||||
# Draw lines and blocks
|
||||
y = 120
|
||||
for line in self.document.lines:
|
||||
if y > target_height - 60:
|
||||
break
|
||||
|
||||
# Line swatch
|
||||
swatch_rect = QRectF(mapping_width + 40, y + 4, 24, 24)
|
||||
painter.setPen(Qt.NoPen)
|
||||
painter.setBrush(QBrush(QColor(line.color)))
|
||||
painter.drawRoundedRect(swatch_rect, 4, 4)
|
||||
|
||||
image = QImage(target_width, target_height, QImage.Format.Format_ARGB32)
|
||||
|
||||
bg_brush = scene.backgroundBrush()
|
||||
if bg_brush.style() != Qt.BrushStyle.NoBrush:
|
||||
image.fill(bg_brush.color())
|
||||
else:
|
||||
image.fill(QColor("#2b2b2b" if self._theme == "dark" else "#ffffff"))
|
||||
# Line name
|
||||
painter.setFont(font_line)
|
||||
painter.setPen(QColor("#f8fafc" if self._theme == "dark" else "#1e293b"))
|
||||
line_text_rect = QRectF(mapping_width + 80, y, sidebar_width - 120, 32)
|
||||
painter.drawText(line_text_rect, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft, line.name)
|
||||
y += 40
|
||||
|
||||
# Blocks under line
|
||||
painter.setFont(font_block)
|
||||
painter.setPen(QColor("#94a3b8" if self._theme == "dark" else "#64748b"))
|
||||
|
||||
current_offset = 0
|
||||
for block in line.blocks:
|
||||
if y > target_height - 40:
|
||||
break
|
||||
block_len = len(block.point_ids)
|
||||
if block_len == 0:
|
||||
block_range_str = "empty"
|
||||
else:
|
||||
start_idx = current_offset
|
||||
end_idx = current_offset + block_len - 1
|
||||
block_range_str = f"{start_idx}-{end_idx}"
|
||||
current_offset += block_len
|
||||
|
||||
block_text = f"• {block.name}: {block_range_str}"
|
||||
block_text_rect = QRectF(mapping_width + 100, y, sidebar_width - 140, 24)
|
||||
painter.drawText(block_text_rect, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft, block_text)
|
||||
y += 28
|
||||
|
||||
y += 12
|
||||
|
||||
painter = QPainter(image)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)
|
||||
|
||||
scene.render(painter, QRectF(0, 0, target_width, target_height), rect)
|
||||
painter.end()
|
||||
|
||||
# 4. Remove temporary badges from the scene
|
||||
for item in temp_items:
|
||||
scene.removeItem(item)
|
||||
|
||||
return image.save(file_path)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user