Merge pull request #5236 from IfcOpenShell/webui_updates

Add RMB context menu to web UI spreadsheets and small updates
This commit is contained in:
Dion Moult
2024-08-29 09:55:55 +10:00
committed by GitHub
14 changed files with 105 additions and 33 deletions
@@ -38,9 +38,9 @@ class WebNamespace(socketio.AsyncNamespace):
async def on_connect(self, sid, environ):
print(f"Web client connected: {sid}")
await sio.emit("theme_data", blender_theme, namespace="/web", room=sid)
if blender_messages:
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)
async def on_disconnect(self, sid):
@@ -55,7 +55,6 @@ class WebNamespace(socketio.AsyncNamespace):
)
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()
@@ -71,6 +70,7 @@ class WebNamespace(socketio.AsyncNamespace):
if "demo_data" in messages:
await self.emit("demo_data", {"blenderId": blenderId, "data": messages["demo_data"]}, room=sid)
# Blender namespace
class BlenderNamespace(socketio.AsyncNamespace):
def __init__(self, namespace):
@@ -115,7 +115,7 @@ class BlenderNamespace(socketio.AsyncNamespace):
blender_theme = data
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):
print(f"Demo data from Blender client {sid}")
blender_messages[sid]["demo_data"] = data
@@ -54,10 +54,7 @@ body {
#container {
flex: 1;
margin-top: var(--margin-medium);
margin-left: var(--margin-small);
margin-right: var(--margin-small);
margin-bottom: var(--margin-medium);
padding: var(--margin-small);
}
h3 {
@@ -55,10 +55,7 @@ body {
}
#container {
margin-top: var(--margin-medium);
margin-left: var(--margin-tiny);
margin-right: var(--margin-tiny);
margin-bottom: var(--margin-medium);
padding: var(--margin-small);
height: calc(100vh - var(--nav-height));
box-sizing: border-box;
display: flex;
@@ -74,10 +74,7 @@ body {
#container {
flex: 1;
margin-top: var(--margin-medium);
margin-left: var(--margin-small);
margin-right: var(--margin-small);
margin-bottom: var(--margin-medium);
padding: var(--margin-small);
}
h3 {
@@ -202,11 +202,10 @@ footer p {
}
.table-container {
position: absolute;
display: block;
height: calc(100% - var(--margin-small) * 2);
width: calc(100% - var(--margin-small) * 2);
overflow-x: scroll;
height: calc(100vh - var(--margin-small));
width: 100%;
margin-bottom: var(--margin-medium);
}
.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;
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)
}
.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;
background-color: var(--blender-panel-background) !important;
transition: filter 0.1s ease;
}
.tabulator-menu-item:hover {
:root.blender .tabulator-menu-item:hover {
filter: brightness(1.4);
}
}
@@ -207,8 +207,35 @@ function addTableElement(blenderId, csvData, filename) {
return menu;
}
var containerHeight=$('.table-container').height();
var h3Height=$('.table-container>h3').height();
// create right click context menu for choosing column top calculation
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, {
placeholder: "No data to display",
resizableColumnGuide: true,
@@ -222,14 +249,21 @@ function addTableElement(blenderId, csvData, filename) {
movableColumns: true,
// Our fields are never nested, and we use the "." character in queries
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) {
menu = createHeaderMenu(definitions, table);
headerMenu = createHeaderMenu(definitions, table);
definitions.forEach((column) => {
column.visible = true;
column.headerFilter = true;
column.headerMenu = menu;
// TODO: more user control to choose function, style at bottomCalc
column.topCalc = 'sum';
column.headerMenu = headerMenu;
column.contextMenu = createColumnContextMenu(column);
});
return definitions;
},
@@ -239,7 +273,9 @@ function addTableElement(blenderId, csvData, filename) {
.css("margin-left", "10px")
.css("cursor", "pointer");
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) {
var index = row.getIndex(); // the guid of the object
@@ -5,6 +5,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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, -->
<!-- using registered static path in the server -->
<link rel="stylesheet" href="/static/css/demo.css" id="demo-stylesheet" />
@@ -4,6 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
@@ -4,6 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" href="/static/css/gantt.css" id="gantt-stylesheet" />
<link
@@ -4,6 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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"
@@ -88,6 +88,7 @@ classes = (
operator.SelectAllDrawings,
operator.SelectAssignedProduct,
operator.SelectDocIfcFile,
operator.OpenDocumentationWebUi,
prop.Variable,
prop.Drawing,
prop.Document,
@@ -2872,3 +2872,16 @@ class ConvertSVGToDXF(bpy.types.Operator):
self.report({"INFO"}, f"{len(drawing_uris)} drawings were converted to .dxf.")
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.operator("bim.create_drawing", text="", icon="OUTPUT")
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(
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
)
+2
View File
@@ -435,6 +435,7 @@ class Web(bonsai.core.tool.Web):
continue
reference_name = os.path.basename(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})
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)
reference_name = os.path.basename(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})
cls.send_webui_data(