|
| 1 | +from pathlib import Path |
| 2 | + |
| 3 | +from trame.app import TrameApp |
| 4 | +from trame.app.file_upload import ClientFile |
| 5 | +from trame.ui.vuetify3 import SinglePageLayout |
| 6 | + |
| 7 | +from trame.widgets import client, html, vuetify3 |
| 8 | + |
| 9 | +DICOM_UTILS_SCRIPT = Path(__file__).with_name("dicom_utils.js") |
| 10 | + |
| 11 | + |
| 12 | +class DicomToNiftiConvertor(TrameApp): |
| 13 | + def __init__(self, server=None): |
| 14 | + super().__init__(server) |
| 15 | + |
| 16 | + self.state.dicom_file_set = None |
| 17 | + self.state.feedback_message = "" |
| 18 | + self.state.is_error = False |
| 19 | + self.state.is_success = False |
| 20 | + |
| 21 | + # Mark your state as client_only! |
| 22 | + self.state.client_only("dicom_file_set") |
| 23 | + |
| 24 | + client.register_external_script( |
| 25 | + name="generate_nii_from_dcm", |
| 26 | + script_file_path=DICOM_UTILS_SCRIPT, |
| 27 | + function_names=["generateNIFTIFromDicomFileSet"], |
| 28 | + ) |
| 29 | + |
| 30 | + self._build_ui() |
| 31 | + |
| 32 | + def _build_ui(self): |
| 33 | + with SinglePageLayout(self.server) as self.ui: |
| 34 | + self.ui.title.set_text("Explicit Pipeline Trigger") |
| 35 | + |
| 36 | + with ( |
| 37 | + self.ui.content, |
| 38 | + vuetify3.VContainer(fluid=True), |
| 39 | + vuetify3.VRow(), |
| 40 | + vuetify3.VCol(cols=3), |
| 41 | + ): |
| 42 | + with vuetify3.VCard(flat=True): |
| 43 | + with ( |
| 44 | + vuetify3.VSnackbar( |
| 45 | + model_value=("is_error || is_success",), |
| 46 | + color=("is_error ? 'error' : 'success'",), |
| 47 | + location="top left", |
| 48 | + close_on_content_click=True, |
| 49 | + timeout=6000, |
| 50 | + update_modelValue="is_error = $event; is_success = $event;", |
| 51 | + ), |
| 52 | + html.Div(style="align-items: center;"), |
| 53 | + ): |
| 54 | + vuetify3.VIcon("mdi-information", style="margin-right: 12px") |
| 55 | + html.Span("{{ feedback_message }}") |
| 56 | + |
| 57 | + vuetify3.VFileInput( |
| 58 | + v_model="dicom_file_set", |
| 59 | + multiple=True, |
| 60 | + label="DICOM files", |
| 61 | + hint="Input here a set of DICOM files that will be converted to a single .nii file", |
| 62 | + persistent_hint=True, |
| 63 | + ) |
| 64 | + |
| 65 | + with client.Handler( |
| 66 | + function="generate_nii_from_dcm", |
| 67 | + inputs=("{ output_file_name: 'my_converted_file.nii' }",), |
| 68 | + # success event for the logic happy path |
| 69 | + success=(self.save_nifti_file, "[$event]"), |
| 70 | + # failure event for the logic error path (e.g. invalid data) |
| 71 | + failure=(self.handle_nifti_convertion_error, "[$event]"), |
| 72 | + # error event for any exception raised during logic execution |
| 73 | + error=(self.handle_nifti_convertion_error, "[$event]"), |
| 74 | + ) as client_handler: |
| 75 | + # trigger the preprocessing on our dicom_file_set state key |
| 76 | + vuetify3.VBtn( |
| 77 | + "Convert DICOMs to NIFTI file", |
| 78 | + color="primary", |
| 79 | + click=client_handler.run("dicom_file_set"), |
| 80 | + ) |
| 81 | + |
| 82 | + def save_nifti_file(self, raw_nifti_file): |
| 83 | + nifti_file = ClientFile(raw_nifti_file[0]) |
| 84 | + print("got a NIFTI file") |
| 85 | + |
| 86 | + print(nifti_file.info) |
| 87 | + print(nifti_file.name) |
| 88 | + |
| 89 | + self.state.feedback_message = "NIFTI file successfully generated" |
| 90 | + self.state.is_success = True |
| 91 | + self.state.is_error = False |
| 92 | + |
| 93 | + file_path = (Path("outputs") / nifti_file.name).resolve() |
| 94 | + file_path.parent.mkdir(parents=True, exist_ok=True) |
| 95 | + |
| 96 | + if file_path.exists(): |
| 97 | + file_path.unlink() |
| 98 | + |
| 99 | + with file_path.open(mode="xb") as file: |
| 100 | + file.write(nifti_file.content) |
| 101 | + |
| 102 | + def handle_nifti_convertion_error(self, error): |
| 103 | + print(error) |
| 104 | + self.state.feedback_message = error |
| 105 | + self.state.is_error = True |
| 106 | + |
| 107 | + |
| 108 | +def main(**kwargs): |
| 109 | + app = DicomToNiftiConvertor() |
| 110 | + app.server.start(**kwargs) |
| 111 | + |
| 112 | + |
| 113 | +if __name__ == "__main__": |
| 114 | + main() |
0 commit comments