QML animation blocked when integrating QQuickWidget(QML) and qwidget(VTK) in Python
Unsolved
QML and Qt Quick
-
I used QVTKRenderWindowInteractor to integrate VTK in QApplication.
I also use QQuickWidget to load a QML file, which contains few QtQuick Modules, in to the QApplication.
The application can run, and I can get the data from the slider in QML when slider moved.However, the animation of all modules inside the QML is blocked at first.
I can get the animation back by click the QQuickWidget first, let it gets focus, and then resize the application window or click the screen area outside the application window.
The animation will block again once I click the VTK area.I had a hard time identifing the problem, can any body help me?
This is the Python file
import sys from PyQt5 import QtWidgets, QtGui from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor from vtkmodules.vtkFiltersSources import vtkSphereSource from vtkmodules.vtkRenderingCore import ( vtkActor, vtkPolyDataMapper, vtkRenderer ) from PyQt5.QtQuickWidgets import QQuickWidget from PyQt5.QtCore import QUrl, QObject, pyqtSlot def getVTKWidget(parentFrame): # Create source source = vtkSphereSource() source.SetCenter(0, 0, 0) source.SetRadius(5.0) # Create a mapper mapper = vtkPolyDataMapper() mapper.SetInputConnection(source.GetOutputPort()) # Create an actor actor = vtkActor() actor.SetMapper(mapper) ren = vtkRenderer() ren.AddActor(actor) ren.ResetCamera() vtkWidget = QVTKRenderWindowInteractor(parentFrame) vtkWidget.GetRenderWindow().AddRenderer(ren) vtkWidget.GetRenderWindow().GetInteractor().Initialize() vtkWidget.GetRenderWindow().Render() return vtkWidget, ren class my_QQWidget(QQuickWidget): def __init__(self): super().__init__() self.setSource(QUrl.fromLocalFile('./Demo/rect.qml')) def focusOutEvent(self, event: QtGui.QFocusEvent) -> None: super().focusOutEvent(event) print('out') def focusInEvent(self, event: QtGui.QFocusEvent) -> None: super().focusInEvent(event) print('in') # print(self.rootObject().findChild(QObject, 'axisComboBox').isFocusScope()) # print(self.rootObject().findChild(QObject, 'textDegree').isFocusScope()) # print(self.rootObject().findChild(QObject, 'rectangle').isFocusScope()) # print(self.rootObject().findChild(QObject, 'angleText').isFocusScope()) # print(self.rootObject().findChild(QObject, 'resetBtn').isFocusScope()) # print(self.rootObject().findChild(QObject, 'sliderContainer').isFocusScope()) # print(self.rootObject().findChild(QObject, 'groove').isFocusScope()) # print(self.rootObject().findChild(QObject, 'handle').isFocusScope()) # print(self.rootObject().findChild(QObject, 'slider').isFocusScope()) self.update() self.rootObject().update() self.rootObject().updatePolish() self.updateGeometry() # print(self.focusWidget()) self.show() # print(self.rootObject().hasFocus()) self.rootObject().polish() def getQQWidget(): # qqWidget = QQuickWidget() # qqWidget.setSource(QUrl.fromLocalFile('./Demo/rect.qml')) qqWidget = my_QQWidget() return qqWidget if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) # main application window window = QtWidgets.QMainWindow() # QFrame that holds vtk vtkframe = QtWidgets.QFrame() vl = QtWidgets.QVBoxLayout() vtkframe.setLayout(vl) # add vtkWidget vtkWidget, ren = getVTKWidget(vtkframe) vl.addWidget(vtkWidget) # QFrame that holds QQuickWidget convert from QML file qmlFrame = QtWidgets.QFrame() ql = QtWidgets.QVBoxLayout() qmlFrame.setLayout(ql) # add QQuickWidget (QML file) qmlWidget = getQQWidget() ql.addWidget(qmlWidget) # a slider in QQuickWidget slider = qmlWidget.rootObject().findChild(QObject, 'slider') # slider moved event @pyqtSlot() def receive_angle(): angle = slider.property("value") print(f"{angle} Degree") can = ren.GetActiveCamera() can.Azimuth(5) vtkWidget.GetRenderWindow().Render() slider.moved.connect(receive_angle) # place vtk frame and qml frame in QVBoxLayout mainFrame = QtWidgets.QFrame() ml = QtWidgets.QVBoxLayout() mainFrame.setLayout(ml) ml.addWidget(vtkframe) ml.addWidget(qmlFrame) window.setCentralWidget(mainFrame) window.show() sys.exit(app.exec_())
And this is the UI, qml file
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Timeline 1.0 Item { id: applicationwindow objectName: "applicationwindow" width: 1024 height: 150 ComboBox { id: comboBox objectName: "axisComboBox" x: 122 y: 68 font.pointSize: 20 model: ["X Axis", 'Y Axis', 'Z Axis'] currentIndex: 1 } Text { id: text1 objectName: "textDegree" x: 827 y: 67 width: 79 height: 40 text: qsTr("Degree") font.pixelSize: 25 verticalAlignment: Text.AlignVCenter } Rectangle { id: rectangle objectName: "rectangle" x: 760 y: 67 width: 55 height: 40 color: "lightgrey" border.color: "grey" TextInput { id: textEdit objectName: "angleText" validator: IntValidator { bottom: 0 top: 360 } maximumLength: 3 text: qsTr("0") anchors.fill: parent font.pixelSize: 26 horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter } } Button { id: button objectName: "resetBtn" x: 462 y: 19 text: qsTr("Reset") font.pixelSize: 26 } Item { id: sliderContainer objectName: "sliderContainer" x: 275 y: 73 width: 474 height: 31 Image { id: sliderContainerAsset source: "assets/sliderContainer.png" } Image { id: groove objectName: "groove" y: 0 anchors.left: parent.left anchors.right: parent.right source: "assets/groove.png" anchors.leftMargin: 0 anchors.rightMargin: 0 } Image { id: handle objectName: "handle" x: 444 y: 1 anchors.verticalCenter: parent.verticalCenter anchors.bottom: parent.bottom source: "assets/handle.png" anchors.verticalCenterOffset: 0 anchors.bottomMargin: 0 } Slider { id: slider objectName: "slider" anchors.fill: parent value: 0 stepSize: 1 to: 180 } } Timeline { id: timeline currentFrame: slider.value KeyframeGroup { target: handle property: "x" Keyframe { value: 444 frame: 180 } Keyframe { value: 0 frame: 0 } } endFrame: 180 enabled: true startFrame: 0 } } /*##^## Designer { D{i:0;formeditorZoom:0.5}D{i:1}D{i:2}D{i:4}D{i:3}D{i:6}D{i:8}D{i:9}D{i:10}D{i:11} D{i:7}D{i:12} } ##^##*/