QML:如何从Footer访问GridView所有Delegate实例的Stream功能?
The Problem
I've spent a lot of time searching Google/Stack Overflow for a solution to this, but haven't found anything that works. I understand that you can't directly access child element IDs/properties without special tricks. I tried using multiple alias properties, but couldn't build an alias chain since I can't define properties in the Component.
My goal is to display all videos via MainGrid.qml (a GridView component) and implement playback controls in Footer.qml. Here's my current code setup:
MainGrid.qml
GridView { x: videoWallWindow.x y: videoWallWindow.y width: videoWallWindow.width height: videoWallWindow.height cellWidth: 300 cellHeight: 300 property alias deleg : loader.item Loader { id: loader sourceComponent: videoDelegate visible: false } Component { id: videoDelegate Rectangle { id: container property alias streamVid : stream width: 256 height: 240 color: "#4f4f4f" border.color: "black" border.width: 3 Stream { id: stream } Playbar { x: 0 y: container.height - 20 width: container.width height: 20 } Text { id: textId text: name // defined in a ListModel } } } model: StreamList {} delegate: videoDelegate focus: true }
Footer.qml
ToolBar { property MainGrid myGrid id: footer x: 0 y: window.height - footer.height width: window.width background: Rectangle { implicitHeight: 45 color: "#212121" Rectangle { width: parent.width height: 1 anchors.bottom: parent.bottom color: "transparent" border.color: "#212121" } } Row { anchors.centerIn: parent PlaybarButton { id: pauseButton height: footer.height width: footer.height property bool currentState: true onClicked: { // This only accesses the Loader's single instance, not all delegates currentState ? myGrid.deleg.streamVid.pause() : myGrid.deleg.streamVid.play() currentState = !currentState } Image { id: pauseImage anchors.fill: parent source: parent.currentState ? ("pics/pause.png") : ("pics/play.png") } } } }
Stream.qml
Video { id: streamVideo width: mainGrid.cellWidth height: mainGrid.cellHeight anchors.fill: parent source: portrait // defined as the path in a ListModel MouseArea { anchors.fill: parent onClicked: { // Note: Typo here - should reference streamVideo, not video streamVideo.playbackState == MediaPlayer.PlayingState ? streamVideo.pause() : streamVideo.play() } } autoPlay: true loops: MediaPlayer.Infinite focus: true }
main.qml
ApplicationWindow { id: window visible: true width: Screen.desktopAvailableWidth height: Screen.desktopAvailableHeight ... MainGrid { id: mainGrid } Footer { id: footer myGrid: mainGrid } }
I need to figure out how to properly access all Delegate video/stream instances in the GridView from Footer.qml. Do I need to completely refactor my existing code?
The Solution
You don't need a full refactor—your core issue is that the Loader + alias approach only gives you access to a single hidden delegate instance, not all the live ones created by the GridView for each model item. Here's a straightforward fix:
Step 1: Track All Active Delegates in MainGrid
Add a property to MainGrid to keep a list of all active delegate instances, then update the delegate to register/unregister itself when it's created/destroyed:
GridView { // ... existing properties ... property var activeDelegates: [] // New property to track live delegates Component { id: videoDelegate Rectangle { id: container property alias streamVid : stream // ... existing delegate code ... // Add these two handlers to manage the delegate list Component.onCompleted: mainGrid.activeDelegates.push(this) Component.onDestruction: mainGrid.activeDelegates.remove(mainGrid.activeDelegates.indexOf(this)) } } // Optional: Add helper methods to control all streams (cleaner than accessing the list directly) function pauseAllStreams() { activeDelegates.forEach(delegate => { delegate.streamVid.pause() }) } function playAllStreams() { activeDelegates.forEach(delegate => { delegate.streamVid.play() }) } }
Step 2: Update Footer to Control All Streams
Modify the Footer's button click handler to use the helper methods (or directly iterate the delegate list) to control every video:
PlaybarButton { id: pauseButton // ... existing button code ... onClicked: { // Use the helper methods for cleaner code currentState ? myGrid.pauseAllStreams() : myGrid.playAllStreams() currentState = !currentState } }
Quick Fix for Stream.qml
Also, fix the typo in your Stream.qml MouseArea—you were referencing an undefined video ID instead of streamVideo:
MouseArea { anchors.fill: parent onClicked: { streamVideo.playbackState == MediaPlayer.PlayingState ? streamVideo.pause() : streamVideo.play() } }
Why This Works
- The
Component.onCompletedandComponent.onDestructionhandlers ensure every delegate adds itself to theactiveDelegateslist when it's created, and removes itself when it's destroyed (e.g., when scrolling off-screen or the model changes). - The helper methods in
MainGridencapsulate the logic to control all streams, keeping your Footer code clean and decoupled from the GridView's internal structure.
You don't need to overhaul your entire setup—this small adjustment will let you control all video instances from the Footer.
内容的提问来源于stack exchange,提问作者NhatNienne

