mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-29 02:08:08 +02:00
273 lines
6.7 KiB
QML
273 lines
6.7 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.11
|
|
|
|
import Controls 1.0
|
|
import MaterialIcons 2.2
|
|
import Utils 1.0
|
|
|
|
/**
|
|
* The Sequence Player is a UI for manipulating
|
|
* the currently selected (and displayed) viewpoint
|
|
* in an ordered set of viewpoints (i.e. a sequence).
|
|
*
|
|
* The viewpoint manipulation process can be manual
|
|
* (for example by dragging a slider to change the current frame)
|
|
* or automatic
|
|
* (by playing the sequence, i.e. incrementing the current frame at a given time rate).
|
|
*/
|
|
FloatingPane {
|
|
id: root
|
|
|
|
// Exposed properties
|
|
property var sortedViewIds: []
|
|
property var viewer: null
|
|
|
|
function updateReconstructionView() {
|
|
if (_reconstruction && m.frame >= 0 && m.frame < sortedViewIds.length) {
|
|
if (m.syncSelected) {
|
|
_reconstruction.selectedViewId = sortedViewIds[m.frame];
|
|
} else {
|
|
_reconstruction.pickedViewId = sortedViewIds[m.frame];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sequence player model:
|
|
// - current frame
|
|
// - data related to automatic sequence playing
|
|
QtObject {
|
|
id: m
|
|
|
|
property int frame: 0
|
|
property bool syncSelected: true
|
|
property bool playing: false
|
|
property bool repeat: false
|
|
property real fps: 24
|
|
|
|
onFrameChanged: {
|
|
updateReconstructionView();
|
|
}
|
|
|
|
onSyncSelectedChanged: {
|
|
updateReconstructionView();
|
|
}
|
|
|
|
onPlayingChanged: {
|
|
syncSelected = !playing;
|
|
if(playing && (frame + 1 >= sortedViewIds.length))
|
|
{
|
|
frame = 0;
|
|
}
|
|
viewer.playback(playing);
|
|
}
|
|
}
|
|
|
|
// Update the frame property
|
|
// when the selected view ID is changed externally
|
|
Connections {
|
|
target: _reconstruction
|
|
function onSelectedViewIdChanged() {
|
|
for (let idx = 0; idx < sortedViewIds.length; idx++) {
|
|
if (_reconstruction.selectedViewId === sortedViewIds[idx] && (m.frame != idx)) {
|
|
m.frame = idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// In play mode
|
|
// we use a timer to increment the frame property
|
|
// at a given time rate (defined by the fps property)
|
|
Timer {
|
|
id: timer
|
|
|
|
repeat: true
|
|
running: m.playing
|
|
interval: 1000 / m.fps
|
|
|
|
onTriggered: {
|
|
if (viewer.imageStatus !== Image.Ready) {
|
|
// Wait for current image to be displayed before switching to next image
|
|
return;
|
|
}
|
|
let nextIndex = m.frame + 1;
|
|
if (nextIndex == sortedViewIds.length) {
|
|
if (m.repeat) {
|
|
m.frame = 0;
|
|
return;
|
|
}
|
|
else {
|
|
m.playing = false;
|
|
return;
|
|
}
|
|
}
|
|
m.frame = nextIndex;
|
|
}
|
|
}
|
|
|
|
// Widgets:
|
|
// - "Previous Frame" button
|
|
// - "Play - Pause" button
|
|
// - "Next Frame" button
|
|
// - frame label
|
|
// - frame slider
|
|
// - FPS spin box
|
|
// - "Repeat" button
|
|
RowLayout {
|
|
|
|
anchors.fill: parent
|
|
|
|
MaterialToolButton {
|
|
id: prevButton
|
|
|
|
text: MaterialIcons.skip_previous
|
|
ToolTip.text: "Previous Frame"
|
|
|
|
onClicked: {
|
|
m.frame -= 1;
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: playButton
|
|
|
|
checkable: true
|
|
checked: false
|
|
text: checked ? MaterialIcons.pause : MaterialIcons.play_arrow
|
|
ToolTip.text: checked ? "Pause Player" : "Play Sequence"
|
|
|
|
onCheckedChanged: {
|
|
m.playing = checked;
|
|
}
|
|
|
|
Connections {
|
|
target: m
|
|
function onPlayingChanged() {
|
|
playButton.checked = m.playing;
|
|
}
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: nextButton
|
|
|
|
text: MaterialIcons.skip_next
|
|
ToolTip.text: "Next Frame"
|
|
|
|
onClicked: {
|
|
m.frame += 1;
|
|
}
|
|
}
|
|
|
|
Label {
|
|
id: frameLabel
|
|
|
|
text: m.frame
|
|
Layout.preferredWidth: frameMetrics.width
|
|
}
|
|
|
|
Slider {
|
|
id: frameSlider
|
|
|
|
Layout.fillWidth: true
|
|
|
|
stepSize: 1
|
|
snapMode: Slider.SnapAlways
|
|
live: true
|
|
enabled: !m.playing
|
|
|
|
from: 0
|
|
to: sortedViewIds.length - 1
|
|
|
|
onValueChanged: {
|
|
m.frame = value;
|
|
}
|
|
|
|
onPressedChanged: {
|
|
m.syncSelected = !pressed;
|
|
}
|
|
|
|
Connections {
|
|
target: m
|
|
function onFrameChanged() {
|
|
frameSlider.value = m.frame;
|
|
}
|
|
}
|
|
|
|
background: Rectangle {
|
|
x: frameSlider.leftPadding
|
|
y: frameSlider.topPadding + frameSlider.height / 2 - height / 2
|
|
width: frameSlider.availableWidth
|
|
height: 4
|
|
radius: 2
|
|
color: Colors.grey
|
|
|
|
Repeater {
|
|
id: cacheView
|
|
|
|
model: viewer ? viewer.cachedFrames : []
|
|
property real frameLength: sortedViewIds.length > 0 ? frameSlider.width / sortedViewIds.length : 0
|
|
|
|
Rectangle {
|
|
x: modelData.x * cacheView.frameLength
|
|
y: 0
|
|
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
|
|
height: 4
|
|
radius: 2
|
|
color: Colors.blue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Label {
|
|
text: "FPS:"
|
|
ToolTip.text: "Frame Per Second"
|
|
}
|
|
|
|
SpinBox {
|
|
id: fpsSpinBox
|
|
|
|
Layout.preferredWidth: fpsMetrics.width + up.implicitIndicatorWidth
|
|
|
|
from: 1
|
|
to: 60
|
|
stepSize: 1
|
|
value: m.fps
|
|
|
|
onValueChanged: {
|
|
m.fps = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: repeatButton
|
|
|
|
checkable: true
|
|
checked: false
|
|
text: MaterialIcons.repeat
|
|
ToolTip.text: "Repeat"
|
|
|
|
onCheckedChanged: {
|
|
m.repeat = checked;
|
|
}
|
|
}
|
|
}
|
|
|
|
TextMetrics {
|
|
id: frameMetrics
|
|
|
|
font: frameLabel.font
|
|
text: "10000"
|
|
}
|
|
|
|
TextMetrics {
|
|
id: fpsMetrics
|
|
|
|
font: fpsSpinBox.font
|
|
text: "100"
|
|
}
|
|
}
|