AI_Development_Automation_Manager / adam /ui /generation_widgets.py
SyntheticMDProductions's picture
ADAM October 2026 source release: PixelRow, INRFlow, Wan Video, Oasis player and field guide
f8c73f9 verified
Raw History Blame Contribute Delete
6.85 kB
"""Small, size-aware widgets used by the generation workspace."""
from __future__ import annotations
from PySide6.QtCore import QRect, QSize, Qt
from PySide6.QtGui import QColor, QPainter, QPainterPath, QPixmap
from PySide6.QtWidgets import (
QFrame, QHBoxLayout, QLabel, QListWidget, QSizePolicy,
QStyledItemDelegate, QStyle, QVBoxLayout, QWidget,
)
class ImagePreview(QWidget):
"""Paint an image without letting its pixel dimensions enlarge the layout."""
def __init__(self, text: str = "No image selected", parent=None):
super().__init__(parent)
self._image = QPixmap()
self._text = text
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding)
self.setMinimumSize(80, 80)
def sizeHint(self):
return QSize(360, 260)
def setPixmap(self, image: QPixmap):
self._image = image
self.update()
def setText(self, text: str):
self._text = text
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.fillRect(self.rect(), QColor("#040d15"))
if self._image.isNull():
painter.setPen(QColor("#89a7bc"))
painter.drawText(self.rect().adjusted(18, 12, -18, -12), Qt.AlignCenter | Qt.TextWordWrap, self._text)
return
size = self._image.size()
size.scale(self.size() - QSize(12, 12), Qt.KeepAspectRatio)
destination = QRect(0, 0, size.width(), size.height())
destination.moveCenter(self.rect().center())
painter.setRenderHint(QPainter.SmoothPixmapTransform)
painter.drawPixmap(destination, self._image)
class ElidedLabel(QLabel):
"""Single-line metadata that cannot force a panel wider than its contents."""
def __init__(self, text="", parent=None):
super().__init__(text, parent)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)
self.setMinimumWidth(0)
self.setMinimumHeight(20)
self.setTextFormat(Qt.PlainText)
def setText(self, text):
super().setText(text)
self.setToolTip(text)
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(self.palette().color(self.foregroundRole()))
painter.drawText(self.contentsRect(), Qt.AlignVCenter | Qt.AlignLeft,
self.fontMetrics().elidedText(self.text(), Qt.ElideRight, self.contentsRect().width()))
def section(number: str, title: str, subtitle: str):
card = QFrame()
card.setObjectName("GenerationSection")
layout = QVBoxLayout(card)
layout.setContentsMargins(12, 10, 12, 12)
layout.setSpacing(8)
header = QHBoxLayout()
badge = QLabel(number)
badge.setObjectName("GenerationNumber")
badge.setAlignment(Qt.AlignCenter)
badge.setFixedSize(30, 30)
header.addWidget(badge)
labels = QVBoxLayout()
labels.setSpacing(0)
heading = QLabel(title)
heading.setObjectName("GenerationHeading")
labels.addWidget(heading)
if subtitle:
detail = QLabel(subtitle)
detail.setProperty("muted", True)
detail.setWordWrap(True)
labels.addWidget(detail)
header.addLayout(labels, 1)
layout.addLayout(header)
return card, layout, header
def field(label: str, widget: QWidget):
container = QWidget()
layout = QVBoxLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(4)
layout.addWidget(QLabel(label))
layout.addWidget(widget)
container.setMinimumWidth(0)
return container
class GeneratorCardDelegate(QStyledItemDelegate):
def sizeHint(self, option, index):
size = self.parent().gridSize()
return QSize(max(176, size.width()), 182)
def paint(self, painter, option, index):
painter.save()
painter.setRenderHint(QPainter.Antialiasing)
rect = option.rect.adjusted(4, 4, -4, -4)
selected = bool(option.state & QStyle.State_Selected)
hovered = bool(option.state & QStyle.State_MouseOver)
painter.setPen(QColor("#16b7f2" if selected or hovered else "#204c68"))
painter.setBrush(QColor("#0b2030" if selected or hovered else "#081925"))
painter.drawRoundedRect(rect, 7, 7)
image_rect = rect.adjusted(5, 5, -5, -69)
icon = index.data(Qt.DecorationRole)
if icon:
image = icon.pixmap(QSize(480, 240))
if not image.isNull():
image = image.scaled(image_rect.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)
painter.save()
clip = QPainterPath()
clip.addRoundedRect(image_rect, 4, 4)
painter.setClipPath(clip)
painter.drawPixmap(image_rect.center().x() - image.width() // 2,
image_rect.center().y() - image.height() // 2, image)
painter.restore()
lines = str(index.data(Qt.DisplayRole) or "").split("\n")
painter.setPen(QColor("#edf5fb"))
font = painter.font()
font.setBold(True)
painter.setFont(font)
painter.drawText(rect.adjusted(10, rect.height() - 64, -10, -42), Qt.AlignVCenter,
painter.fontMetrics().elidedText(lines[0], Qt.ElideRight, rect.width() - 20))
font.setBold(False)
painter.setFont(font)
painter.setPen(QColor("#94b3c9"))
painter.drawText(rect.adjusted(10, rect.height() - 43, -10, -22), Qt.AlignVCenter,
lines[1] if len(lines) > 1 else "")
painter.setPen(QColor("#38c4ff"))
painter.drawText(rect.adjusted(10, rect.height() - 24, -10, -3), Qt.AlignVCenter, "Open →")
painter.restore()
class GeneratorCards(QListWidget):
def __init__(self):
super().__init__()
self.setViewMode(QListWidget.IconMode)
self.setResizeMode(QListWidget.Adjust)
self.setMovement(QListWidget.Static)
self.setMouseTracking(True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setItemDelegate(GeneratorCardDelegate(self))
self.setGridSize(QSize(220, 182))
self.setMinimumHeight(190)
self.setMaximumHeight(212)
def resizeEvent(self, event):
super().resizeEvent(event)
self.fit_cards()
def keyPressEvent(self, event):
if event.key() in (Qt.Key_Return, Qt.Key_Enter) and self.currentItem() is not None:
self.itemClicked.emit(self.currentItem())
event.accept()
return
super().keyPressEvent(event)
def fit_cards(self):
available = max(180, self.viewport().width() - 14)
columns = max(1, min(max(1, self.count()), 4, available // 176))
self.setGridSize(QSize(available // columns, 182))