134 lines
5.3 KiB
Python
134 lines
5.3 KiB
Python
from PySide6.QtWidgets import (
|
|
QMessageBox, QGraphicsRectItem, QGraphicsTextItem,
|
|
QPushButton, QGraphicsProxyWidget, QTableWidget,
|
|
QTableWidgetItem, QDialog, QVBoxLayout
|
|
)
|
|
from PySide6.QtGui import QBrush, QColor
|
|
from pathlib import Path
|
|
import napari
|
|
import json
|
|
from .port_item import PortItem # Import PortItem with type support
|
|
|
|
class NodeWidget(QGraphicsRectItem):
|
|
def __init__(self, title: str, inputs: list[tuple[str, str]], outputs: list[tuple[str, str]]):
|
|
"""
|
|
inputs/outputs: list of (label, port_type)
|
|
Example:
|
|
inputs=[("Image", "Image"), ("Mask", "Mask")]
|
|
outputs=[("Features", "Features")]
|
|
"""
|
|
super().__init__(0, 0, 160, 90)
|
|
self.setBrush(QBrush(QColor("#e0e0e0")))
|
|
self.title = QGraphicsTextItem(title, self)
|
|
self.title.setPos(10, 5)
|
|
|
|
self.status_text = QGraphicsTextItem("", self)
|
|
self.status_text.setPos(10, 40)
|
|
|
|
# Create input ports
|
|
self.input_ports = []
|
|
y_offset = 20
|
|
for label, port_type in inputs:
|
|
port = PortItem(label, port_type, is_input=True, parent=self)
|
|
port.setPos(0, y_offset)
|
|
self.input_ports.append(port)
|
|
y_offset += 20
|
|
|
|
# Create output ports
|
|
self.output_ports = []
|
|
y_offset = 20
|
|
for label, port_type in outputs:
|
|
port = PortItem(label, port_type, is_input=False, parent=self)
|
|
port.setPos(150, y_offset)
|
|
self.output_ports.append(port)
|
|
y_offset += 20
|
|
|
|
# Output Display Button
|
|
self.btn_view = QPushButton("View Output")
|
|
proxy = QGraphicsProxyWidget(self)
|
|
proxy.setWidget(self.btn_view)
|
|
proxy.setPos(10, 60)
|
|
self.btn_view.clicked.connect(self.view_output)
|
|
|
|
def set_status(self, status: str):
|
|
# Change node color and status text based on execution state
|
|
if status == "running":
|
|
self.setBrush(QBrush(QColor("yellow")))
|
|
self.status_text.setPlainText("⏳ Running")
|
|
elif status == "success":
|
|
self.setBrush(QBrush(QColor("lightgreen")))
|
|
self.status_text.setPlainText("✔ Success")
|
|
elif status == "failed":
|
|
self.setBrush(QBrush(QColor("red")))
|
|
self.status_text.setPlainText("❌ Failed")
|
|
else:
|
|
self.setBrush(QBrush(QColor("#e0e0e0")))
|
|
self.status_text.setPlainText("")
|
|
|
|
def view_output(self):
|
|
node_type = self.title.toPlainText()
|
|
|
|
# Image Reader Or Image Filter → Processed Image
|
|
if node_type in ["Image Reader", "Image Filter"]:
|
|
file_path = Path("artifacts/processed_image.nii.gz")
|
|
if file_path.exists():
|
|
viewer = napari.Viewer()
|
|
viewer.open(str(file_path))
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", f"No output image for {node_type}")
|
|
|
|
# Mask Reader → Mask
|
|
elif node_type == "Mask Reader":
|
|
file_path = Path("artifacts/mask.nii.gz")
|
|
if file_path.exists():
|
|
viewer = napari.Viewer()
|
|
viewer.open(str(file_path))
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", "No mask file found")
|
|
|
|
# Image Registration → Registered image
|
|
elif node_type == "Image Registration":
|
|
file_path = Path("artifacts/registered_image.nii.gz")
|
|
if file_path.exists():
|
|
viewer = napari.Viewer()
|
|
viewer.open(str(file_path))
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", "No registered image found")
|
|
|
|
# Image Fusion → Composite Image
|
|
elif node_type == "Image Fusion":
|
|
file_path = Path("artifacts/fused_image.nii.gz")
|
|
if file_path.exists():
|
|
viewer = napari.Viewer()
|
|
viewer.open(str(file_path))
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", "No fused image found")
|
|
|
|
# Image Extraction Or PySERA Extract → Features table
|
|
elif node_type in ["Image Extraction", "PySERA Extract"]:
|
|
file_path = Path("artifacts/features.json")
|
|
if file_path.exists():
|
|
features = json.loads(file_path.read_text())
|
|
dialog = QDialog()
|
|
dialog.setWindowTitle("Radiomics Features")
|
|
layout = QVBoxLayout(dialog)
|
|
table = QTableWidget()
|
|
table.setRowCount(len(features))
|
|
table.setColumnCount(2)
|
|
table.setHorizontalHeaderLabels(["Feature", "Value"])
|
|
for i, (key, value) in enumerate(features.items()):
|
|
table.setItem(i, 0, QTableWidgetItem(key))
|
|
table.setItem(i, 1, QTableWidgetItem(str(value)))
|
|
layout.addWidget(table)
|
|
dialog.exec()
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", "No features.json found")
|
|
|
|
# Writer → Final Output File
|
|
elif node_type in ["Writer", "Image Writer"]:
|
|
file_path = Path("artifacts/output.nii.gz")
|
|
if file_path.exists():
|
|
QMessageBox.information(None, "Writer Output", f"Output file saved: {file_path}")
|
|
else:
|
|
QMessageBox.warning(None, "Output not found", "No output file found")
|