Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ def process(self, forceCompute=False, inCurrentEnv=False):
executionStatus = None
self.statThread = stats.StatisticsThread(self)
self.statThread.start()

try:
self.node.nodeDesc.processChunk(self)
# NOTE: this assumes saving the output attributes for each chunk
Expand Down
15 changes: 14 additions & 1 deletion meshroom/ui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from PySide6 import __version__ as PySideVersion
from PySide6 import QtCore
from PySide6.QtCore import QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings
from PySide6.QtCore import QObject, QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings
from PySide6.QtGui import QIcon
from PySide6.QtQml import QQmlDebuggingEnabler
from PySide6.QtQuickControls2 import QQuickStyle
Expand All @@ -25,6 +25,7 @@
from meshroom.ui.components.scene3D import Scene3DHelper, Transformations3DHelper
from meshroom.ui.components.scriptEditor import ScriptEditorManager
from meshroom.ui.components.thumbnail import ThumbnailCache
from meshroom.ui.components.statusBar import MessageController
from meshroom.ui.palette import PaletteManager
from meshroom.ui.reconstruction import Reconstruction
from meshroom.ui.utils import QmlInstantEngine
Expand Down Expand Up @@ -281,6 +282,8 @@ def __init__(self, inputArgs):
self.engine.rootContext().setContextProperty("ThumbnailCache", ThumbnailCache(parent=self))

# additional context properties
self._messageController = MessageController(parent=self)
self.engine.rootContext().setContextProperty("_messageController", self._messageController)
self.engine.rootContext().setContextProperty("_PaletteManager", PaletteManager(self.engine, parent=self))
self.engine.rootContext().setContextProperty("ScriptEditorManager", ScriptEditorManager(parent=self))
self.engine.rootContext().setContextProperty("MeshroomApp", self)
Expand Down Expand Up @@ -353,6 +356,16 @@ def _pipelineTemplateNames(self):
def reloadTemplateList(self):
meshroom.core.initPipelines()
self.pipelineTemplateFilesChanged.emit()

@Slot()
def forceUIUpdate(self):
""" Force UI to process pending events
Necessary when we want to update the UI while a trigger is still running (e.g. reloadPlugins)
"""
self.processEvents()

def showMessage(self, message, status=None, duration=5000):
self._messageController.sendMessage(message, status, duration)

def _retrieveThumbnailPath(self, filepath: str) -> str:
"""
Expand Down
16 changes: 16 additions & 0 deletions meshroom/ui/components/statusBar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from PySide6.QtCore import QObject, Signal


class MessageController(QObject):
"""
Handles messages sent from the Python side to the StatusBar component
"""

message = Signal(str, str, int)

def __init__(self, parent):
super().__init__(parent)

def sendMessage(self, msg, status, duration):
""" Sends a message that will be displayed on the status bar """
self.message.emit(msg, status, duration)
16 changes: 14 additions & 2 deletions meshroom/ui/qml/Application.qml
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,8 @@ Page {
text: "Reload Plugins Source Code"
shortcut: "Ctrl+Shift+R"
onTriggered: {
_reconstruction.reloadPlugins()
statusBar.showMessage("Reloading plugins...")
_reconstruction.reloadPlugins() // This will handle the message to show that it finished properly
}
}

Expand Down Expand Up @@ -1023,9 +1024,10 @@ Page {
rightPadding: 4
palette.window: Qt.darker(activePalette.window, 1.15)

// Cache Folder
RowLayout {
anchors.fill: parent
spacing: 0

MaterialToolButton {
font.pointSize: 8
text: MaterialIcons.folder_open
Expand All @@ -1040,6 +1042,16 @@ Page {
color: Qt.darker(palette.text, 1.2)
background: Item {}
}

// Spacer to push status bar to the right
Item { Layout.fillWidth: true }

StatusBar {
id: statusBar
objectName: "statusBar" // Expose to python
height: parent.height
defaultIcon : MaterialIcons.comment
}
}
}

Expand Down
110 changes: 110 additions & 0 deletions meshroom/ui/qml/Controls/StatusBar.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2

import Utils 1.0

RowLayout {
id: root

property color defaultColor: Qt.darker(palette.text, 1.2)
property string defaultIcon : MaterialIcons.circle
property int interval : 5000
property bool logMessage : false

TextField {
id: statusBarField
Layout.fillHeight: true
readOnly: true
selectByMouse: true
text: statusBar.message
color: defaultColor
background: Item {}
visible: statusBar.message !== ""
}

// TODO : Idea for later : implement a ProgressBar here

MaterialToolButton {
id: statusBarButton
Layout.fillHeight: true
Layout.preferredWidth: 17
visible: true
font.pointSize: 8
text: defaultIcon
ToolTip.text: "Open Messages UI"
// TODO : Open messages UI
// onClicked: statusBar.showMessage("NotImplementedError : Cannot open interface", "error", 2000)
enabled: false // TODO: to remove when implemented
ToolTip.visible: false // TODO: to remove when implemented
Component.onCompleted: {
statusBarButton.contentItem.color = defaultColor
}
}

Timer {
id: statusBarTimer
interval: root.interval
running: false
repeat: false
onTriggered: {
// Erase message and reset button icon
statusBar.message = ""
statusBarField.color = defaultColor
statusBarButton.contentItem.color = defaultColor
statusBarButton.text = defaultIcon
}
}

QtObject {
id: statusBar
property string message: ""

function showMessage(msg, status=undefined, duration=root.interval) {
var textColor = defaultColor
var logLevel = "info"
switch (status) {
case "ok": {
statusBarField.color = Colors.green
statusBarButton.text = MaterialIcons.check_circle
break
}
case "warning": {
logLevel = "warn"
statusBarField.color = Colors.orange
statusBarButton.text = MaterialIcons.warning
break
}
case "error": {
logLevel = "error"
statusBarField.color = Colors.red
statusBarButton.text = MaterialIcons.error
break
}
default: {
statusBarButton.text = defaultIcon
}
}
if (logMessage === true) {
console.log("[Message][" + logLevel.toUpperCase().padEnd(5) + "] " + msg)
}
statusBarButton.contentItem.color = statusBarField.color
statusBar.message = msg
statusBarTimer.interval = duration
statusBarTimer.restart()
MeshroomApp.forceUIUpdate()
}
}

function showMessage(msg, status=undefined, duration=root.interval) {
statusBar.showMessage(msg, status, duration)
}

Connections {
target: _messageController
function onMessage(message, color, duration) {
showMessage(message, color, duration)
}
}
}
1 change: 1 addition & 0 deletions meshroom/ui/qml/Controls/qmldir
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ SelectionBox 1.0 SelectionBox.qml
SelectionLine 1.0 SelectionLine.qml
DelegateSelectionBox 1.0 DelegateSelectionBox.qml
DelegateSelectionLine 1.0 DelegateSelectionLine.qml
StatusBar 1.0 StatusBar.qml
15 changes: 14 additions & 1 deletion meshroom/ui/reconstruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ def __init__(self, undoStack: commands.UndoStack, taskManager: TaskManager, defa
# react to internal graph changes to update those variables
self.graphChanged.connect(self.onGraphChanged)

# Connect the pluginsReloaded signal to the onPluginsReloaded function
self.pluginsReloaded.connect(self._onPluginsReloaded)

self.setDefaultPipeline(defaultPipeline)

def __del__(self):
Expand Down Expand Up @@ -432,6 +435,10 @@ def onCameraInitChanged(self):

@Slot()
def reloadPlugins(self):
""" Launch _reloadPlugins in a worker thread to avoid blocking the ui. """
self._workerThreads.apply_async(func=self._reloadPlugins, args=())

def _reloadPlugins(self):
"""
Reload all the NodePlugins from all the registered plugins.
The nodes in the graph will be updated to match the changes in the description, if
Expand All @@ -442,8 +449,12 @@ def reloadPlugins(self):
for node in plugin.nodes.values():
if node.reload():
nodeTypes.append(node.nodeDescriptor.__name__)
self.pluginsReloaded.emit(nodeTypes)

@Slot(list)
def _onPluginsReloaded(self, nodeTypes: list):
self._graph.reloadNodePlugins(nodeTypes)
self.parent().showMessage("Plugins reloaded!", "ok")

@Slot()
@Slot(str)
Expand Down Expand Up @@ -930,7 +941,9 @@ def setBuildingIntrinsics(self, value):
displayedAttr2D = makeProperty(QObject, "_displayedAttr2D", displayedAttr2DChanged)

displayedAttrs3DChanged = Signal()
displayedAttrs3D = Property(QObject, lambda self: self._displayedAttrs3D, notify=displayedAttrs3DChanged)
displayedAttrs3D = Property(QObject, lambda self: self._displayedAttrs3D, notify=displayedAttrs3DChanged)

pluginsReloaded = Signal(list)

@Slot(QObject)
def setActiveNode(self, node, categories=True, inputs=True):
Expand Down
Loading