Hi everyone,
I am trying to set up an IFCjs viewer inside of Streamlit.
I tried this on a simple example of an array of URLs, each one pointing to a specific IFC model saved on the web.
Using st.selectbox the user can pick a specific model, on selection the URL is passed to JavaScript which initialises the IFCjs viewer and opens the 3D model.
Here is the Homepage.py:
import streamlit as st
from ifc_viewer.ifc_viewer import ifc_viewer
# List of URLs
url_list = ["https://cropka.com/TEST.ifc", "http://example2.com", "http://example3.com"]
# Dropdown selector for URLs
selected_url = st.selectbox("Select URL", options=url_list)
# Call the IFC viewer with the selected URL
ifc_viewer(selected_url)
In the “ifc_viewer” folder the following code handles passing of the URL to Java:
import streamlit as st
from streamlit.components.v1 import declare_component
# Declare the component
_ifc_viewer = declare_component("ifc_viewer", path="ifc_viewer")
def ifc_viewer(url: str):
# Call the component and pass the url
_ifc_viewer(url=url)
However in my JavaScript I am clueless why this is not being received:
import { Color } from "three";
import { IfcViewerAPI } from "web-ifc-viewer";
console.log("Streamlit object: ", window.Streamlit);
const Streamlit = window.Streamlit;
const container = document.getElementById('viewer-container')
const viewer = new IfcViewerAPI({container, backgroundColor: new Color(0xffffff)});
viewer.axes.setAxes();
viewer.grid.setGrid();
async function loadIfc(url) {
// Load the model
const model = await viewer.IFC.loadIfcUrl(url);
// Now that the model is loaded, tell Streamlit that the component is ready
Streamlit.setComponentReady();
}
Streamlit.onRender((data) => {
const url = data.args.url;
loadIfc(url);
});
Clearly the problem I get in the console is that window.Streamlit is undefined. How can I receive the URL on JavaScript’s end? Any ideas much appreciated!
After applying and tweaking the snippet (Code snippet: create Components without any frontend tooling (no React, Babel, Webpack, etc)) I came up with this code:
import { Color } from "three";
import { IfcViewerAPI } from "web-ifc-viewer";
function sendMessageToStreamlitClient(type, data) {
const message = { ...data, isStreamlitMessage: true, type };
window.parent.postMessage(message, "*");
}
function init() {
sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1});
}
function setFrameHeight(height) {
sendMessageToStreamlitClient("streamlit:setFrameHeight", {height});
}
const viewer = new IfcViewerAPI({container: document.getElementById('viewer-container'), backgroundColor: new Color(0xffffff)});
viewer.axes.setAxes();
viewer.grid.setGrid();
async function loadIfc(url) {
const model = await viewer.IFC.loadIfcUrl(url);
await viewer.shadowDropper.renderShadow(model.modelID);
viewer.context.renderer.postProduction.active = true;
}
function onDataFromPython(event) {
if (event.data.type !== "streamlit:render") return;
const url = event.data.args.url;
if(url){
loadIfc(url);
}
}
// Hook things up!
window.addEventListener("message", onDataFromPython);
init();
// Hack to autoset the iframe height.
window.addEventListener("load", function() {
window.setTimeout(function() {
setFrameHeight(document.documentElement.clientHeight)
}, 0);
});
According to console my model is being opened (parsing usinf IFC4 Schema) but no IFCjs viewer is being displayed inside streamlit. I have tested the viewer running it independently in Live Server and it is fine. It appears that the last function:
window.addEventListener("load", function() {
window.setTimeout(function() {
setFrameHeight(document.documentElement.clientHeight)
}, 0);
});
Is not loading correctly. I can get the viewer to appear only if I provide hard coded height in setFrameHeight, e.g. setFrameHeight(300), but I cannot figure out how to autoset iframe height.
Hi Felipe,
thanks for the link. IFC-101 is an amazing tutorial, I know this by heart now 
This works perfectly for a localhost application but not for StreamlitCloud deployment. Also the use of an ArrayBuffer means you have to use web-ifc-three instead of web-ifc-viewer, which is adding a lot of unnecessary work. With the URL solution you have the full power of IFCjs at your finger tips.
In the meantime I have just set the frame height manually and all is working great, the URL gets passed as intended. Also a little hint - the IFCs I am using are now located on Google Cloud Storage, which can generate URLs, this makes it much easier to pass them between streamlit and the IFCjs viewer.
1 Like