本文へ移動
cccskills
無料GitHub で公開

pyqt-styling

Use when styling PyQt/PySide6 widgets with QSS - selectors and pseudo-states, stylesheet application, common style properties, widget-specific styling, or building a dark theme

インストール方法を見る

含まれるファイル(2)

  • SKILL.md11.4 KB
  • references/dark-theme.md4.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

PyQt Styling - QSS (Qt Style Sheets)

QSS (Qt Style Sheets) is Qt's styling system. It resembles CSS but has critical differences that cause cross-platform bugs.

QSS Is Not CSS

QSS shares CSS syntax but differs in supported selectors, properties, and cascade behavior. These differences are the source of most cross-platform bugs.

Unsupported or Different Selectors

SelectorCSSQSS
::pseudo-elementSupported (e.g., ::before, ::after)Limited support - only Qt-specific pseudo-elements like ::indicator, ::drop-down, ::item
Descendant selector (A B)Matches nested elementsDoes not work in most widgets - Qt uses parent-to-child cascade instead
Child selector (A > B)Matches direct childrenPartially supported, but widget hierarchy matters more than DOM-like nesting
Sibling selector (A + B, A ~ B)SupportedNot supported - widgets don't have sibling relationships like DOM
Attribute selectors ([attr])Full supportSupported via setProperty() but syntax differs ([attr="value"] works)
Class selector (.my-class)SupportedNot supported - use objectName or custom properties instead

Properties Qt Ignores

Qt silently ignores CSS properties it doesn't implement:

  • box-shadow - Qt uses custom properties or qproperty- prefix instead
  • text-shadow - Not implemented
  • filter - Not implemented (use QGraphicsEffect instead)
  • transform - Not implemented (use widget geometry methods)
  • position: absolute/fixed - Qt uses layouts or manual geometry
  • display: flex/grid - Qt uses QLayout classes, not CSS display
  • ::before, ::after - Not supported (use child widgets instead)

Border-Radius Clipping Issue

border-radius on a parent widget does not clip child widgets unless the parent sets a mask:

# Bad - children overflow rounded corners
parent.setStyleSheet("border-radius: 10px;")
parent.addWidget(child)  # child shows outside rounded area

# Good - use a mask
from PySide6.QtGui import QRegion
from PySide6.QtCore import QRect

parent.setMask(QRegion(QRect(0, 0, parent.width(), parent.height()), QRegion.Ellipse))
# Or use a custom paintEvent for rounded rectangle mask

Cascade Behavior

Widgets cascade styles from parent to child differently from the DOM:

  • DOM: Styles cascade through the document tree based on selector specificity
  • Qt: Styles applied to a parent widget propagate to children only if the child has no explicit stylesheet
  • Key difference: Setting app.setStyleSheet() applies globally, but parent.setStyleSheet() only affects children that don't have their own stylesheet set
# Bad - child overrides parent
app.setStyleSheet("QPushButton { background: blue; }")
child_button.setStyleSheet("")  # Empty string clears inherited style

# Good - use custom properties for theming
app.setStyleSheet("QPushButton { background: $primary-color; }")
child_button.setProperty("primary-color", "#0078d4")
child_button.style().polish(child_button)

Platform Differences

The same QSS renders differently across Windows, macOS, and Linux due to native widget rendering.

Default Widget Metrics

  • Font sizes: macOS uses larger default fonts; Windows uses smaller
  • Padding: Native look varies - macOS buttons have more padding, Windows has less
  • Line heights: Not consistent across platforms

Fix: Use explicit min-height, padding on critical widgets rather than relying on defaults.

Native-Appearance Opt-Out

Qt widgets may render with native platform styling that ignores QSS:

# Force QSS rendering on Windows/macOS
widget.setAttribute(Qt.WidgetAttribute.WA_StyleSheet)

# For QComboBox dropdown - native rendering ignores QSS
combo.setStyleSheet("QComboBox::drop-down { border: none; }")
combo.view().setAttribute(Qt.WidgetAttribute.WA_StyleSheet)

High-DPI Scaling

QSS uses device-independent pixels, but scaling behavior varies:

# Set high-DPI scaling policy (Qt 6)
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import Qt

app = QApplication(sys.argv)
app.setHighDpiScaleFactorRoundingPolicy(
    Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)

Verification on a platform you don't have: Use Qt's remote desktop or CI services (GitHub Actions with Xvfb), or test in a VM with the target OS.

Theming Discipline

Tokenize Your Theme

A theme should be a single QSS source with placeholder/palette substitution, not per-widget stylesheets:

# Bad - per-widget stylesheets
class MainWindow(QMainWindow):
    def __init__(self):
        self.button1.setStyleSheet("background: #0078d4;")
        self.button2.setStyleSheet("background: #0078d4;")
        self.label.setStyleSheet("color: #333;")

# Good - tokenized theme
class Theme:
    @staticmethod
    def apply(app):
        app.setStyleSheet("""
            QPushButton { background-color: $primary; }
            QLabel { color: $text-primary; }
        """.replace("$primary", "#0078d4").replace("$text-primary", "#333"))

# Or use a palette
from PySide6.QtGui import QPalette, QColor

class Theme:
    @staticmethod
    def apply_dark(app):
        palette = QPalette()
        palette.setColor(QPalette.ColorRole.Window, QColor("#1e1e1e"))
        palette.setColor(QPalette.ColorRole.WindowText, QColor("#ffffff"))
        app.setPalette(palette)

Test a Theme Change Across Widget Types

Create a gallery page to verify theme changes:

class ThemeGallery(QWidget):
    def __init__(self):
        super().__init__()
        # Create one of each widget type
        self.buttons = [QPushButton(f"Button {i}") for i in range(3)]
        self.inputs = [QLineEdit(f"Input {i}") for i in range(3)]
        self.labels = [QLabel(f"Label {i}") for i in range(3)]
        # Layout them all
        # Switch themes with a button to verify consistency

When to Use Custom paintEvent/Delegates

Use custom painting instead of fighting QSS when:

  • You need gradients, shadows, or complex shapes QSS can't express
  • You need per-pixel control (e.g., custom progress bar animation)
  • QSS performance is poor with many widgets
  • You need platform-independent rendering (QSS varies by platform)
class RoundedButton(QPushButton):
    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        
        # Custom rounded rectangle with gradient
        gradient = QLinearGradient(0, 0, 0, self.height())
        gradient.setColorAt(0, "#0078d4")
        gradient.setColorAt(1, "#106ebe")
        
        painter.setBrush(gradient)
        painter.setPen(Qt.PenStyle.NoPen)
        painter.drawRoundedRect(self.rect().adjusted(1, 1, -1, -1), 8, 8)

Basic Syntax (Quick Reference)

Assumes familiarity with CSS selectors and specificity. Focuses on Qt-specific syntax.

Applying Styles

Application-Wide

from PySide6.QtWidgets import QApplication

app = QApplication()

# Inline
app.setStyleSheet("""
    QLabel { color: #333; }
    QPushButton { padding: 5px 10px; }
""")

# From file
with open("style.qss", "r") as f:
    app.setStyleSheet(f.read())

Widget-Specific

button = QPushButton("Styled")
button.setStyleSheet("""
    QPushButton {
        background-color: blue;
        color: white;
        border-radius: 5px;
    }
    QPushButton:hover {
        background-color: darkblue;
    }
""")

Custom Properties

# Set custom property
button = QPushButton("Primary")
button.setProperty("primary", True)

# Force style refresh
button.style().unpolish(button)
button.style().polish(button)
/* Use in QSS */
QPushButton[primary="true"] {
    background-color: #0078d4;
    color: white;
}

QPushButton[primary="true"]:hover {
    background-color: #106ebe;
}

Widget-Specific Styles

QPushButton

QPushButton {
    background-color: #0078d4;
    color: white;
    border: none;
    border-radius: 4px;
    padding: 8px 16px;
    font-weight: bold;
}

QPushButton:hover {
    background-color: #106ebe;
}

QPushButton:pressed {
    background-color: #005a9e;
}

QPushButton:disabled {
    background-color: #cccccc;
    color: #666666;
}

/* Flat button */
QPushButton[flat="true"] {
    background-color: transparent;
    color: #0078d4;
    border: 1px solid #0078d4;
}

QLineEdit

QLineEdit {
    background-color: white;
    border: 1px solid #cccccc;
    border-radius: 4px;
    padding: 4px 8px;
    selection-background-color: #0078d4;
}

QLineEdit:focus {
    border: 2px solid #0078d4;
}

QLineEdit:disabled {
    background-color: #f5f5f5;
    color: #999999;
}

/* Password field */
QLineEdit[echoMode="2"] {
    lineedit-password-character: 9679;  /* Unicode bullet */
}

QComboBox

QComboBox {
    background-color: white;
    border: 1px solid #cccccc;
    border-radius: 4px;
    padding: 4px 8px;
}

QComboBox:hover {
    border-color: #999999;
}

QComboBox::drop-down {
    border: none;
    width: 24px;
}

QComboBox::down-arrow {
    image: url(down_arrow.png);
    width: 12px;
    height: 12px;
}

/* Dropdown list */
QComboBox QAbstractItemView {
    background-color: white;
    border: 1px solid #cccccc;
    selection-background-color: #0078d4;
}

QTabWidget

QTabWidget::pane {
    border: 1px solid #cccccc;
    border-radius: 4px;
}

QTabBar::tab {
    background-color: #f5f5f5;
    border: 1px solid #cccccc;
    padding: 8px 16px;
    margin-right: 2px;
}

QTabBar::tab:selected {
    background-color: white;
    border-bottom-color: white;
}

QTabBar::tab:hover {
    background-color: #e5e5e5;
}

QScrollBar

/* Vertical scrollbar */
QScrollBar:vertical {
    background-color: #f5f5f5;
    width: 12px;
    margin: 0;
}

QScrollBar::handle:vertical {
    background-color: #cccccc;
    border-radius: 6px;
    min-height: 30px;
}

QScrollBar::handle:vertical:hover {
    background-color: #999999;
}

QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
    height: 0;
}

Deep Dives

Best Practices

  1. Tokenize themes - Single QSS source with placeholder substitution, not per-widget stylesheets
  2. Test on all platforms - Colors, fonts, and native rendering vary significantly
  3. Use custom properties - setProperty() for theme variables instead of hardcoding colors
  4. Prefer delegates for complex rendering - Don't fight QSS for gradients, animations, or platform-independent drawing
  5. Verify border-radius clipping - Use masks or custom paintEvent if children must respect rounded corners
  6. Block signals during bulk updates - widget.blockSignals(True) before batch changes, then blockSignals(False)

References

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

aiohttp

無料

Use when building Python async HTTP services or clients with aiohttp - web server routing, middleware, WebSocket, SSE, streaming, client sessions, pytest-aiohttp testing, or troubleshooting SSL and timeout issues

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

ast-grep

無料

Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

Use when building GBA games with the BPCore Lua engine - entity, sprite and tilemap functions, SRAM save and load, link cable multiplayer protocol, camera and scrolling, or optimization patterns

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

celery

無料

Use when running background tasks with Celery - worker and broker configuration (Redis, RabbitMQ), task routing by name vs queue, chains/groups/chords, retry patterns (autoretry_for, retry_backoff), acks_late semantics, failure detection, and monitoring with Flower

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

django

無料

Use when building Django applications - security hardening, authentication and permissions, ORM optimization, PostgreSQL features, Django 6.0, migrations, testing, and ecosystem libraries

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

Use when customizing Django Admin - save_formset, get_search_results, formsets, queryset optimization, db_index, custom URLs

日本語の概要は準備中です。原文の説明を表示しています。

CodeAtCode/oss-ai-skills222026年10月9日 更新

CodeAtCode のスキルをすべて見る

このスキルの問題を報告する