mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 10:16:53 +00:00
Merge pull request #5236 from IfcOpenShell/webui_updates
Add RMB context menu to web UI spreadsheets and small updates
This commit is contained in:
@@ -38,9 +38,9 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
|
|
||||||
async def on_connect(self, sid, environ):
|
async def on_connect(self, sid, environ):
|
||||||
print(f"Web client connected: {sid}")
|
print(f"Web client connected: {sid}")
|
||||||
|
await sio.emit("theme_data", blender_theme, namespace="/web", room=sid)
|
||||||
if blender_messages:
|
if blender_messages:
|
||||||
await sio.emit("connected_clients", list(blender_messages.keys()), namespace="/web", room=sid)
|
await sio.emit("connected_clients", list(blender_messages.keys()), namespace="/web", room=sid)
|
||||||
await sio.emit("theme_data", blender_theme, namespace="/web", room=sid)
|
|
||||||
await self.send_cached_messages(sid)
|
await self.send_cached_messages(sid)
|
||||||
|
|
||||||
async def on_disconnect(self, sid):
|
async def on_disconnect(self, sid):
|
||||||
@@ -55,7 +55,6 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def on_get_svg(self, sid, data):
|
async def on_get_svg(self, sid, data):
|
||||||
print("hello world!")
|
|
||||||
file_path = data["path"]
|
file_path = data["path"]
|
||||||
with open(file_path, "r") as file:
|
with open(file_path, "r") as file:
|
||||||
svg_data = file.read()
|
svg_data = file.read()
|
||||||
@@ -71,6 +70,7 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
if "demo_data" in messages:
|
if "demo_data" in messages:
|
||||||
await self.emit("demo_data", {"blenderId": blenderId, "data": messages["demo_data"]}, room=sid)
|
await self.emit("demo_data", {"blenderId": blenderId, "data": messages["demo_data"]}, room=sid)
|
||||||
|
|
||||||
|
|
||||||
# Blender namespace
|
# Blender namespace
|
||||||
class BlenderNamespace(socketio.AsyncNamespace):
|
class BlenderNamespace(socketio.AsyncNamespace):
|
||||||
def __init__(self, namespace):
|
def __init__(self, namespace):
|
||||||
@@ -115,7 +115,7 @@ class BlenderNamespace(socketio.AsyncNamespace):
|
|||||||
blender_theme = data
|
blender_theme = data
|
||||||
await sio.emit("theme_data", data, namespace="/web")
|
await sio.emit("theme_data", data, namespace="/web")
|
||||||
|
|
||||||
# this function will be called when the event demo_data is emitted
|
# this function will be called when the event demo_data is emitted
|
||||||
async def on_demo_data(self, sid, data):
|
async def on_demo_data(self, sid, data):
|
||||||
print(f"Demo data from Blender client {sid}")
|
print(f"Demo data from Blender client {sid}")
|
||||||
blender_messages[sid]["demo_data"] = data
|
blender_messages[sid]["demo_data"] = data
|
||||||
|
|||||||
@@ -54,10 +54,7 @@ body {
|
|||||||
|
|
||||||
#container {
|
#container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-top: var(--margin-medium);
|
padding: var(--margin-small);
|
||||||
margin-left: var(--margin-small);
|
|
||||||
margin-right: var(--margin-small);
|
|
||||||
margin-bottom: var(--margin-medium);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
|||||||
@@ -55,10 +55,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
margin-top: var(--margin-medium);
|
padding: var(--margin-small);
|
||||||
margin-left: var(--margin-tiny);
|
|
||||||
margin-right: var(--margin-tiny);
|
|
||||||
margin-bottom: var(--margin-medium);
|
|
||||||
height: calc(100vh - var(--nav-height));
|
height: calc(100vh - var(--nav-height));
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -74,10 +74,7 @@ body {
|
|||||||
|
|
||||||
#container {
|
#container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-top: var(--margin-medium);
|
padding: var(--margin-small);
|
||||||
margin-left: var(--margin-small);
|
|
||||||
margin-right: var(--margin-small);
|
|
||||||
margin-bottom: var(--margin-medium);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
|||||||
@@ -202,11 +202,10 @@ footer p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
position: absolute;
|
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100% - var(--margin-small) * 2);
|
height: calc(100vh - var(--margin-small));
|
||||||
width: calc(100% - var(--margin-small) * 2);
|
width: 100%;
|
||||||
overflow-x: scroll;
|
margin-bottom: var(--margin-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.csv-table {
|
.csv-table {
|
||||||
@@ -281,22 +280,29 @@ button:hover {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
:root.blender .tabulator-selected .tabulator-cell {
|
:root.blender .tabulator-selected .tabulator-cell,
|
||||||
|
:root.blender .tabulator-row.tabulator-selected {
|
||||||
background-color: var(--blender-active-highlight) !important;
|
background-color: var(--blender-active-highlight) !important;
|
||||||
color: var(--blender-active-text-highlight) !important;
|
color: var(--blender-active-text-highlight) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:root.blender .tabulator-selected:hover .tabulator-cell {
|
:root.blender .tabulator-selected:hover .tabulator-cell,
|
||||||
|
:root.blender .tabulator-row.tabulator-selected:hover {
|
||||||
filter: brightness(1.2)
|
filter: brightness(1.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabulator-menu-item {
|
:root.blender .tabulator-popup-container {
|
||||||
|
background: var(--blender-panel-background);
|
||||||
|
border: var(--blender-tab-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.blender .tabulator-menu-item {
|
||||||
color: var(--blender-text) !important;
|
color: var(--blender-text) !important;
|
||||||
background-color: var(--blender-panel-background) !important;
|
background-color: var(--blender-panel-background) !important;
|
||||||
transition: filter 0.1s ease;
|
transition: filter 0.1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabulator-menu-item:hover {
|
:root.blender .tabulator-menu-item:hover {
|
||||||
filter: brightness(1.4);
|
filter: brightness(1.4);
|
||||||
}
|
}
|
||||||
@@ -207,8 +207,35 @@ function addTableElement(blenderId, csvData, filename) {
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
var containerHeight=$('.table-container').height();
|
// create right click context menu for choosing column top calculation
|
||||||
var h3Height=$('.table-container>h3').height();
|
function createColumnContextMenu(column) {
|
||||||
|
const field = column.field;
|
||||||
|
const calculations = ["sum", "avg", "max", "min"];
|
||||||
|
|
||||||
|
// label is the item label in the menu
|
||||||
|
// update column defination on clicking an item
|
||||||
|
// specifically the topCalc and pass calc type to the topCalcFormatter function
|
||||||
|
// uisng the topCalcFormatterParams field
|
||||||
|
return calculations.map((calc) => ({
|
||||||
|
label: `Show ${
|
||||||
|
calc.charAt(0).toUpperCase() + calc.slice(1)
|
||||||
|
} for ${field}`,
|
||||||
|
action: (e, cell) =>
|
||||||
|
cell
|
||||||
|
.getColumn()
|
||||||
|
.updateDefinition({ topCalc: calc, topCalcFormatterParams: calc }),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcFormatter(cell, formatterParams, onRendered) {
|
||||||
|
// formatterParams is anything in the topCalcFormatterParams field in the column definition
|
||||||
|
// so we pass the calculation type to display what the calculation type is
|
||||||
|
return `${formatterParams}: ${cell.getValue()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
var containerHeight = $(".table-container").height();
|
||||||
|
var h3Height = $(".table-container>h3").height();
|
||||||
|
|
||||||
var table = new Tabulator("#table-" + blenderId, {
|
var table = new Tabulator("#table-" + blenderId, {
|
||||||
placeholder: "No data to display",
|
placeholder: "No data to display",
|
||||||
resizableColumnGuide: true,
|
resizableColumnGuide: true,
|
||||||
@@ -222,14 +249,21 @@ function addTableElement(blenderId, csvData, filename) {
|
|||||||
movableColumns: true,
|
movableColumns: true,
|
||||||
// Our fields are never nested, and we use the "." character in queries
|
// Our fields are never nested, and we use the "." character in queries
|
||||||
nestedFieldSeparator: false,
|
nestedFieldSeparator: false,
|
||||||
|
// defines column defualts that are applied to all columns
|
||||||
|
columnDefaults: {
|
||||||
|
visible: true,
|
||||||
|
headerFilter: true,
|
||||||
|
topCalc: "sum",
|
||||||
|
topCalcFormatter: calcFormatter,
|
||||||
|
topCalcFormatterParams: "sum",
|
||||||
|
},
|
||||||
|
// defines a callback to edit the column definition generated by autoColumns
|
||||||
|
// that cannot be set as default
|
||||||
autoColumnsDefinitions: function (definitions) {
|
autoColumnsDefinitions: function (definitions) {
|
||||||
menu = createHeaderMenu(definitions, table);
|
headerMenu = createHeaderMenu(definitions, table);
|
||||||
definitions.forEach((column) => {
|
definitions.forEach((column) => {
|
||||||
column.visible = true;
|
column.headerMenu = headerMenu;
|
||||||
column.headerFilter = true;
|
column.contextMenu = createColumnContextMenu(column);
|
||||||
column.headerMenu = menu;
|
|
||||||
// TODO: more user control to choose function, style at bottomCalc
|
|
||||||
column.topCalc = 'sum';
|
|
||||||
});
|
});
|
||||||
return definitions;
|
return definitions;
|
||||||
},
|
},
|
||||||
@@ -239,7 +273,9 @@ function addTableElement(blenderId, csvData, filename) {
|
|||||||
.css("margin-left", "10px")
|
.css("margin-left", "10px")
|
||||||
.css("cursor", "pointer");
|
.css("cursor", "pointer");
|
||||||
tableTitle.append(downloadCsv);
|
tableTitle.append(downloadCsv);
|
||||||
downloadCsv.on('click', function() { table.download("csv", "data.csv"); })
|
downloadCsv.on("click", function () {
|
||||||
|
table.download("csv", "data.csv");
|
||||||
|
});
|
||||||
|
|
||||||
table.on("rowSelected", function (row) {
|
table.on("rowSelected", function (row) {
|
||||||
var index = row.getIndex(); // the guid of the object
|
var index = row.getIndex(); // the guid of the object
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Bonsai Web UI</title>
|
<title>Bonsai Web UI</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://bonsaibim.org/assets/images/favicon-blender.png"
|
||||||
|
/>
|
||||||
<!-- here we request the CSS file from the server, -->
|
<!-- here we request the CSS file from the server, -->
|
||||||
<!-- using registered static path in the server -->
|
<!-- using registered static path in the server -->
|
||||||
<link rel="stylesheet" href="/static/css/demo.css" id="demo-stylesheet" />
|
<link rel="stylesheet" href="/static/css/demo.css" id="demo-stylesheet" />
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Bonsai Web UI</title>
|
<title>Bonsai Web UI</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://bonsaibim.org/assets/images/favicon-blender.png"
|
||||||
|
/>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
|
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Bonsai Web UI</title>
|
<title>Bonsai Web UI</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://bonsaibim.org/assets/images/favicon-blender.png"
|
||||||
|
/>
|
||||||
<link rel="stylesheet" type="text/css" href="/jsgantt/jsgantt.css" />
|
<link rel="stylesheet" type="text/css" href="/jsgantt/jsgantt.css" />
|
||||||
<link rel="stylesheet" href="/static/css/gantt.css" id="gantt-stylesheet" />
|
<link rel="stylesheet" href="/static/css/gantt.css" id="gantt-stylesheet" />
|
||||||
<link
|
<link
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Bonsai Web UI</title>
|
<title>Bonsai Web UI</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/x-icon"
|
||||||
|
href="https://bonsaibim.org/assets/images/favicon-blender.png"
|
||||||
|
/>
|
||||||
<link rel="stylesheet" href="/static/css/index.css" id="index-stylesheet" />
|
<link rel="stylesheet" href="/static/css/index.css" id="index-stylesheet" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ classes = (
|
|||||||
operator.SelectAllDrawings,
|
operator.SelectAllDrawings,
|
||||||
operator.SelectAssignedProduct,
|
operator.SelectAssignedProduct,
|
||||||
operator.SelectDocIfcFile,
|
operator.SelectDocIfcFile,
|
||||||
|
operator.OpenDocumentationWebUi,
|
||||||
prop.Variable,
|
prop.Variable,
|
||||||
prop.Drawing,
|
prop.Drawing,
|
||||||
prop.Document,
|
prop.Document,
|
||||||
|
|||||||
@@ -2872,3 +2872,16 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
|||||||
|
|
||||||
self.report({"INFO"}, f"{len(drawing_uris)} drawings were converted to .dxf.")
|
self.report({"INFO"}, f"{len(drawing_uris)} drawings were converted to .dxf.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class OpenDocumentationWebUi(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.open_documentation_web_ui"
|
||||||
|
bl_label = "Open Documentation Web UI"
|
||||||
|
bl_description = "Open the documentation web UI page"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if not context.scene.WebProperties.is_connected:
|
||||||
|
bpy.ops.bim.connect_websocket_server(page="documentation")
|
||||||
|
else:
|
||||||
|
bpy.ops.bim.bim.open_web_browser(page="documentation")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -283,6 +283,9 @@ class BIM_PT_drawings(Panel):
|
|||||||
create_drawing_button = row.row(align=True)
|
create_drawing_button = row.row(align=True)
|
||||||
create_drawing_button.operator("bim.create_drawing", text="", icon="OUTPUT")
|
create_drawing_button.operator("bim.create_drawing", text="", icon="OUTPUT")
|
||||||
create_drawing_button.enabled = active_drawing.ifc_definition_id > 0
|
create_drawing_button.enabled = active_drawing.ifc_definition_id > 0
|
||||||
|
|
||||||
|
# might need a different icon since the URL icon is already used by the open_drawing
|
||||||
|
row.operator("bim.open_documentation_web_ui", icon="URL", text="")
|
||||||
self.layout.template_list(
|
self.layout.template_list(
|
||||||
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
|
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -435,6 +435,7 @@ class Web(bonsai.core.tool.Web):
|
|||||||
continue
|
continue
|
||||||
reference_name = os.path.basename(reference.Location)
|
reference_name = os.path.basename(reference.Location)
|
||||||
reference_path = os.path.join(ifc_file_dir, reference.Location)
|
reference_path = os.path.join(ifc_file_dir, reference.Location)
|
||||||
|
reference_path = os.path.normpath(reference_path)
|
||||||
sheets_data.append({"name": reference_name, "path": reference_path})
|
sheets_data.append({"name": reference_name, "path": reference_path})
|
||||||
|
|
||||||
drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
|
drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
|
||||||
@@ -442,6 +443,7 @@ class Web(bonsai.core.tool.Web):
|
|||||||
document = tool.Drawing.get_drawing_document(drawing)
|
document = tool.Drawing.get_drawing_document(drawing)
|
||||||
reference_name = os.path.basename(document.Location)
|
reference_name = os.path.basename(document.Location)
|
||||||
reference_path = os.path.join(ifc_file_dir, document.Location)
|
reference_path = os.path.join(ifc_file_dir, document.Location)
|
||||||
|
reference_path = os.path.normpath(reference_path)
|
||||||
drawings_data.append({"name": reference_name, "path": reference_path})
|
drawings_data.append({"name": reference_name, "path": reference_path})
|
||||||
|
|
||||||
cls.send_webui_data(
|
cls.send_webui_data(
|
||||||
|
|||||||
Reference in New Issue
Block a user