pyside6-qml-views について
pyside6-qml-viewsは、PySide6とQMLで画面を構築するための設計スキルです。表示とビジネスロジックを分離したい開発者向けに、ページ、再利用可能なコンポーネント、テーマ、読み込み状態、リソースの構成を扱います。
主な機能
- 01QMLによる宣言的な画面定義
- 02Pythonブリッジプロパティへのページ連携
- 03ボタンや検索バーなどの再利用
- 04テーマシングルトンによるスタイル設定
- 05Loaderによる条件付きコンテンツ読み込み
- 06リソースシステムでのアイコン読み込み
活用シーン
- 01複数ページのデスクトップ画面構築
- 02再利用可能なQMLコントロールの作成
- 03画面表示とI/Oロジックの分離
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ds-codi/ project-memory-mcp --skill pyside6-qml-viewsインストール後、Claude Code で/pyside6-qml-viewsを入力して読み込まれたか確認します。
詳細
PySide6 QML ビュー
このアーキテクチャのすべての UI は、.qml ファイルで宣言的に定義されています。QML ビューは Python ブリッジのプロパティにバインドし、ブリッジのスロットを呼び出します。ビジネスロジックは含みません。
QML ファイルの構成
resources/
├── qml/
│ ├── main.qml # Root window / StackLayout host
│ ├── components/
│ │ ├── ActionButton.qml # Reusable styled button
│ │ ├── StatusBadge.qml # Status indicator
│ │ ├── SearchBar.qml # Search input with debounce
│ │ ├── LoadingOverlay.qml # Busy spinner overlay
│ │ └── ErrorBanner.qml # Error message bar
│ ├── pages/
│ │ ├── JobListPage.qml # Job listing with cards
│ │ ├── JobDetailPage.qml # Single job detail view
│ │ ├── SettingsPage.qml # App settings form
│ │ └── DashboardPage.qml # Overview / landing page
│ ├── dialogs/
│ │ ├── CreateJobDialog.qml # Modal dialog for new job
│ │ └── ConfirmDialog.qml # Generic confirmation popup
│ └── styles/
│ ├── Theme.qml # Colour palette, spacing, fonts
│ └── qmldir # Module metadata for imports
├── icons/
│ ├── *.svg # Vector icons
│ └── *.png # Raster icons
└── qml.qrc # Qt resource file (optional)
ルートウィンドウ(main.qml)
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
ApplicationWindow {
id: root
visible: true
width: 1280
height: 720
title: "My Application"
// Page navigation
StackLayout {
id: pageStack
anchors.fill: parent
currentIndex: 0
JobListPage {}
JobDetailPage {}
SettingsPage {}
}
// Global toolbar
header: ToolBar {
RowLayout {
anchors.fill: parent
Label {
text: "My App"
font.bold: true
Layout.leftMargin: 12
}
Item { Layout.fillWidth: true }
ToolButton {
text: "Jobs"
onClicked: pageStack.currentIndex = 0
}
ToolButton {
text: "Settings"
onClicked: pageStack.currentIndex = 2
}
}
}
// Global error banner
ErrorBanner {
id: errorBanner
anchors { top: parent.top; left: parent.left; right: parent.right }
visible: jobBridge.errorMessage !== ""
message: jobBridge.errorMessage
}
// Loading overlay
LoadingOverlay {
anchors.fill: parent
visible: jobBridge.isBusy
}
}
ページのパターン
各ページは、ブリッジのプロパティにバインドする自己完結型の QML ファイルです。
// pages/JobListPage.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
id: jobListPage
header: ToolBar {
RowLayout {
anchors.fill: parent
SearchBar {
id: searchBar
Layout.fillWidth: true
onSearchTriggered: jobBridge.searchJobs(query)
}
ActionButton {
text: "New Job"
icon.name: "add"
onClicked: createJobDialog.open()
}
}
}
ListView {
id: jobsListView
anchors.fill: parent
model: jobListModel
spacing: 4
clip: true
delegate: ItemDelegate {
width: jobsListView.width
height: 64
contentItem: RowLayout {
spacing: 12
Label {
text: model.jobNumber
font.bold: true
Layout.preferredWidth: 100
}
Label {
text: model.jobName
Layout.fillWidth: true
elide: Text.ElideRight
}
StatusBadge {
status: model.status
}
}
onClicked: {
jobBridge.activateJob(model.jobNumber)
pageStack.currentIndex = 1 // navigate to detail
}
}
// Empty state
Label {
anchors.centerIn: parent
visible: jobsListView.count === 0
text: "No jobs found"
opacity: 0.5
}
}
CreateJobDialog {
id: createJobDialog
}
}
再利用可能なコンポーネントのパターン
コンポーネントのファイル構成
// components/ActionButton.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
// Custom properties
property color accentColor: "#1976D2"
property bool loading: false
enabled: !loading
opacity: enabled ? 1.0 : 0.5
contentItem: Row {
spacing: 8
BusyIndicator {
running: control.loading
visible: control.loading
width: 16; height: 16
}
Label {
text: control.text
color: "white"
verticalAlignment: Text.AlignVCenter
}
}
background: Rectangle {
radius: 4
color: control.down ? Qt.darker(accentColor, 1.2)
: control.hovered ? Qt.lighter(accentColor, 1.1)
: accentColor
}
}
カスタムシグナルを持つコンポーネント
// components/SearchBar.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
TextField {
id: searchField
signal searchTriggered(string query)
placeholderText: "Search..."
selectByMouse: true
// Debounced search
Timer {
id: debounceTimer
interval: 300
onTriggered: searchField.searchTriggered(searchField.text)
}
onTextChanged: debounceTimer.restart()
onAccepted: {
debounceTimer.stop()
searchTriggered(text)
}
}
ダイアログのパターン
// dialogs/CreateJobDialog.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Dialog {
id: dialog
title: "Create New Job"
modal: true
anchors.centerIn: Overlay.overlay
width: 400
standardButtons: Dialog.Ok | Dialog.Cancel
onAccepted: {
if (jobNumberInput.text.trim() !== "") {
jobBridge.createJob(jobNumberInput.text.trim())
}
}
onRejected: dialog.close()
// Reset on open
onOpened: {
jobNumberInput.text = ""
jobNumberInput.forceActiveFocus()
}
ColumnLayout {
anchors.fill: parent
spacing: 12
Label { text: "Job Number" }
TextField {
id: jobNumberInput
Layout.fillWidth: true
placeholderText: "e.g. 1234567"
validator: RegularExpressionValidator {
regularExpression: /^\d{5,8}[A-Z]?$/
}
}
Label {
text: "Enter a valid job number (5-8 digits, optional letter suffix)"
font.pixelSize: 11
opacity: 0.6
}
}
}
テーマ/スタイル設定
テーマシングルトン
// styles/Theme.qml
pragma Singleton
import QtQuick 2.15
QtObject {
// Colours
readonly property color primary: "#1976D2"
readonly property color primaryDark: "#1565C0"
readonly property color accent: "#FF9800"
readonly property color background: "#FAFAFA"
readonly property color surface: "#FFFFFF"
readonly property color error: "#D32F2F"
readonly property color textPrimary: "#212121"
readonly property color textSecondary: "#757575"
// Spacing
readonly property int spacingSmall: 4
readonly property int spacingMedium: 8
readonly property int spacingLarge: 16
readonly property int spacingXLarge: 24
// Typography
readonly property int fontSizeSmall: 12
readonly property int fontSizeMedium: 14
readonly property int fontSizeLarge: 18
readonly property int fontSizeTitle: 24
// Elevation / Radii
readonly property int borderRadius: 4
readonly property int cardRadius: 8
}
qmldir(モジュール登録)
// styles/qmldir
module Styles
singleton Theme 1.0 Theme.qml
使用方法
import "styles" as Styles
Rectangle {
color: Styles.Theme.surface
radius: Styles.Theme.cardRadius
Label {
color: Styles.Theme.textPrimary
font.pixelSize: Styles.Theme.fontSizeMedium
}
}
読み込み状態
// components/LoadingOverlay.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
id: overlay
color: "#80000000" // semi-transparent black
visible: false
z: 999
BusyIndicator {
anchors.centerIn: parent
running: overlay.visible
width: 48; height: 48
}
MouseArea {
anchors.fill: parent
// Block clicks through overlay
}
}
ステータスインジケーター
// components/StatusBadge.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
id: badge
property string status: ""
width: statusLabel.implicitWidth + 16
height: 24
radius: 12
color: {
switch (status.toLowerCase()) {
case "active": return "#4CAF50";
case "complete": return "#2196F3";
case "on_hold": return "#FF9800";
case "archived": return "#9E9E9E";
default: return "#BDBDBD";
}
}
Label {
id: statusLabel
anchors.centerIn: parent
text: badge.status
color: "white"
font.pixelSize: 11
font.bold: true
}
}
QML のベストプラクティス
| ルール | 理由 |
|---|---|
| コンポーネントは 150 行未満に保つ | 保守性を高めます。サブコンポーネントを切り出します |
| ファイルごとにルートアイテムを 1 つにする | QML の慣例です |
参照する場合にのみ id を使う | 不要な識別子を避けます |
| 命令型 JS よりプロパティバインディングを優先する | 宣言的な更新ができ、バグが減ります |
重いコンテンツや条件付きコンテンツには Loader を使う | 遅延インスタンス化によりメモリを節約できます |
| QML の JS に SQL、HTTP、ファイル I/O を埋め込まない | すべての副作用はブリッジのスロットを経由させます |
プロパティアクセスを明示する(root.width と width の使い分け) | ネストされたアイテムでの名前の衝突を避けます |
| 手動の x/y 指定ではなく、アンカーまたはレイアウトを使う | レスポンシブで保守しやすくなります |
リソースの読み込み
ファイルシステムからのアイコン読み込み
Image {
source: "file:///" + Qt.resolvedUrl("../../icons/logo.svg")
}
Qt リソースシステム経由のアイコン読み込み
Image {
source: "qrc:/icons/logo.svg"
}
Qt リソースファイル(qml.qrc)
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/components/ActionButton.qml</file>
<file>icons/logo.svg</file>
</qresource>
</RCC>
参考資料
コメント
Markdown 対応