HMI and PyQt5 Slider Connection
-
I have created a urdf visualiser using three.js and then connecting it in my pyQT5 HMI so basically the urdf that is getting loaded is my HMI is being run on a local web server so how do i connect my controls (slider) to the urdf robot like if i make change in base so it should highlight the change in the web application also that is present in my pyQT5 HMI , so can anyone please help me how can we make a bridge between the two .
i will provide u my python and Js Code to get a refernce
Javascript code
import React, { Component } from "react";
import * as THREE from "three";
//import { MTLLoader, OBJLoader } from "three-obj-mtl-loader";
import OrbitControls from "three-orbitcontrols";import { LoadingManager, Box3 } from "three"; //new
import URDFLoader from "urdf-loader";let robot;
const manager = new LoadingManager();
const loader = new URDFLoader(manager);class RobotScene extends Component {
// constructor(props) {
// super(props)
// this.state = {robot: null}
// }componentDidMount() {
const width = this.mount.clientWidth; const height = this.mount.clientHeight; this.scene = new THREE.Scene(); //Add Renderer this.renderer = new THREE.WebGLRenderer({ antialias: true }); //this.renderer.setClearColor("#263238"); this.renderer.setSize(width, height); this.mount.appendChild(this.renderer.domElement); //add Camera this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); this.camera.position.z = 20; this.camera.position.y = 5; //add robot loader.load("/robot_active.urdf", (result) => { robot = result; // console.log("loader load function") // console.log(result) this.scene.add(result); robot.isLoaded = true; }); //Camera Controls const controls = new OrbitControls(this.camera, this.renderer.domElement); //LIGHTS var lights = []; lights[0] = new THREE.PointLight(0x304ffe, 1, 0); lights[1] = new THREE.PointLight(0xffffff, 1, 0); lights[2] = new THREE.PointLight(0xffffff, 1, 0); lights[0].position.set(0, 200, 0); lights[1].position.set(100, 200, 100); lights[2].position.set(-100, -200, -100); this.scene.add(lights[0]); this.scene.add(lights[1]); this.scene.add(lights[2]); //Simple Box with WireFrame this.addModels(); this.renderScene(); //start animation //this.start(); this.setState({robot: robot}); console.log(this.state.robot)
}
addModels() {
// -----Step 1--------
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshBasicMaterial({
color: "#0F0",
});
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);// -----Step 2-------- //LOAD TEXTURE and on completion apply it on SPHERE new THREE.TextureLoader().load( "https://images.pexels.com/photos/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", (texture) => { //Update Texture this.cube.material.map = texture; this.cube.material.needsUpdate = true; }, (xhr) => { //Download Progress console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, (error) => { //Error CallBack console.log("An error happened" + error); } );
}
componentWillUnmount() {
//this.stop();
this.mount.removeChild(this.renderer.domElement);
}renderScene = () => {
if (this.renderer) this.renderer.render(this.scene, this.camera);
};render() {
return (
<div
style={{ width: "800px", height: "800px" }}
ref={(mount) => {
this.mount = mount;
}}
/>
);
}
}// Define the updateJoint function
function updateJoint(jointNumber, value) {
//let robot = this.state.robot;
if (!robot || !robot.isLoaded) {
console.error('Robot arm or joints not initialized');
return;
}var joint = robot.joints[jointNumber - 1];
if (!joint) {
console.error('Joint ' + jointNumber + ' does not exist');
return;
}// Convert the normalized value (0-1) to a rotation angle
var rotationAngle = value * 2 * Math.PI;// Apply rotation to the joint
joint.rotation.x = rotationAngle;// Update the scene if necessary
}
// Attach the function to the window object for global accessibility
window.updateJoint = updateJoint;export default RobotScene;
Python code :
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QUrl
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QSliderclass Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600) # Initial size
MainWindow.setFixedSize(800, 600) # Set fixed size for the windowself.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") # Main layout mainLayout = QVBoxLayout(self.centralwidget) # WebEngine View self.webEngineView = QWebEngineView(self.centralwidget) self.webEngineView.load(QUrl("http://localhost:3000")) # URL of your Three.js app mainLayout.addWidget(self.webEngineView, 75) # Sliders Group slidersGroup = QGroupBox("Robot Control Sliders", self.centralwidget) slidersLayout = QVBoxLayout(slidersGroup) # Slider Labels sliderLabels = ["Base", "Shoulder", "Elbow", "Wrist1", "Wrist2", "Wrist3"] # Sliders with Labels self.sliders = [] for label in sliderLabels: sliderLayout = QHBoxLayout() labelWidget = QLabel(label, self.centralwidget) slider = QSlider(QtCore.Qt.Horizontal, self.centralwidget) slider.setObjectName(f"horizontalSlider_{label.lower()}") slider.setRange(0, 100) # Assuming a range of 0-100 for the slider slider.valueChanged.connect(lambda value, j=label: self.sliderValueChanged(j, value)) sliderLayout.addWidget(labelWidget) sliderLayout.addWidget(slider) self.sliders.append(slider) slidersLayout.addLayout(sliderLayout) mainLayout.addWidget(slidersGroup, 25) # Set central widget and layout MainWindow.setCentralWidget(self.centralwidget) self.centralwidget.setLayout(mainLayout) self.retranslateUi(MainWindow) # Connect to the loadFinished signal self.webEngineView.loadFinished.connect(self.onLoadFinished) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Robot Arm Control")) def onLoadFinished(self, ok): if ok: print("Page loaded successfully") else: print("Error loading page") def sliderValueChanged(self, label, value): # Normalize the slider value to a range between 0.0 and 1.0 normalized_value = value / 100.0 # Map labels to joint numbers (if necessary) joint_map = {"Base": 1, "Shoulder": 2, "Elbow": 3, "Wrist1": 4, "Wrist2": 5, "Wrist3": 6} joint_number = joint_map.get(label) # Execute the updateJoint JavaScript function js_code = f"updateJoint({joint_number}, {normalized_value});" self.webEngineView.page().runJavaScript(js_code)
if name == "main":
import sys
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_()) -
Hi,
You are mixing quite a lot of different technologies here.
Since you already have React in your web part, why not implement the rest of the UI there as well ?