スキル一覧に戻る
PROdotes

glow-component-factory

by PROdotes

MP3 library manager and auto player

0🍴 0📅 2026年1月23日
GitHubで見るManusで実行

SKILL.md


name: Glow Component Factory description: Step-by-step guide for creating Industrial Amber UI components following the GlowFactory pattern.

Glow Component Factory Workflow

This skill guides the creation of new Industrial Amber design system components for Gosling2.

The Glow Component Philosophy

Core Principles:

  1. NO Inline Styles: All styling via theme.qss and objectName
  2. Consistent Factory: Always extend GlowWidget base classes
  3. Amber Aesthetic: Dark background, amber accents, tactical readability
  4. Signal-Based: Components communicate via signals, not direct references

Component Creation Workflow

Phase 1: Design & Planning

Step 1.1: Define Component Purpose

Ask yourself:

  • What is the component's primary function?
  • Is it an input (button, line edit) or display (label, LED)?
  • Does it need state (active/inactive, selected/unselected)?
  • What signals does it need to emit?

Step 1.2: Check for Existing Components

Before creating new component, check:

src/presentation/widgets/glow/
    __init__.py
    base.py          # GlowWidget base class
    button.py        # GlowButton
    line_edit.py     # GlowLineEdit
    combo_box.py     # GlowComboBox
    led.py           # GlowLED
    toggle.py        # GlowToggle
    tooltip.py       # GlowTooltip

Can you extend or compose existing components instead?

Step 1.3: Choose Base Class

  • Interactive Elements: Extend GlowWidget
  • Buttons: Extend GlowButton
  • Input Fields: Extend QLineEdit or QTextEdit
  • Display Elements: Extend QLabel or QFrame

Phase 2: Implementation

Step 2.1: Create Component File

Location: src/presentation/widgets/glow/{component_name}.py

Template:

from PyQt6.QtWidgets import QWidget
from PyQt6.QtCore import pyqtSignal
from .base import GlowWidget  # If extending GlowWidget


class Glow{ComponentName}(QWidget):  # Or extend GlowWidget
    """
    Brief description of component's purpose.

    Signals:
        signal_name: When signal is emitted
    """

    # Define signals
    value_changed = pyqtSignal(object)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._init_properties()
        self._init_ui()
        self._connect_signals()

    def _init_properties(self):
        """Initialize internal state"""
        self._value = None
        self._is_active = False

    def _init_ui(self):
        """Create and layout child widgets"""
        # Create widgets
        # Set object names
        # Create layouts
        # Apply layouts

    def _connect_signals(self):
        """Connect internal signals/slots"""
        pass

    # Public API
    def set_value(self, value):
        """Set component value"""
        self._value = value
        self._update_ui()
        self.value_changed.emit(value)

    def get_value(self):
        """Get current value"""
        return self._value

    # Private helpers
    def _update_ui(self):
        """Update visual state based on internal state"""
        pass

Step 2.2: Set Object Names (for QSS styling)

def _init_ui(self):
    # Main widget
    self.setObjectName("GlowMyComponent")

    # Child widgets
    self.label = QLabel("Label")
    self.label.setObjectName("GlowLabel")

    self.input = QLineEdit()
    self.input.setObjectName("GlowInput")

Step 2.3: Implement Glow Effects (if interactive)

For hover/active states:

def enterEvent(self, event):
    """Mouse enter - trigger glow"""
    self.setProperty("glow", "active")
    self.style().polish(self)  # Reapply stylesheet
    super().enterEvent(event)

def leaveEvent(self, event):
    """Mouse leave - remove glow"""
    self.setProperty("glow", "inactive")
    self.style().polish(self)
    super().leaveEvent(event)

Phase 3: Styling (theme.qss)

Step 3.1: Add QSS Rules

Location: src/resources/theme.qss

Template:

/* Glow{ComponentName} - Brief description */
QWidget#GlowMyComponent {
    background-color: #000000;
    border: 1px solid #FFC66D;
    border-radius: 4px;
    padding: 8px;
}

QWidget#GlowMyComponent[glow="active"] {
    border: 2px solid #FFC66D;
    box-shadow: 0 0 12px rgba(255, 198, 109, 0.6);
}

QLabel#GlowLabel {
    color: #FFC66D;
    font-family: "Bahnschrift Condensed", sans-serif;
    font-size: 11pt;
    text-transform: uppercase;
}

Step 3.2: Color Palette Usage

Use Industrial Amber colors:

/* Amber - Primary accent */
color: #FFC66D;
border-color: #FFC66D;

/* Muted Amber - Secondary */
color: #FF8C00;

/* Magenta - Critical actions */
background-color: #FF00FF;

/* Cyan - Unprocessed data */
color: #00E5FF;

/* Red - Errors */
color: #FF4444;

/* Void - Backgrounds */
background-color: #000000;

Step 3.3: Typography

font-family: "Bahnschrift Condensed", "Segoe UI", sans-serif;
font-size: 11pt;  /* Body text */
font-size: 14pt;  /* Headers */
text-transform: uppercase;  /* For buttons and labels */
letter-spacing: 1px;  /* Tactical spacing */

Phase 4: Integration

Step 4.1: Export from Glow Module

Update src/presentation/widgets/glow/__init__.py:

from .button import GlowButton
from .line_edit import GlowLineEdit
from .my_component import GlowMyComponent  # Add new component

__all__ = [
    'GlowButton',
    'GlowLineEdit',
    'GlowMyComponent',  # Export
]

Step 4.2: Use in Parent Widget

from ..widgets.glow import GlowMyComponent

class MyFeatureWidget(QWidget):
    def _init_ui(self):
        # Create component
        self.my_component = GlowMyComponent()
        self.my_component.value_changed.connect(self._on_value_changed)

        # Add to layout
        layout.addWidget(self.my_component)

Phase 5: Testing

Step 5.1: Visual Testing

  1. Run application
  2. Navigate to component
  3. Verify:
    • Colors match Industrial Amber palette
    • Glow effects work on hover
    • Text is readable
    • Component responds to interaction

Step 5.2: Unit Testing

Create tests/unit/presentation/widgets/glow/test_my_component.py:

import pytest
from PyQt6.QtCore import Qt
from src.presentation.widgets.glow import GlowMyComponent


def test_component_creation(qtbot):
    """Test component can be created"""
    component = GlowMyComponent()
    qtbot.addWidget(component)
    assert component is not None


def test_value_changed_signal(qtbot):
    """Test signal emission on value change"""
    component = GlowMyComponent()
    qtbot.addWidget(component)

    with qtbot.waitSignal(component.value_changed, timeout=1000):
        component.set_value("test")

    assert component.get_value() == "test"


def test_mouse_interaction(qtbot):
    """Test hover effects"""
    component = GlowMyComponent()
    qtbot.addWidget(component)

    # Simulate hover
    qtbot.mouseMove(component)
    # Verify property changed (if applicable)

Step 5.3: Integration Testing

Test in actual UI context:

def test_component_in_parent_widget(qtbot):
    """Test component works within parent widget"""
    parent = MyFeatureWidget()
    qtbot.addWidget(parent)

    # Interact with component
    component = parent.my_component
    component.set_value("test")

    # Verify parent receives signal
    assert parent.current_value == "test"

Common Component Patterns

Pattern 1: Stateful Toggle

class GlowToggle(GlowWidget):
    toggled = pyqtSignal(bool)

    def __init__(self):
        super().__init__()
        self._is_on = False

    def mousePressEvent(self, event):
        self._is_on = not self._is_on
        self.toggled.emit(self._is_on)
        self._update_visual_state()

Pattern 2: Value Display with Units

class GlowValueDisplay(QWidget):
    def __init__(self, label: str, unit: str = ""):
        super().__init__()
        self._label = label
        self._unit = unit
        self._value = None

    def set_value(self, value: float):
        self._value = value
        self._update_display()

    def _update_display(self):
        text = f"{self._label}: {self._value} {self._unit}"
        self.label_widget.setText(text)

Pattern 3: Multi-State Indicator

class GlowStatusLED(GlowLED):
    class State(Enum):
        OFF = "#333333"
        ACTIVE = "#FFC66D"
        WARNING = "#FF8C00"
        ERROR = "#FF4444"

    def set_state(self, state: State):
        self.setGlowColor(state.value)
        self.setActive(state != State.OFF)

Industrial Amber Design Guidelines

Spacing

  • Tight: 4px (within components)
  • Normal: 8px (between related components)
  • Wide: 16px (between sections)

Borders

  • Standard: 1px solid
  • Active: 2px solid with glow
  • Separator: 7px solid black bar

Glow Effects

/* Standard glow */
box-shadow: 0 0 8px rgba(255, 198, 109, 0.4);

/* Strong glow (active state) */
box-shadow: 0 0 16px rgba(255, 198, 109, 0.8);

/* Pulsing glow (critical state) */
animation: glow-pulse 1.5s ease-in-out infinite;

@keyframes glow-pulse {
    0%, 100% { box-shadow: 0 0 8px rgba(255, 198, 109, 0.4); }
    50% { box-shadow: 0 0 20px rgba(255, 198, 109, 1.0); }
}

Text Treatment

  • All Caps for Controls: "SAVE", "CANCEL", "APPLY"
  • Title Case for Data: "Artist Name", "Song Title"
  • Uppercase for Status: "READY", "PROCESSING", "ERROR"

Anti-Patterns to Avoid

❌ Inline Styles

# BAD
button.setStyleSheet("background-color: red;")

# GOOD
button.setObjectName("ErrorButton")
# Define #ErrorButton in theme.qss

❌ Raw PyQt Widgets

# BAD
button = QPushButton("Click Me")

# GOOD
from ..widgets.glow import GlowButton
button = GlowButton("CLICK ME")

❌ Hardcoded Colors

# BAD
painter.setPen(QColor(255, 198, 109))

# GOOD
painter.setPen(QColor("#FFC66D"))  # Or define in constants

❌ Fixed Sizes

# BAD
widget.setFixedSize(200, 100)

# GOOD
widget.setMinimumSize(200, 100)
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

Completion Checklist

Before considering component complete:

  • ✅ Extends appropriate GlowWidget base
  • ✅ Has clear, descriptive objectName
  • ✅ All styling in theme.qss (no inline styles)
  • ✅ Uses Industrial Amber color palette
  • ✅ Emits appropriate signals
  • ✅ Has docstring explaining purpose
  • ✅ Exported from glow/init.py
  • ✅ Has unit tests
  • ✅ Manually tested in UI
  • ✅ Follows naming conventions

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です