mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +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):
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user