mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
Merge pull request #5103 from IfcOpenShell/webui_drawings
Web UI drawings page
This commit is contained in:
@@ -2,7 +2,9 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
blenderbim_lib_path = os.environ.get("blenderbim_lib_path")
|
blenderbim_lib_path = os.environ.get("BLENDERBIM_LIB_PATH")
|
||||||
|
blenderbim_version = os.environ.get("BLENDERBIM_VERSION")
|
||||||
|
|
||||||
if blenderbim_lib_path:
|
if blenderbim_lib_path:
|
||||||
sys.path.insert(0, blenderbim_lib_path)
|
sys.path.insert(0, blenderbim_lib_path)
|
||||||
|
|
||||||
@@ -52,9 +54,16 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
"web_operator",
|
"web_operator",
|
||||||
data,
|
data,
|
||||||
namespace="/blender",
|
namespace="/blender",
|
||||||
room=data["blenderId"],
|
room=data.get("blenderId", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def on_get_svg(self, sid, data):
|
||||||
|
print("hello world!")
|
||||||
|
file_path = data["path"]
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
svg_data = file.read()
|
||||||
|
await sio.emit("svg_data", svg_data, room=sid, namespace="/web")
|
||||||
|
|
||||||
async def send_cached_messages(self, sid):
|
async def send_cached_messages(self, sid):
|
||||||
# Send cached messages to the connected web client
|
# Send cached messages to the connected web client
|
||||||
for blenderId, messages in blender_messages.items():
|
for blenderId, messages in blender_messages.items():
|
||||||
@@ -98,6 +107,12 @@ class BlenderNamespace(socketio.AsyncNamespace):
|
|||||||
blender_messages[sid]["gantt_data"] = data
|
blender_messages[sid]["gantt_data"] = data
|
||||||
await sio.emit("gantt_data", {"blenderId": sid, "data": data}, namespace="/web")
|
await sio.emit("gantt_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||||
|
|
||||||
|
async def on_drawings_data(self, sid, data):
|
||||||
|
print(f"Drawings directory from Blender client {sid}")
|
||||||
|
print(data)
|
||||||
|
blender_messages[sid]["drwings_data"] = data
|
||||||
|
await sio.emit("drawings_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||||
|
|
||||||
|
|
||||||
# Attach namespaces
|
# Attach namespaces
|
||||||
sio.register_namespace(WebNamespace("/web"))
|
sio.register_namespace(WebNamespace("/web"))
|
||||||
@@ -108,13 +123,20 @@ sio.register_namespace(BlenderNamespace("/blender"))
|
|||||||
async def index(request):
|
async def index(request):
|
||||||
with open("templates/index.html", "r") as f:
|
with open("templates/index.html", "r") as f:
|
||||||
template = f.read()
|
template = f.read()
|
||||||
html_content = pystache.render(template, {"port": sio_port})
|
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||||
return web.Response(text=html_content, content_type="text/html")
|
return web.Response(text=html_content, content_type="text/html")
|
||||||
|
|
||||||
|
|
||||||
async def gantt(request):
|
async def gantt(request):
|
||||||
with open("templates/gantt.html", "r") as f:
|
with open("templates/gantt.html", "r") as f:
|
||||||
template = f.read()
|
template = f.read()
|
||||||
|
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||||
|
return web.Response(text=html_content, content_type="text/html")
|
||||||
|
|
||||||
|
|
||||||
|
async def drawings(request):
|
||||||
|
with open("templates/drawings.html", "r") as f:
|
||||||
|
template = f.read()
|
||||||
html_content = pystache.render(template, {"port": sio_port})
|
html_content = pystache.render(template, {"port": sio_port})
|
||||||
return web.Response(text=html_content, content_type="text/html")
|
return web.Response(text=html_content, content_type="text/html")
|
||||||
|
|
||||||
@@ -135,6 +157,7 @@ async def on_startup(app):
|
|||||||
|
|
||||||
|
|
||||||
app.router.add_get("/", index)
|
app.router.add_get("/", index)
|
||||||
|
app.router.add_get("/drawings", drawings)
|
||||||
app.router.add_get("/gantt", gantt)
|
app.router.add_get("/gantt", gantt)
|
||||||
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
||||||
app.router.add_static("/static/", path="./static", name="static")
|
app.router.add_static("/static/", path="./static", name="static")
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
:root {
|
||||||
|
--font-family: Arial, sans-serif;
|
||||||
|
--base-font-size: 16px;
|
||||||
|
--margin-tiny: 0.125rem;
|
||||||
|
--margin-small: 0.625rem;
|
||||||
|
--margin-medium: 1.25rem;
|
||||||
|
--margin-large: 2.5rem;
|
||||||
|
--padding-tiny: 0.125rem;
|
||||||
|
--padding-small: 0.625rem;
|
||||||
|
--padding-medium: 1rem;
|
||||||
|
--font-size-large: 1.2rem;
|
||||||
|
--logo-height: 2.5rem;
|
||||||
|
--nav-height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg-color: #252525;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--nav-bg-color: #121212;
|
||||||
|
--nav-border-color: #25682a;
|
||||||
|
--nav-link-color: #fff;
|
||||||
|
--nav-link-hover-color: #3fb449;
|
||||||
|
--warning-color: #FFDB8F;
|
||||||
|
--border-color: #464444;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.light {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg-color: #ffffff;
|
||||||
|
--text-color: #000000;
|
||||||
|
--nav-bg-color: #f8f8f8;
|
||||||
|
--nav-border-color: #cccccc;
|
||||||
|
--nav-link-color: #000000;
|
||||||
|
--nav-link-hover-color: #38a63d;
|
||||||
|
--warning-color: #FF4500;
|
||||||
|
--border-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
margin-top: var(--margin-medium);
|
||||||
|
margin-left: var(--margin-small);
|
||||||
|
margin-right: var(--margin-small);
|
||||||
|
margin-bottom: var(--margin-medium);
|
||||||
|
height: calc(100vh - var(--nav-height));
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
height: var(--nav-height);
|
||||||
|
padding: var(--padding-medium) 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 2px solid var(--nav-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .logo {
|
||||||
|
margin-left: var(--margin-large);
|
||||||
|
height: var(--logo-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 0.0625rem;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
margin-right: 3.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--nav-link-color);
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a:hover,
|
||||||
|
nav ul li a.active {
|
||||||
|
color: var(--nav-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: var(--warning-color);
|
||||||
|
margin-bottom: var(--margin-small);
|
||||||
|
padding: var(--padding-tiny);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin-right: var(--margin-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(0 + var(--nav-height));
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||||
|
border: 1px solid var(--table-border-color);
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connected-list-div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client {
|
||||||
|
padding: var(--margin-small);
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: var(--padding-small);
|
||||||
|
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
margin-top: var(--margin-tiny);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details.show {
|
||||||
|
max-height: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-detail {
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
padding: var(--padding-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button {
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: none;
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button:hover,
|
||||||
|
.scroll-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
margin-left: var(--margin-tiny);
|
||||||
|
margin-top: var(--margin-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.left {
|
||||||
|
flex: 1;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
flex: 4;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#svg-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header,
|
||||||
|
.card-body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.svg-name {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dropdown-menu:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove bootstrap css confilcts */
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before {
|
||||||
|
box-sizing: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn,
|
||||||
|
.btn:hover {
|
||||||
|
color: var(--nav-link-hover-color);
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
:root {
|
:root {
|
||||||
--font-family: Arial, sans-serif;
|
--font-family: Arial, sans-serif;
|
||||||
|
--base-font-size: 16px;
|
||||||
|
--margin-tiny: 0.125rem;
|
||||||
--margin-small: 0.625rem;
|
--margin-small: 0.625rem;
|
||||||
--margin-medium: 1.25rem;
|
--margin-medium: 1.25rem;
|
||||||
--margin-large: 2.5rem;
|
--margin-large: 2.5rem;
|
||||||
--padding-small: 0.125rem;
|
--padding-tiny: 0.125rem;
|
||||||
|
--padding-small: 0.625rem;
|
||||||
--padding-medium: 1rem;
|
--padding-medium: 1rem;
|
||||||
--font-size-small: 1rem;
|
--font-size-small: 1rem;
|
||||||
--font-size-large: 1.2rem;
|
--font-size-large: 1.2rem;
|
||||||
--logo-height: 2.5rem;
|
--logo-height: 2.5rem;
|
||||||
|
--nav-height: 2.5rem;
|
||||||
--folder-collapse-font-size: 0.75rem;
|
--folder-collapse-font-size: 0.75rem;
|
||||||
--folder-collapse-font-family: Courier, "Courier New", monospace;
|
--folder-collapse-font-family: Courier, "Courier New", monospace;
|
||||||
--box-shadow: 0 0 0.7rem #5f5f5f66;
|
--box-shadow: 0 0 0.7rem #5f5f5f66;
|
||||||
@@ -31,6 +35,7 @@
|
|||||||
--group-item-bg-color: #4b4b4b;
|
--group-item-bg-color: #4b4b4b;
|
||||||
--input-bg-color: #3b3b3b;
|
--input-bg-color: #3b3b3b;
|
||||||
--input-border-color: #000;
|
--input-border-color: #000;
|
||||||
|
--details-border-color: #464444;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.light {
|
:root.light {
|
||||||
@@ -42,6 +47,7 @@
|
|||||||
--nav-link-color: #000000;
|
--nav-link-color: #000000;
|
||||||
--nav-link-hover-color: #38a63d;
|
--nav-link-hover-color: #38a63d;
|
||||||
--warning-color: #FF4500;
|
--warning-color: #FF4500;
|
||||||
|
--details-border-color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -53,9 +59,13 @@ body {
|
|||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
|
flex: 1;
|
||||||
margin-top: var(--margin-medium);
|
margin-top: var(--margin-medium);
|
||||||
margin-left: var(--margin-small);
|
margin-left: var(--margin-small);
|
||||||
margin-right: var(--margin-small);
|
margin-right: var(--margin-small);
|
||||||
@@ -107,7 +117,7 @@ nav ul li a.active {
|
|||||||
.warning {
|
.warning {
|
||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
margin-bottom: var(--margin-small);
|
margin-bottom: var(--margin-small);
|
||||||
padding: var(--padding-small);
|
padding: var(--padding-tiny);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +151,87 @@ nav ul li a.active {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#client-list {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(0 + var(--nav-height));
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||||
|
border: 1px solid var(--table-border-color);
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connected-list-div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client {
|
||||||
|
padding: var(--margin-small);
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: var(--padding-small);
|
||||||
|
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
margin-top: var(--margin-tiny);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details.show {
|
||||||
|
max-height: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-detail {
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
padding: var(--padding-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button {
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: none;
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button:hover,
|
||||||
|
.scroll-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
margin-left: var(--margin-tiny);
|
||||||
|
margin-top: var(--margin-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
text-align: right;
|
||||||
|
padding: var(--padding-tiny);
|
||||||
|
border-top: 1px solid var(--nav-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-right: var(--margin-small);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.gantt-info {
|
.gantt-info {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
:root {
|
:root {
|
||||||
--font-family: Arial, sans-serif;
|
--font-family: Arial, sans-serif;
|
||||||
|
--base-font-size: 16px;
|
||||||
|
--margin-tiny: 0.125rem;
|
||||||
--margin-small: 0.625rem;
|
--margin-small: 0.625rem;
|
||||||
--margin-medium: 1.25rem;
|
--margin-medium: 1.25rem;
|
||||||
--margin-large: 2.5rem;
|
--margin-large: 2.5rem;
|
||||||
--padding-small: 0.125rem;
|
--padding-tiny: 0.125rem;
|
||||||
|
--padding-small: 0.625rem;
|
||||||
--padding-medium: 1rem;
|
--padding-medium: 1rem;
|
||||||
--font-size-large: 1.2rem;
|
--font-size-large: 1.2rem;
|
||||||
--logo-height: 2.5rem;
|
--logo-height: 2.5rem;
|
||||||
|
--nav-height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.dark {
|
:root.dark {
|
||||||
@@ -18,6 +22,7 @@
|
|||||||
--nav-link-color: #fff;
|
--nav-link-color: #fff;
|
||||||
--nav-link-hover-color: #3fb449;
|
--nav-link-hover-color: #3fb449;
|
||||||
--warning-color: #FFDB8F;
|
--warning-color: #FFDB8F;
|
||||||
|
--table-border-color: #464444;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root.light {
|
:root.light {
|
||||||
@@ -29,6 +34,7 @@
|
|||||||
--nav-link-color: #000000;
|
--nav-link-color: #000000;
|
||||||
--nav-link-hover-color: #38a63d;
|
--nav-link-hover-color: #38a63d;
|
||||||
--warning-color: #FF4500;
|
--warning-color: #FF4500;
|
||||||
|
--table-border-color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -40,29 +46,26 @@ body {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
|
flex: 1;
|
||||||
margin-top: var(--margin-medium);
|
margin-top: var(--margin-medium);
|
||||||
margin-left: var(--margin-small);
|
margin-left: var(--margin-small);
|
||||||
margin-right: var(--margin-small);
|
margin-right: var(--margin-small);
|
||||||
margin-bottom: var(--margin-medium);
|
margin-bottom: var(--margin-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
|
||||||
margin-bottom: var(--margin-large);
|
|
||||||
}
|
|
||||||
|
|
||||||
.csv-table {
|
|
||||||
margin-top: var(--margin-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background-color: var(--nav-bg-color);
|
background-color: var(--nav-bg-color);
|
||||||
|
height: var(--nav-height);
|
||||||
padding: var(--padding-medium) 0;
|
padding: var(--padding-medium) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -102,7 +105,7 @@ nav ul li a.active {
|
|||||||
.warning {
|
.warning {
|
||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
margin-bottom: var(--margin-small);
|
margin-bottom: var(--margin-small);
|
||||||
padding: var(--padding-small);
|
padding: var(--padding-tiny);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +121,95 @@ nav ul li a.active {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#client-list {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(0 + var(--nav-height));
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||||
|
border: 1px solid var(--table-border-color);
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#client-list.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connected-list-div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client {
|
||||||
|
padding: var(--margin-small);
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: var(--padding-small);
|
||||||
|
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
margin-top: var(--margin-tiny);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details.show {
|
||||||
|
max-height: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-detail {
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
padding: var(--padding-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button {
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: none;
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#show-connected-button:hover,
|
||||||
|
.scroll-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
margin-left: var(--margin-tiny);
|
||||||
|
margin-top: var(--margin-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: var(--nav-bg-color);
|
||||||
|
text-align: right;
|
||||||
|
padding: var(--padding-tiny);
|
||||||
|
border-top: 1px solid var(--nav-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-right: var(--margin-small);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
margin-bottom: var(--margin-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.csv-table {
|
||||||
|
margin-top: var(--margin-small);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||||
|
|
||||||
:root.light .tabulator-header,
|
:root.light .tabulator-header,
|
||||||
@@ -127,6 +219,6 @@ nav ul li a.active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root.light .tabulator {
|
:root.light .tabulator {
|
||||||
border-top: 1px solid #222;
|
border-top: 1px solid var(--table-border-color);
|
||||||
border-bottom: 2px solid #222;
|
border-bottom: 2px solid var(--table-border-color);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
// keeps track of blenders connected
|
||||||
|
const connectedClients = {};
|
||||||
|
const allDrawings = {};
|
||||||
|
let socket;
|
||||||
|
|
||||||
|
// Document ready function
|
||||||
|
$(document).ready(function () {
|
||||||
|
var systemTheme = window.matchMedia("(prefers-color-scheme: light)").matches
|
||||||
|
? "light"
|
||||||
|
: "dark";
|
||||||
|
var theme = localStorage.getItem("theme") || systemTheme;
|
||||||
|
setTheme(theme);
|
||||||
|
|
||||||
|
$("#dropdown-menu").change(function () {
|
||||||
|
const blenderId = $(this).attr("id");
|
||||||
|
const ifcFile = $(this).val();
|
||||||
|
displayDrawingsNames(blenderId, ifcFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
connectSocket();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to connect to Socket.IO server
|
||||||
|
function connectSocket() {
|
||||||
|
const url = "ws://localhost:" + SOCKET_PORT + "/web";
|
||||||
|
socket = io(url);
|
||||||
|
console.log("socket: ", socket);
|
||||||
|
|
||||||
|
// Register socket event handlers
|
||||||
|
socket.on("blender_connect", handleBlenderConnect);
|
||||||
|
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||||
|
socket.on("connect", handleWebConnect);
|
||||||
|
socket.on("drawings_data", handleDrawingsData);
|
||||||
|
socket.on("svg_data", handleSvgData);
|
||||||
|
// socket.on("default_data", handleDefaultData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function used to get drawings data from blenderbim
|
||||||
|
function handleWebConnect() {
|
||||||
|
const msg = {
|
||||||
|
sourcePage: "drawings",
|
||||||
|
operator: {
|
||||||
|
type: "getDrawings",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
socket.emit("web_operator", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle 'blender_connect' event
|
||||||
|
function handleBlenderConnect(blenderId) {
|
||||||
|
console.log("blender_connect: ", blenderId);
|
||||||
|
if (!connectedClients.hasOwnProperty(blenderId)) {
|
||||||
|
connectedClients[blenderId] = {
|
||||||
|
shown: false,
|
||||||
|
ifc_file: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle 'blender_disconnect' event
|
||||||
|
function handleBlenderDisconnect(blenderId) {
|
||||||
|
console.log("blender_disconnect: ", blenderId);
|
||||||
|
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||||
|
delete connectedClients[blenderId];
|
||||||
|
// remove(blenderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#blender-count").text(function (i, text) {
|
||||||
|
return parseInt(text, 10) - 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDrawingsData(data) {
|
||||||
|
const blenderId = data["blenderId"];
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
const filename = data["data"]["ifc_file"];
|
||||||
|
const drawings = data["data"]["drawings_data"]["drawings"];
|
||||||
|
const sheets = data["data"]["drawings_data"]["sheets"];
|
||||||
|
|
||||||
|
allDrawings[filename] = { drawings: drawings, sheets: sheets };
|
||||||
|
|
||||||
|
console.log(connectedClients);
|
||||||
|
|
||||||
|
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||||
|
if (!connectedClients[blenderId].shown) {
|
||||||
|
connectedClients[blenderId] = {
|
||||||
|
shown: true,
|
||||||
|
ifc_file: filename,
|
||||||
|
};
|
||||||
|
addSelectOption(blenderId, filename);
|
||||||
|
} else {
|
||||||
|
// update(blenderId, data, filename);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connectedClients[blenderId] = {
|
||||||
|
shown: true,
|
||||||
|
ifc_file: filename,
|
||||||
|
};
|
||||||
|
addSelectOption(blenderId, filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSvgData(data) {
|
||||||
|
const svgContainer = document.getElementById("svg-container");
|
||||||
|
svgContainer.innerHTML = "";
|
||||||
|
svgContainer.style.backgroundColor = "white";
|
||||||
|
|
||||||
|
const dom = SVG(svgContainer).svg(data);
|
||||||
|
const svgElement = dom.node.children[0];
|
||||||
|
|
||||||
|
// to make the svg not grow outside the svg container
|
||||||
|
svgElement.setAttribute("width", "100%");
|
||||||
|
svgElement.setAttribute("height", "100%");
|
||||||
|
svgElement.style.maxWidth = "100%";
|
||||||
|
svgElement.style.maxHeight = "100%";
|
||||||
|
svgElement.style.width = "100%";
|
||||||
|
svgElement.style.height = "100%";
|
||||||
|
svgElement.style.boxSizing = "border-box"; // Ensure padding and borders are included in the element's total width and height
|
||||||
|
|
||||||
|
const panZoomControls = svgPanZoom(svgElement, {
|
||||||
|
zoomEnabled: true,
|
||||||
|
controlIconsEnabled: true,
|
||||||
|
fit: true,
|
||||||
|
center: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSelectOption(blenderId, filename) {
|
||||||
|
const filenameOption = $("<option></option>")
|
||||||
|
.attr("id", "blender-" + blenderId)
|
||||||
|
.val(filename)
|
||||||
|
.text(filename);
|
||||||
|
$("#dropdown-menu").append(filenameOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayDrawingsNames(blenderId, ifcFile) {
|
||||||
|
drawings = allDrawings[ifcFile].drawings;
|
||||||
|
sheets = allDrawings[ifcFile].sheets;
|
||||||
|
|
||||||
|
function createSvgNames(text, index, type) {
|
||||||
|
var label = $("<li></lio>")
|
||||||
|
.text(text)
|
||||||
|
.attr("id", type + "-" + index)
|
||||||
|
.addClass("svg-name")
|
||||||
|
.click(function () {
|
||||||
|
const id = $(this).attr("id").split("-");
|
||||||
|
const index = parseInt(id[1]);
|
||||||
|
const type = id[0];
|
||||||
|
const ifcFile = $("#dropdown-menu").val();
|
||||||
|
|
||||||
|
var path = "";
|
||||||
|
|
||||||
|
if (type === "drawing") {
|
||||||
|
path = allDrawings[ifcFile].drawings[index].path;
|
||||||
|
} else if (type === "sheet") {
|
||||||
|
path = allDrawings[ifcFile].sheets[index].path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
path: path,
|
||||||
|
};
|
||||||
|
console.log(msg);
|
||||||
|
socket.emit("get_svg", msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type === "drawing") $("#drawings-sub-panel").append(label);
|
||||||
|
else if (type === "sheet") $("#sheets-sub-panel").append(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#drawings-sub-panel").empty();
|
||||||
|
drawings.forEach(function (drawing, index) {
|
||||||
|
createSvgNames(drawing.name, index, "drawing");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#sheets-sub-panel").empty();
|
||||||
|
sheets.forEach(function (sheet, index) {
|
||||||
|
createSvgNames(sheet.name, index, "sheet");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTheme(theme) {
|
||||||
|
if (theme === "light") {
|
||||||
|
$("html").removeClass("dark").addClass("light");
|
||||||
|
$("#toggle-theme").html('<i class="fas fa-sun"></i>');
|
||||||
|
} else {
|
||||||
|
$("html").removeClass("light").addClass("dark");
|
||||||
|
$("#toggle-theme").html('<i class="fas fa-moon"></i>');
|
||||||
|
}
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
if ($("html").hasClass("dark")) {
|
||||||
|
setTheme("light");
|
||||||
|
} else {
|
||||||
|
setTheme("dark");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleClientList() {
|
||||||
|
var clientList = $("#client-list");
|
||||||
|
|
||||||
|
if (clientList.hasClass("show")) {
|
||||||
|
clientList.removeClass("show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientList.empty();
|
||||||
|
|
||||||
|
$.each(connectedClients, function (id, client) {
|
||||||
|
if (!client.shown) return;
|
||||||
|
|
||||||
|
const dropdownIcon = $("<i>")
|
||||||
|
.addClass("fas fa-chevron-down")
|
||||||
|
.css("margin-left", "0.5rem");
|
||||||
|
|
||||||
|
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||||
|
|
||||||
|
clientDiv.append(dropdownIcon);
|
||||||
|
|
||||||
|
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const clientId = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Blender ID: ${id}`);
|
||||||
|
clientDetailsDiv.append(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.headers && client.headers.length) {
|
||||||
|
const clientHeaders = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Table Headers: ${client.headers.join(", ")}`);
|
||||||
|
|
||||||
|
const scrollButton = $("<button>")
|
||||||
|
.addClass("scroll-button")
|
||||||
|
.text("Scroll to Table")
|
||||||
|
.on("click", function () {
|
||||||
|
$("html, body").animate(
|
||||||
|
{
|
||||||
|
scrollTop: $("#table-" + id).offset().top,
|
||||||
|
},
|
||||||
|
600
|
||||||
|
);
|
||||||
|
clientList.removeClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientDetailsDiv.append(clientHeaders);
|
||||||
|
clientDetailsDiv.append(scrollButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientDiv.append(clientDetailsDiv);
|
||||||
|
|
||||||
|
clientDiv.on("click", function () {
|
||||||
|
clientDetailsDiv.toggleClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientList.append(clientDiv);
|
||||||
|
});
|
||||||
|
clientList.addClass("show");
|
||||||
|
}
|
||||||
@@ -60,6 +60,10 @@ function handleBlenderDisconnect(blenderId) {
|
|||||||
delete connectedClients[blenderId];
|
delete connectedClients[blenderId];
|
||||||
removeGanttElement(blenderId);
|
removeGanttElement(blenderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#blender-count").text(function (i, text) {
|
||||||
|
return parseInt(text, 10) - 1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle 'gantt_data' event
|
// Function to handle 'gantt_data' event
|
||||||
@@ -77,6 +81,7 @@ function handleGanttData(data) {
|
|||||||
if (!connectedClients[blenderId].shown) {
|
if (!connectedClients[blenderId].shown) {
|
||||||
connectedClients[blenderId] = {
|
connectedClients[blenderId] = {
|
||||||
shown: true,
|
shown: true,
|
||||||
|
ifc_file: filename,
|
||||||
ganttTasks: ganttTasks,
|
ganttTasks: ganttTasks,
|
||||||
workSchedule: ganttWorkSched,
|
workSchedule: ganttWorkSched,
|
||||||
};
|
};
|
||||||
@@ -89,6 +94,7 @@ function handleGanttData(data) {
|
|||||||
} else {
|
} else {
|
||||||
connectedClients[blenderId] = {
|
connectedClients[blenderId] = {
|
||||||
shown: true,
|
shown: true,
|
||||||
|
ifc_file: filename,
|
||||||
ganttTasks: ganttTasks,
|
ganttTasks: ganttTasks,
|
||||||
workSchedule: ganttWorkSched,
|
workSchedule: ganttWorkSched,
|
||||||
};
|
};
|
||||||
@@ -105,6 +111,10 @@ function handleDefaultData(data) {
|
|||||||
|
|
||||||
// Function to add a new gantt with data and filename
|
// Function to add a new gantt with data and filename
|
||||||
function addGanttElement(blenderId, tasks, workSched, filename) {
|
function addGanttElement(blenderId, tasks, workSched, filename) {
|
||||||
|
$("#blender-count").text(function (i, text) {
|
||||||
|
return parseInt(text, 10) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
const ganttContainer = $("<div></div>")
|
const ganttContainer = $("<div></div>")
|
||||||
.addClass("gantt-container")
|
.addClass("gantt-container")
|
||||||
.attr("id", "container-" + blenderId);
|
.attr("id", "container-" + blenderId);
|
||||||
@@ -382,3 +392,75 @@ function toggleTheme() {
|
|||||||
setTheme("dark");
|
setTheme("dark");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleClientList() {
|
||||||
|
var clientList = $("#client-list");
|
||||||
|
|
||||||
|
if (clientList.hasClass("show")) {
|
||||||
|
clientList.removeClass("show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientList.empty();
|
||||||
|
|
||||||
|
$.each(connectedClients, function (id, client) {
|
||||||
|
if (!client.shown) return;
|
||||||
|
|
||||||
|
const dropdownIcon = $("<i>")
|
||||||
|
.addClass("fas fa-chevron-down")
|
||||||
|
.css("margin-left", "0.5rem");
|
||||||
|
|
||||||
|
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||||
|
|
||||||
|
clientDiv.append(dropdownIcon);
|
||||||
|
|
||||||
|
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const clientId = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Blender ID: ${id}`);
|
||||||
|
clientDetailsDiv.append(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.workSchedule && client.gantt) {
|
||||||
|
const clientScheduleName = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Schedule Name: ${client.workSchedule.Name}`);
|
||||||
|
|
||||||
|
const clientScheduleDate = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(
|
||||||
|
`Schedule Date: ${new Date(
|
||||||
|
client.workSchedule.CreationDate
|
||||||
|
).toLocaleDateString()}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollButton = $("<button>")
|
||||||
|
.addClass("scroll-button")
|
||||||
|
.text("Scroll to Gantt Chart")
|
||||||
|
.on("click", function () {
|
||||||
|
$("html, body").animate(
|
||||||
|
{
|
||||||
|
scrollTop: $("#gantt-" + id).offset().top,
|
||||||
|
},
|
||||||
|
600
|
||||||
|
);
|
||||||
|
clientList.removeClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientDetailsDiv.append(clientScheduleName);
|
||||||
|
clientDetailsDiv.append(clientScheduleDate);
|
||||||
|
clientDetailsDiv.append(scrollButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientDiv.append(clientDetailsDiv);
|
||||||
|
|
||||||
|
clientDiv.on("click", function () {
|
||||||
|
clientDetailsDiv.toggleClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientList.append(clientDiv);
|
||||||
|
});
|
||||||
|
clientList.addClass("show");
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ function handleBlenderDisconnect(blenderId) {
|
|||||||
delete connectedClients[blenderId];
|
delete connectedClients[blenderId];
|
||||||
removeTableElement(blenderId);
|
removeTableElement(blenderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#blender-count").text(function (i, text) {
|
||||||
|
return parseInt(text, 10) - 1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle 'csv_data' event
|
// Function to handle 'csv_data' event
|
||||||
@@ -81,6 +85,10 @@ function handleDefaultData(data) {
|
|||||||
|
|
||||||
// Function to add a new table with data and filename
|
// Function to add a new table with data and filename
|
||||||
function addTableElement(blenderId, csvData, filename) {
|
function addTableElement(blenderId, csvData, filename) {
|
||||||
|
$("#blender-count").text(function (i, text) {
|
||||||
|
return parseInt(text, 10) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
// store headers of the csv data
|
// store headers of the csv data
|
||||||
const firstLine = csvData.indexOf("\n");
|
const firstLine = csvData.indexOf("\n");
|
||||||
const csvHeaders = csvData.substring(0, firstLine).split(",");
|
const csvHeaders = csvData.substring(0, firstLine).split(",");
|
||||||
@@ -274,3 +282,66 @@ function toggleTheme() {
|
|||||||
setTheme("dark");
|
setTheme("dark");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleClientList() {
|
||||||
|
var clientList = $("#client-list");
|
||||||
|
|
||||||
|
if (clientList.hasClass("show")) {
|
||||||
|
clientList.removeClass("show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientList.empty();
|
||||||
|
|
||||||
|
$.each(connectedClients, function (id, client) {
|
||||||
|
if (!client.shown) return;
|
||||||
|
|
||||||
|
const dropdownIcon = $("<i>")
|
||||||
|
.addClass("fas fa-chevron-down")
|
||||||
|
.css("margin-left", "0.5rem");
|
||||||
|
|
||||||
|
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||||
|
|
||||||
|
clientDiv.append(dropdownIcon);
|
||||||
|
|
||||||
|
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
const clientId = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Blender ID: ${id}`);
|
||||||
|
clientDetailsDiv.append(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.headers && client.headers.length) {
|
||||||
|
const clientHeaders = $("<div>")
|
||||||
|
.addClass("client-detail")
|
||||||
|
.text(`Table Headers: ${client.headers.join(", ")}`);
|
||||||
|
|
||||||
|
const scrollButton = $("<button>")
|
||||||
|
.addClass("scroll-button")
|
||||||
|
.text("Scroll to Table")
|
||||||
|
.on("click", function () {
|
||||||
|
$("html, body").animate(
|
||||||
|
{
|
||||||
|
scrollTop: $("#table-" + id).offset().top,
|
||||||
|
},
|
||||||
|
600
|
||||||
|
);
|
||||||
|
clientList.removeClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientDetailsDiv.append(clientHeaders);
|
||||||
|
clientDetailsDiv.append(scrollButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientDiv.append(clientDetailsDiv);
|
||||||
|
|
||||||
|
clientDiv.on("click", function () {
|
||||||
|
clientDetailsDiv.toggleClass("show");
|
||||||
|
});
|
||||||
|
|
||||||
|
clientList.append(clientDiv);
|
||||||
|
});
|
||||||
|
clientList.addClass("show");
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Web Client</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="/static/css/drawings.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.1.1/svg.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="https://cdn.socket.io/4.0.0/socket.io.min.js"
|
||||||
|
></script>
|
||||||
|
<script>
|
||||||
|
var SOCKET_PORT = {{port}};
|
||||||
|
</script>
|
||||||
|
<script defer src="./static/js/drawings.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<img
|
||||||
|
src="https://blenderbim.org/assets/images/blender/blender-logo.png"
|
||||||
|
alt="Logo"
|
||||||
|
class="logo"
|
||||||
|
/>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">IFC Data</a></li>
|
||||||
|
<li><a href="/gantt">Gantt Chart</a></li>
|
||||||
|
<li><a href="/drawings" class="active">Drawings</a></li>
|
||||||
|
</ul>
|
||||||
|
<button id="toggle-theme" onclick="toggleTheme()">
|
||||||
|
<i class="fas fa-moon"></i>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<div id="connected-list-div">
|
||||||
|
<button id="show-connected-button" onclick="toggleClientList()">
|
||||||
|
Connected Blenders:
|
||||||
|
<span id="blender-count">0</span>
|
||||||
|
<i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div id="client-list"></div>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid d-flex" id="container">
|
||||||
|
<div class="col-2 left">
|
||||||
|
<!-- Dropdown menu -->
|
||||||
|
<div class="dropdown mb-3">
|
||||||
|
<label for="dropdown-menu">IFC File:</label>
|
||||||
|
<select class="p-0 pl-2" id="dropdown-menu">
|
||||||
|
<!-- IFC files will be dynamically populated here -->
|
||||||
|
<option value="" selected disabled hidden>IFC File</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Panel with sub-panels -->
|
||||||
|
<div class="panel">
|
||||||
|
<div id="accordion">
|
||||||
|
<div class="card pb-2">
|
||||||
|
<div class="card-header overflow-hidden" id="headingOne">
|
||||||
|
<button
|
||||||
|
class="btn p-0 w-100 text-left"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#collapseOne"
|
||||||
|
>
|
||||||
|
Drawings
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="collapseOne" class="collapse show">
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="sub-panel pl-2" id="drawings-sub-panel">
|
||||||
|
<!-- Drawings will be dynamically populated here -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card pb-2">
|
||||||
|
<div class="card-header overflow-hidden" id="headingTwo">
|
||||||
|
<button
|
||||||
|
class="btn p-0 w-100 text-left"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#collapseTwo"
|
||||||
|
>
|
||||||
|
Sheets
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="collapseTwo" class="collapse show">
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="sub-panel pl-2" id="sheets-sub-panel">
|
||||||
|
<!-- Sheets will be dynamically populated here -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-10 right pl-3">
|
||||||
|
<div id="svg-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -34,11 +34,23 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">IFC Data</a></li>
|
<li><a href="/">IFC Data</a></li>
|
||||||
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
||||||
|
<li><a href="/drawings">Drawings</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<button id="toggle-theme" onclick="toggleTheme()">
|
<button id="toggle-theme" onclick="toggleTheme()">
|
||||||
<i class="fas fa-moon"></i>
|
<i class="fas fa-moon"></i>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div id="connected-list-div">
|
||||||
|
<button id="show-connected-button" onclick="toggleClientList()">
|
||||||
|
Connected Blenders:
|
||||||
|
<span id="blender-count">0</span>
|
||||||
|
<i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div id="client-list"></div>
|
||||||
|
</div>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
<footer>
|
||||||
|
<p>BlenderBIM Version: {{version}}</p>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -41,11 +41,23 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="/" class="active">IFC Data</a></li>
|
<li><a href="/" class="active">IFC Data</a></li>
|
||||||
<li><a href="/gantt">Gantt Chart</a></li>
|
<li><a href="/gantt">Gantt Chart</a></li>
|
||||||
|
<li><a href="/drawings">Drawings</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<button id="toggle-theme" onclick="toggleTheme()">
|
<button id="toggle-theme" onclick="toggleTheme()">
|
||||||
<i class="fas fa-moon"></i>
|
<i class="fas fa-moon"></i>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div id="connected-list-div">
|
||||||
|
<button id="show-connected-button" onclick="toggleClientList()">
|
||||||
|
Connected Blenders:
|
||||||
|
<span id="blender-count">0</span>
|
||||||
|
<i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div id="client-list"></div>
|
||||||
|
</div>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
<footer>
|
||||||
|
<p>BlenderBIM Version: {{version}}</p>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
blenderbim_lib_path = os.path.join(blenderbim_path, "libs", "site", "packages")
|
blenderbim_lib_path = os.path.join(blenderbim_path, "libs", "site", "packages")
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["blenderbim_lib_path"] = str(blenderbim_lib_path)
|
env["BLENDERBIM_LIB_PATH"] = str(blenderbim_lib_path)
|
||||||
|
env["BLENDERBIM_VERSION"] = tool.Blender.get_blenderbim_version()
|
||||||
|
|
||||||
ws_process = subprocess.Popen(
|
ws_process = subprocess.Popen(
|
||||||
[sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"],
|
[sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"],
|
||||||
@@ -284,6 +285,8 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
cls.handle_csv_operator(operator["operator"])
|
cls.handle_csv_operator(operator["operator"])
|
||||||
elif operator["sourcePage"] == "gantt":
|
elif operator["sourcePage"] == "gantt":
|
||||||
cls.handle_gantt_operator(operator["operator"])
|
cls.handle_gantt_operator(operator["operator"])
|
||||||
|
elif operator["sourcePage"] == "drawings":
|
||||||
|
cls.handle_drawings_operator(operator["operator"])
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -328,6 +331,29 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
||||||
cls.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
cls.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def handle_drawings_operator(cls, operator_data: dict) -> None:
|
||||||
|
if operator_data["type"] == "getDrawings":
|
||||||
|
drawings_data = []
|
||||||
|
sheets_data = []
|
||||||
|
ifc_file_dir = os.path.dirname(bpy.context.scene.BIMProperties.ifc_file)
|
||||||
|
|
||||||
|
sheets = [d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "SHEET"]
|
||||||
|
for sheet in sorted(sheets, key=lambda s: getattr(s, "Identification", getattr(s, "DocumentId", None))):
|
||||||
|
for reference in tool.Drawing.get_document_references(sheet):
|
||||||
|
reference_description = tool.Drawing.get_reference_description(reference)
|
||||||
|
reference_location = tool.Drawing.get_reference_location(reference)
|
||||||
|
reference_name = os.path.basename(reference_location)
|
||||||
|
reference_path = os.path.join(ifc_file_dir, reference_location)
|
||||||
|
if reference_description == "SHEET":
|
||||||
|
sheets_data.append({"name": reference_name, "path": reference_path})
|
||||||
|
if reference_description == "DRAWING":
|
||||||
|
drawings_data.append({"name": reference_name, "path": reference_path})
|
||||||
|
|
||||||
|
cls.send_webui_data(
|
||||||
|
data={"drawings": drawings_data, "sheets": sheets_data}, data_key="drawings_data", event="drawings_data"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def open_web_browser(cls, port: int) -> None:
|
def open_web_browser(cls, port: int) -> None:
|
||||||
webbrowser.open(f"http://127.0.0.1:{port}/")
|
webbrowser.open(f"http://127.0.0.1:{port}/")
|
||||||
|
|||||||
Reference in New Issue
Block a user