Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 66 additions & 13 deletions src/main/python/utils/Container.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,73 @@ def delete(self, l):
print(f"[DEBUG] delete: error deleting item {getattr(item, 'name', None)}: {e}")

if items_to_delete:
self.update_components_editability()
self.graphics.push_snapshot()

except Exception as e:
print("[DEBUG] delete: outer exception:", e)

def update_components_editability(self):
"""
Recalculate and update the editable/read-only state of every component
in the scene based strictly on its current connection state.

- If a component has an incoming connection (connected to the output of an upstream component):
Its property window becomes read-only / disabled (e.g., product stream).
- If a component has NO incoming connections (feed stream or isolated component):
Its property window becomes editable immediately, even post-simulation.
- If a component is completely disconnected from all other components:
Its property window is immediately re-enabled.
"""
try:
scene = getattr(self, "scene", None)
if scene is None and hasattr(self, "graphics"):
scene = getattr(self.graphics, "scene", None)
if scene is None:
return

for item in scene.items():
if not isinstance(item, NodeItem):
continue

dw = getattr(item, "dock_widget", None)
if dw is None:
continue

# Check if item has any incoming connection from an upstream component's output
has_incoming_connection = False
for sock in getattr(item, "input", []):
if len(getattr(sock, "in_lines", [])) > 0:
has_incoming_connection = True
break

# Check if item has any outgoing connection
has_outgoing_connection = False
for sock in getattr(item, "output", []):
if len(getattr(sock, "out_lines", [])) > 0:
has_outgoing_connection = True
break

is_connected = has_incoming_connection or has_outgoing_connection

if getattr(item, "type", "") == "MaterialStream":
if has_incoming_connection:
if hasattr(item.obj, "disableInputDataTab"):
item.obj.disableInputDataTab(dw)
elif hasattr(dw, "set_read_only"):
dw.set_read_only(True)
else:
if hasattr(item.obj, "enableInputDataTab"):
item.obj.enableInputDataTab(dw)
elif hasattr(dw, "set_read_only"):
dw.set_read_only(False)
else:
if not is_connected or not has_incoming_connection:
if hasattr(dw, "set_read_only"):
dw.set_read_only(False)
except Exception as e:
print(f"[DEBUG] update_components_editability failed: {e}")

def fetch_object(self, name):
for i in self.unit_operations:
if i.name == name:
Expand Down Expand Up @@ -396,11 +458,8 @@ def try_get_other_node_from_line(ln, this_item):
it.update_tooltip_selectedVar()
except Exception as tooltip_err:
print(f"[DEBUG] update_tooltip_selectedVar failed for {it.name}: {tooltip_err}")
try:
if len(it.input[0].in_lines) > 0:
it.obj.disableInputDataTab(it.dock_widget)
except Exception:
pass

self.update_components_editability()

# Step 9: Populate Results Tabs (via signal for thread-safety)
self.signals.results_ready.emit()
Expand Down Expand Up @@ -430,14 +489,8 @@ def _populate_results(self):
if hasattr(dw, 'set_read_only'):
dw.set_read_only(False)

# Preserve connection-based restrictions for MaterialStream
for it in self.graphics.scene.items():
if isinstance(it, NodeItem) and getattr(it, "type", "") == "MaterialStream":
try:
if len(it.input[0].in_lines) > 0:
it.obj.disableInputDataTab(it.dock_widget)
except Exception as e:
print(f"[DEBUG] MaterialStream input state update failed: {e}")
# Preserve connection-based restrictions for all components
self.update_components_editability()

# Make all result table items non-editable
for dw in dock_widget_lst:
Expand Down
34 changes: 18 additions & 16 deletions src/main/python/utils/Graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ def load_canvas_from_snapshot(self, snapshot, container):
except Exception as e:
print("[DEBUG] load_canvas_from_snapshot: rebuild connection failed:", e)

if hasattr(container, "update_components_editability"):
container.update_components_editability()

self._restore_counters(container.unit_operations)

except Exception as e:
Expand Down Expand Up @@ -700,14 +703,9 @@ def mouseReleaseEvent(self, event):
if getattr(self.new_line.target.parent.obj, 'type', None) not in stm:
self.new_line.target.parent.obj.add_connection(1, self.new_line.target.id, self.new_line.source.parent.obj)

sc = self.new_line.source.parent
tg = self.new_line.target.parent
if getattr(sc.obj, 'type', None) == 'MaterialStream':
if len(getattr(sc.input[0], 'in_lines', [])) > 0:
sc.obj.disableInputDataTab(sc.dock_widget)
if getattr(tg.obj, 'type', None) == 'MaterialStream':
if len(getattr(tg.input[0], 'in_lines', [])) > 0:
tg.obj.disableInputDataTab(tg.dock_widget)
if hasattr(self, "parent") and hasattr(self.parent, "container") and self.parent.container:
if hasattr(self.parent.container, "update_components_editability"):
self.parent.container.update_components_editability()

try:
self.parent.container.graphics.push_snapshot()
Expand Down Expand Up @@ -756,14 +754,9 @@ def mouseReleaseEvent(self, event):
if getattr(self.new_line.target.parent.obj, 'type', None) not in stm:
self.new_line.target.parent.obj.add_connection(1, self.new_line.target.id, self.new_line.source.parent.obj)

sc = self.new_line.source.parent
tg = self.new_line.target.parent
if getattr(sc.obj, 'type', None) == 'MaterialStream':
if len(getattr(sc.input[0], 'in_lines', [])) > 0:
sc.obj.disableInputDataTab(sc.dock_widget)
if getattr(tg.obj, 'type', None) == 'MaterialStream':
if len(getattr(tg.input[0], 'in_lines', [])) > 0:
tg.obj.disableInputDataTab(tg.dock_widget)
if hasattr(self, "parent") and hasattr(self.parent, "container") and self.parent.container:
if hasattr(self.parent.container, "update_components_editability"):
self.parent.container.update_components_editability()

try:
self.parent.container.graphics.push_snapshot()
Expand Down Expand Up @@ -1228,6 +1221,9 @@ def resize_node(self, new_width, new_height):

self.update()

if hasattr(self, "container") and self.container is not None and hasattr(self.container, "update_components_editability"):
self.container.update_components_editability()

def _reposition_sockets(self):
"""Reposition sockets after a resize while keeping their relative
positions on the component.
Expand Down Expand Up @@ -1541,6 +1537,9 @@ def update_input_ports(self, new_count):

self.update()

if hasattr(self, "container") and self.container is not None and hasattr(self.container, "update_components_editability"):
self.container.update_components_editability()

print(
f"[DEBUG] update_input_ports complete: "
f"{old_count} -> {new_count}"
Expand Down Expand Up @@ -1850,6 +1849,9 @@ def update_output_ports(self, new_count):

self.update()

if hasattr(self, "container") and self.container is not None and hasattr(self.container, "update_components_editability"):
self.container.update_components_editability()

print(
f"[DEBUG] update_output_ports complete: "
f"{old_count} -> {new_count}"
Expand Down
79 changes: 63 additions & 16 deletions src/main/python/utils/Streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,27 +543,74 @@ def OM_Flowsheet_Equation(self, addedcomp, method):


def disableInputDataTab(self, dockwidget):
if dockwidget is None:
return

# Disable main inputs
dockwidget.comboBox.setDisabled(True)
if hasattr(dockwidget, 'comboBox') and dockwidget.comboBox is not None:
dockwidget.comboBox.setDisabled(True)

# Safe access helper
def safe_set_text(widget, key, default=0):
if key in self.variables:
widget.setText(str(round(float(self.variables[key]['value']),4)))
else:
widget.setText(str(default))
print(f"[DEBUG] Key not found: {key}")
widget.setDisabled(True)

safe_set_text(dockwidget.input_dict['P'], 'P')
safe_set_text(dockwidget.input_dict['T'], 'T')
safe_set_text(dockwidget.input_dict['MolFlow'], 'F_p[1]')

dockwidget.cbTP.setCurrentText(str(self.thermo_package))
dockwidget.cbTP.setDisabled(True)
dockwidget.pushButton_2.setDisabled(True)
if hasattr(widget, 'setText') and hasattr(widget, 'setDisabled'):
if key in self.variables:
widget.setText(str(round(float(self.variables[key]['value']),4)))
else:
widget.setText(str(default))
print(f"[DEBUG] Key not found: {key}")
widget.setDisabled(True)

if hasattr(dockwidget, 'input_dict') and isinstance(dockwidget.input_dict, dict):
if 'P' in dockwidget.input_dict:
safe_set_text(dockwidget.input_dict['P'], 'P')
if 'T' in dockwidget.input_dict:
safe_set_text(dockwidget.input_dict['T'], 'T')
if 'MolFlow' in dockwidget.input_dict:
safe_set_text(dockwidget.input_dict['MolFlow'], 'F_p[1]')

if hasattr(dockwidget, 'cbTP') and dockwidget.cbTP is not None:
dockwidget.cbTP.setCurrentText(str(self.thermo_package))
dockwidget.cbTP.setDisabled(True)

if hasattr(dockwidget, 'pushButton_2') and dockwidget.pushButton_2 is not None:
dockwidget.pushButton_2.setDisabled(True)

# Disable x_pc values safely
for index, k in enumerate(dockwidget.x_pclist):
for index, k in enumerate(getattr(dockwidget, 'x_pclist', [])):
key_name = f"x_pc[1,{index+1}]"
safe_set_text(k, key_name)

for attr in ('btn_normalize', 'btn_equalize', 'cbCompositionBasis'):
w = getattr(dockwidget, attr, None)
if w is not None and hasattr(w, 'setDisabled'):
w.setDisabled(True)

def enableInputDataTab(self, dockwidget):
if dockwidget is None:
return

if hasattr(dockwidget, 'comboBox') and dockwidget.comboBox is not None:
dockwidget.comboBox.setDisabled(False)

if hasattr(dockwidget, 'input_dict') and isinstance(dockwidget.input_dict, dict):
for k, w in dockwidget.input_dict.items():
if hasattr(w, 'setDisabled'):
w.setDisabled(False)

if hasattr(dockwidget, 'cbTP') and dockwidget.cbTP is not None:
dockwidget.cbTP.setDisabled(False)

if hasattr(dockwidget, 'pushButton_2') and dockwidget.pushButton_2 is not None:
dockwidget.pushButton_2.setDisabled(False)

for le in getattr(dockwidget, 'x_pclist', []):
if hasattr(le, 'setDisabled'):
le.setDisabled(False)

for attr in ('btn_normalize', 'btn_equalize', 'cbCompositionBasis'):
w = getattr(dockwidget, attr, None)
if w is not None and hasattr(w, 'setDisabled'):
w.setDisabled(False)

if hasattr(dockwidget, 'set_read_only'):
dockwidget.set_read_only(False)
Loading
Loading