mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-28 17:57:16 +02:00
459 lines
15 KiB
QML
459 lines
15 KiB
QML
import QtCore
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
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
|
|
property bool isOutputSequence: false
|
|
readonly property alias sync3DSelected: m.sync3DSelected
|
|
readonly property alias syncFeaturesSelected: m.syncFeaturesSelected
|
|
property bool loading: fetchButton.checked || m.playing
|
|
property alias settings_SequencePlayer: settings_SequencePlayer
|
|
property alias frameId: m.frame
|
|
property var frameRange: { "min" : 0, "max" : 0 }
|
|
|
|
Settings {
|
|
id: settings_SequencePlayer
|
|
property int maxCacheMemory: viewer && viewer.ramInfo != undefined ? viewer.ramInfo.x / 4 : 0
|
|
}
|
|
|
|
function updateReconstructionView() {
|
|
if (isOutputSequence)
|
|
return
|
|
if (_reconstruction && m.frame >= frameRange.min && m.frame < frameRange.max + 1) {
|
|
if (!m.playing && !frameSlider.pressed) {
|
|
_reconstruction.selectedViewId = sortedViewIds[m.frame]
|
|
} else {
|
|
_reconstruction.pickedViewId = sortedViewIds[m.frame]
|
|
if (m.sync3DSelected) {
|
|
_reconstruction.updateSelectedViewpoint(_reconstruction.pickedViewId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onIsOutputSequenceChanged: {
|
|
if (!isOutputSequence) {
|
|
frameId = frameRange.min
|
|
}
|
|
}
|
|
|
|
onSortedViewIdsChanged: {
|
|
frameSlider.from = frameRange.min
|
|
frameSlider.to = frameRange.max
|
|
}
|
|
|
|
// Sequence player model:
|
|
// - current frame
|
|
// - data related to automatic sequence playing
|
|
QtObject {
|
|
id: m
|
|
|
|
property int frame: frameRange.min
|
|
property bool syncFeaturesSelected: true
|
|
property bool sync3DSelected: true
|
|
property bool playing: false
|
|
property bool repeat: false
|
|
property real fps: 24
|
|
|
|
onFrameChanged: {
|
|
updateReconstructionView()
|
|
}
|
|
|
|
onPlayingChanged: {
|
|
if (!playing) {
|
|
updateReconstructionView()
|
|
} else if (playing && (frame + 1 >= frameRange.max + 1)) {
|
|
frame = frameRange.min
|
|
}
|
|
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 && root.visible
|
|
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 == frameRange.max + 1) {
|
|
if (m.repeat) {
|
|
m.frame = frameRange.min
|
|
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
|
|
|
|
IntSelector {
|
|
id: frameInput
|
|
|
|
tooltipText: "Frame"
|
|
|
|
value: m.frame
|
|
range: frameRange
|
|
|
|
onValueChanged: {
|
|
m.frame = value
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: playButton
|
|
|
|
checkable: true
|
|
checked: false
|
|
text: checked ? MaterialIcons.pause_circle : MaterialIcons.play_circle
|
|
font.pointSize: 20
|
|
ToolTip.text: checked ? "Pause Player" : "Play Sequence"
|
|
|
|
onCheckedChanged: {
|
|
m.playing = checked
|
|
}
|
|
|
|
Connections {
|
|
target: m
|
|
function onPlayingChanged() {
|
|
playButton.checked = m.playing
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Slider {
|
|
id: frameSlider
|
|
|
|
Layout.fillWidth: true
|
|
|
|
stepSize: 1
|
|
snapMode: Slider.SnapAlways
|
|
live: true
|
|
|
|
from: frameRange.min
|
|
to: frameRange.max
|
|
|
|
onValueChanged: {
|
|
m.frame = value
|
|
}
|
|
|
|
onPressedChanged: {
|
|
if (!pressed) {
|
|
updateReconstructionView()
|
|
}
|
|
}
|
|
|
|
ToolTip {
|
|
parent: frameSlider.handle
|
|
visible: frameSlider.hovered
|
|
text: m.frame
|
|
}
|
|
|
|
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 / (frameRange.max - frameRange.min + 1) : 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 {
|
|
TextInput {
|
|
id: fpsTextInput
|
|
|
|
Layout.preferredWidth: fpsMetrics.width
|
|
selectByMouse: true
|
|
|
|
text: !focus ? m.fps + " FPS" : m.fps
|
|
color: palette.text
|
|
|
|
onEditingFinished: {
|
|
m.fps = parseInt(text)
|
|
focus = false
|
|
}
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: fetchButton
|
|
|
|
text: MaterialIcons.subscriptions
|
|
ToolTip.text: "Fetch"
|
|
checkable: true
|
|
checked: loading
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: repeatButton
|
|
|
|
checkable: true
|
|
checked: false
|
|
text: MaterialIcons.repeat
|
|
ToolTip.text: "Repeat"
|
|
|
|
onCheckedChanged: {
|
|
m.repeat = checked
|
|
}
|
|
}
|
|
|
|
MaterialToolButton {
|
|
id: infoButton
|
|
|
|
text: MaterialIcons.settings
|
|
font.pointSize: 11
|
|
padding: 2
|
|
onClicked: infoMenu.open()
|
|
checkable: true
|
|
checked: infoMenu.visible
|
|
|
|
Popup {
|
|
id: infoMenu
|
|
y: parent.height
|
|
x: -width + parent.width
|
|
|
|
contentItem: GridLayout {
|
|
layoutDirection: Qt.LeftToRight
|
|
columns: 2
|
|
|
|
Column {
|
|
id: syncColumn
|
|
Layout.alignment: Qt.AlignTop
|
|
|
|
Text {
|
|
text: "<b>Synchronisation:</b>"
|
|
color: palette.text
|
|
}
|
|
|
|
CheckBox {
|
|
id: syncFeaturePointsCheckBox
|
|
text: "Sync Feature Points"
|
|
checkable: true
|
|
checked: m.syncFeaturesSelected
|
|
onCheckedChanged: {
|
|
m.syncFeaturesSelected = checked
|
|
}
|
|
|
|
ToolTip.text: "The Feature points will be updated at the same time as the Sequence Player."
|
|
ToolTip.visible: hovered
|
|
ToolTip.delay: 100
|
|
}
|
|
|
|
CheckBox {
|
|
id: sync3DCheckBox
|
|
text: "Sync 3D Viewer"
|
|
checkable: true
|
|
checked: m.sync3DSelected
|
|
onCheckedChanged: {
|
|
m.sync3DSelected = checked
|
|
}
|
|
|
|
ToolTip.text: "The 3D Viewer will be updated at the same time as the Sequence Player."
|
|
ToolTip.visible: hovered
|
|
ToolTip.delay: 100
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: cacheColumn
|
|
Layout.alignment: Qt.AlignTop
|
|
|
|
Text {
|
|
text: "<b>Cache:</b>"
|
|
color: palette.text
|
|
}
|
|
|
|
// max cache memory limit
|
|
Row {
|
|
height: sync3DCheckBox.height
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Max Cache Memory: "
|
|
color: palette.text
|
|
}
|
|
|
|
TextField {
|
|
id: maxCacheMemoryInput
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: palette.text
|
|
|
|
text: !focus ? settings_SequencePlayer.maxCacheMemory + " GB" : settings_SequencePlayer.maxCacheMemory
|
|
|
|
onEditingFinished: {
|
|
settings_SequencePlayer.maxCacheMemory = parseInt(text)
|
|
focus = false
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
height: sync3DCheckBox.height
|
|
verticalAlignment: Text.AlignVCenter
|
|
text: {
|
|
if (viewer && viewer.ramInfo != undefined)
|
|
return "Available Memory: " + viewer.ramInfo.x + " GB"
|
|
return "Unknown Available Memory"
|
|
}
|
|
color: palette.text
|
|
}
|
|
|
|
Text {
|
|
height: sync3DCheckBox.height
|
|
verticalAlignment: Text.AlignVCenter
|
|
// text: {
|
|
// // number of cached frames is the difference between the first and last frame of all intervals in the cache
|
|
// let cachedFrames = viewer ? viewer.cachedFrames : []
|
|
// let cachedFramesCount = 0
|
|
// for (let i = 0; i < cachedFrames.length; i++) {
|
|
// cachedFramesCount += cachedFrames[i].y - cachedFrames[i].x + 1
|
|
// }
|
|
// return "Cached Frames: " + (viewer ? cachedFramesCount : "0") + " / " + sortedViewIds.length
|
|
// }
|
|
color: palette.text
|
|
}
|
|
|
|
// do beautiful progress bar
|
|
ProgressBar {
|
|
id: cacheProgressBar
|
|
|
|
width: parent.width
|
|
|
|
from: 0
|
|
to: viewer && viewer.ramInfo != undefined ? viewer.ramInfo.x : 0
|
|
|
|
value: viewer ? settings_SequencePlayer.maxCacheMemory : 0
|
|
|
|
ToolTip.text: {
|
|
let ramMsg = "Max cache memory set: " + settings_SequencePlayer.maxCacheMemory + " GB"
|
|
if (viewer && viewer.ramInfo != undefined) {
|
|
return ramMsg + "\n" + "on available memory: " + viewer.ramInfo.x + " GB"
|
|
}
|
|
return ramMsg + ",\n" + "available memory unknown"
|
|
}
|
|
ToolTip.visible: hovered
|
|
ToolTip.delay: 100
|
|
}
|
|
|
|
ProgressBar {
|
|
id: occupiedCacheProgressBar
|
|
|
|
property string occupiedCache: viewer && viewer.ramInfo ? Format.GB2SizeStr(viewer.ramInfo.y) : 0
|
|
|
|
width: parent.width
|
|
|
|
from: 0
|
|
to: settings_SequencePlayer.maxCacheMemory
|
|
value: viewer && viewer.ramInfo != undefined ? viewer.ramInfo.y : 0
|
|
|
|
ToolTip.text: "Occupied cache: " + occupiedCache + "\n" + "On max cache memory set: " + settings_SequencePlayer.maxCacheMemory + " GB"
|
|
ToolTip.visible: hovered
|
|
ToolTip.delay: 100
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TextMetrics {
|
|
id: fpsMetrics
|
|
|
|
font: fpsTextInput.font
|
|
text: "100 FPS"
|
|
}
|
|
|
|
// Action to play/pause the sequence player
|
|
Action {
|
|
id: playPauseAction
|
|
|
|
shortcut: "Space"
|
|
|
|
onTriggered: {
|
|
m.playing = !m.playing
|
|
}
|
|
}
|
|
}
|