mosamatic2 2.0.15__py3-none-any.whl → 2.0.17__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mosamatic2 might be problematic. Click here for more details.

Files changed (25) hide show
  1. mosamatic2/cli.py +6 -0
  2. mosamatic2/commands/calculatemaskstatistics.py +59 -0
  3. mosamatic2/commands/defaultdockerpipeline.py +12 -1
  4. mosamatic2/commands/liveranalysispipeline.py +61 -0
  5. mosamatic2/commands/totalsegmentator.py +11 -2
  6. mosamatic2/core/pipelines/__init__.py +2 -1
  7. mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py +2 -9
  8. mosamatic2/core/pipelines/liveranalysispipeline/__init__.py +0 -0
  9. mosamatic2/core/pipelines/liveranalysispipeline/liveranalysispipeline.py +48 -0
  10. mosamatic2/core/tasks/__init__.py +2 -1
  11. mosamatic2/core/tasks/calculatemaskstatisticstask/__init__.py +0 -0
  12. mosamatic2/core/tasks/calculatemaskstatisticstask/calculatemaskstatisticstask.py +104 -0
  13. mosamatic2/core/tasks/totalsegmentatortask/totalsegmentatortask.py +35 -10
  14. mosamatic2/ui/mainwindow.py +30 -0
  15. mosamatic2/ui/resources/VERSION +1 -1
  16. mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +1 -1
  17. mosamatic2/ui/widgets/panels/pipelines/liveranalysispipelinepanel.py +187 -0
  18. mosamatic2/ui/widgets/panels/tasks/calculatemaskstatisticstaskpanel.py +215 -0
  19. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/custominteractorstyle.py +18 -0
  20. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/sliceviewer.py +21 -3
  21. mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualization.py +36 -1
  22. {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/METADATA +4 -4
  23. {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/RECORD +25 -17
  24. {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/WHEEL +0 -0
  25. {mosamatic2-2.0.15.dist-info → mosamatic2-2.0.17.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,187 @@
1
+ import os
2
+
3
+ from PySide6.QtWidgets import (
4
+ QLineEdit,
5
+ QSpinBox,
6
+ QComboBox,
7
+ QCheckBox,
8
+ QHBoxLayout,
9
+ QVBoxLayout,
10
+ QFormLayout,
11
+ QPushButton,
12
+ QFileDialog,
13
+ QMessageBox,
14
+ )
15
+ from PySide6.QtCore import (
16
+ QThread,
17
+ Slot,
18
+ )
19
+
20
+ from mosamatic2.core.managers.logmanager import LogManager
21
+ from mosamatic2.ui.widgets.panels.pipelines.pipelinepanel import PipelinePanel
22
+ from mosamatic2.ui.settings import Settings
23
+ from mosamatic2.ui.utils import is_macos
24
+ from mosamatic2.ui.worker import Worker
25
+ from mosamatic2.core.pipelines import LiverAnalysisPipeline
26
+
27
+ LOG = LogManager()
28
+
29
+ PANEL_TITLE = 'LiverAnalysisPipeline'
30
+ PANEL_NAME = 'liveranalysispipelinepanel'
31
+
32
+
33
+ class LiverAnalysisPipelinePanel(PipelinePanel):
34
+ def __init__(self):
35
+ super(LiverAnalysisPipelinePanel, self).__init__()
36
+ self.set_title(PANEL_TITLE)
37
+ self._scans_dir_line_edit = None
38
+ self._scans_dir_select_button = None
39
+ self._output_dir_line_edit = None
40
+ self._output_dir_select_button = None
41
+ self._overwrite_checkbox = None
42
+ self._form_layout = None
43
+ self._run_pipeline_button = None
44
+ self._settings = None
45
+ self._task = None
46
+ self._worker = None
47
+ self._thread = None
48
+ self.init_layout()
49
+
50
+ def scans_dir_line_edit(self):
51
+ if not self._scans_dir_line_edit:
52
+ self._scans_dir_line_edit = QLineEdit()
53
+ self._scans_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/scans_dir'))
54
+ return self._scans_dir_line_edit
55
+
56
+ def scans_dir_select_button(self):
57
+ if not self._scans_dir_select_button:
58
+ self._scans_dir_select_button = QPushButton('Select')
59
+ self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
60
+ return self._scans_dir_select_button
61
+
62
+ def output_dir_line_edit(self):
63
+ if not self._output_dir_line_edit:
64
+ self._output_dir_line_edit = QLineEdit()
65
+ self._output_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/output_dir'))
66
+ return self._output_dir_line_edit
67
+
68
+ def output_dir_select_button(self):
69
+ if not self._output_dir_select_button:
70
+ self._output_dir_select_button = QPushButton('Select')
71
+ self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
72
+ return self._output_dir_select_button
73
+
74
+ def overwrite_checkbox(self):
75
+ if not self._overwrite_checkbox:
76
+ self._overwrite_checkbox = QCheckBox('')
77
+ self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
78
+ return self._overwrite_checkbox
79
+
80
+ def form_layout(self):
81
+ if not self._form_layout:
82
+ self._form_layout = QFormLayout()
83
+ if is_macos():
84
+ self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
85
+ return self._form_layout
86
+
87
+ def run_pipeline_button(self):
88
+ if not self._run_pipeline_button:
89
+ self._run_pipeline_button = QPushButton('Run pipeline')
90
+ self._run_pipeline_button.clicked.connect(self.handle_run_pipeline_button)
91
+ return self._run_pipeline_button
92
+
93
+ def settings(self):
94
+ if not self._settings:
95
+ self._settings = Settings()
96
+ return self._settings
97
+
98
+ def init_help_dialog(self):
99
+ self.help_dialog().set_text('Show some help information')
100
+
101
+ def init_layout(self):
102
+ scans_dir_layout = QHBoxLayout()
103
+ scans_dir_layout.addWidget(self.scans_dir_line_edit())
104
+ scans_dir_layout.addWidget(self.scans_dir_select_button())
105
+ output_dir_layout = QHBoxLayout()
106
+ output_dir_layout.addWidget(self.output_dir_line_edit())
107
+ output_dir_layout.addWidget(self.output_dir_select_button())
108
+ self.form_layout().addRow('Scans directory', scans_dir_layout)
109
+ self.form_layout().addRow('Output directory', output_dir_layout)
110
+ self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
111
+ layout = QVBoxLayout()
112
+ layout.addLayout(self.form_layout())
113
+ layout.addWidget(self.run_pipeline_button())
114
+ self.setLayout(layout)
115
+ self.setObjectName(PANEL_NAME)
116
+
117
+ def handle_scans_dir_select_button(self):
118
+ last_directory = self.settings().get('last_directory')
119
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
120
+ if directory:
121
+ self.scans_dir_line_edit().setText(directory)
122
+ self.settings().set('last_directory', directory)
123
+
124
+ def handle_output_dir_select_button(self):
125
+ last_directory = self.settings().get('last_directory')
126
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
127
+ if directory:
128
+ self.output_dir_line_edit().setText(directory)
129
+ self.settings().set('last_directory', directory)
130
+
131
+ def handle_run_pipeline_button(self):
132
+ errors = self.check_inputs_and_parameters()
133
+ if len(errors) > 0:
134
+ error_message = 'Following errors were encountered:\n'
135
+ for error in errors:
136
+ error_message += f' - {error}\n'
137
+ QMessageBox.information(self, 'Error', error_message)
138
+ else:
139
+ LOG.info('Running pipeline...')
140
+ self.run_pipeline_button().setEnabled(False)
141
+ self.save_inputs_and_parameters()
142
+ self._task = LiverAnalysisPipeline(
143
+ inputs={'scans': self.scans_dir_line_edit().text()},
144
+ params={'compressed': True},
145
+ output=self.output_dir_line_edit().text(),
146
+ overwrite=self.overwrite_checkbox().isChecked(),
147
+ )
148
+ self._worker = Worker(self._task)
149
+ self._thread = QThread()
150
+ self._worker.moveToThread(self._thread)
151
+ self._thread.started.connect(self._worker.run)
152
+ self._worker.progress.connect(self.handle_progress)
153
+ self._worker.status.connect(self.handle_status)
154
+ self._worker.finished.connect(self.handle_finished)
155
+ self._worker.finished.connect(self._thread.quit)
156
+ self._worker.finished.connect(self._worker.deleteLater)
157
+ self._thread.finished.connect(self._thread.deleteLater)
158
+ self._thread.start()
159
+
160
+ @Slot(int)
161
+ def handle_progress(self, progress):
162
+ LOG.info(f'Progress: {progress} / 100%')
163
+
164
+ @Slot(str)
165
+ def handle_status(self, status):
166
+ LOG.info(f'Status: {status}')
167
+
168
+ @Slot()
169
+ def handle_finished(self):
170
+ self.run_pipeline_button().setEnabled(True)
171
+
172
+ def check_inputs_and_parameters(self):
173
+ errors = []
174
+ if self.scans_dir_line_edit().text() == '':
175
+ errors.append('Empty scans directory path')
176
+ elif not os.path.isdir(self.scans_dir_line_edit().text()):
177
+ errors.append('Scans directory does not exist')
178
+ if self.output_dir_line_edit().text() == '':
179
+ errors.append('Empty output directory path')
180
+ elif os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
181
+ errors.append('Output directory exists but overwrite=False. Please remove output directory first')
182
+ return errors
183
+
184
+ def save_inputs_and_parameters(self):
185
+ self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
186
+ self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
187
+ self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
@@ -0,0 +1,215 @@
1
+ import os
2
+
3
+ from PySide6.QtWidgets import (
4
+ QLineEdit,
5
+ QCheckBox,
6
+ QSpinBox,
7
+ QHBoxLayout,
8
+ QVBoxLayout,
9
+ QFormLayout,
10
+ QPushButton,
11
+ QFileDialog,
12
+ QMessageBox,
13
+ )
14
+ from PySide6.QtCore import (
15
+ QThread,
16
+ Slot,
17
+ )
18
+
19
+ from mosamatic2.core.managers.logmanager import LogManager
20
+ from mosamatic2.ui.widgets.panels.tasks.taskpanel import TaskPanel
21
+ from mosamatic2.ui.settings import Settings
22
+ from mosamatic2.ui.utils import is_macos
23
+ from mosamatic2.ui.worker import Worker
24
+ from mosamatic2.core.tasks import CalculateMaskStatisticsTask
25
+
26
+ LOG = LogManager()
27
+
28
+ PANEL_TITLE = 'CalculateMaskStatisticsTask'
29
+ PANEL_NAME = 'calculatemaskstatisticstaskpanel'
30
+
31
+
32
+ class CalculateMaskStatisticsTaskPanel(TaskPanel):
33
+ def __init__(self):
34
+ super(CalculateMaskStatisticsTaskPanel, self).__init__()
35
+ self.set_title(PANEL_TITLE)
36
+ self._scans_dir_line_edit = None
37
+ self._scans_dir_select_button = None
38
+ self._masks_dir_line_edit = None
39
+ self._masks_dir_select_button = None
40
+ self._output_dir_line_edit = None
41
+ self._output_dir_select_button = None
42
+ self._overwrite_checkbox = None
43
+ self._form_layout = None
44
+ self._run_task_button = None
45
+ self._settings = None
46
+ self._task = None
47
+ self._worker = None
48
+ self._thread = None
49
+ self.init_layout()
50
+
51
+ def scans_dir_line_edit(self):
52
+ if not self._scans_dir_line_edit:
53
+ self._scans_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/scans_dir'))
54
+ return self._scans_dir_line_edit
55
+
56
+ def scans_dir_select_button(self):
57
+ if not self._scans_dir_select_button:
58
+ self._scans_dir_select_button = QPushButton('Select')
59
+ self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
60
+ return self._scans_dir_select_button
61
+
62
+ def masks_dir_line_edit(self):
63
+ if not self._masks_dir_line_edit:
64
+ self._masks_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/masks_dir'))
65
+ return self._masks_dir_line_edit
66
+
67
+ def masks_dir_select_button(self):
68
+ if not self._masks_dir_select_button:
69
+ self._masks_dir_select_button = QPushButton('Select')
70
+ self._masks_dir_select_button.clicked.connect(self.handle_masks_dir_select_button)
71
+ return self._masks_dir_select_button
72
+
73
+ def output_dir_line_edit(self):
74
+ if not self._output_dir_line_edit:
75
+ self._output_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/output_dir'))
76
+ return self._output_dir_line_edit
77
+
78
+ def output_dir_select_button(self):
79
+ if not self._output_dir_select_button:
80
+ self._output_dir_select_button = QPushButton('Select')
81
+ self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
82
+ return self._output_dir_select_button
83
+
84
+ def overwrite_checkbox(self):
85
+ if not self._overwrite_checkbox:
86
+ self._overwrite_checkbox = QCheckBox('')
87
+ self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
88
+ return self._overwrite_checkbox
89
+
90
+ def form_layout(self):
91
+ if not self._form_layout:
92
+ self._form_layout = QFormLayout()
93
+ if is_macos():
94
+ self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
95
+ return self._form_layout
96
+
97
+ def run_task_button(self):
98
+ if not self._run_task_button:
99
+ self._run_task_button = QPushButton('Run task')
100
+ self._run_task_button.clicked.connect(self.handle_run_task_button)
101
+ return self._run_task_button
102
+
103
+ def settings(self):
104
+ if not self._settings:
105
+ self._settings = Settings()
106
+ return self._settings
107
+
108
+ def init_layout(self):
109
+ scans_dir_layout = QHBoxLayout()
110
+ scans_dir_layout.addWidget(self.scans_dir_line_edit())
111
+ scans_dir_layout.addWidget(self.scans_dir_select_button())
112
+ masks_dir_layout = QHBoxLayout()
113
+ masks_dir_layout.addWidget(self.masks_dir_line_edit())
114
+ masks_dir_layout.addWidget(self.masks_dir_select_button())
115
+ output_dir_layout = QHBoxLayout()
116
+ output_dir_layout.addWidget(self.output_dir_line_edit())
117
+ output_dir_layout.addWidget(self.output_dir_select_button())
118
+ self.form_layout().addRow('Scans directory', scans_dir_layout)
119
+ self.form_layout().addRow('Masks directory', masks_dir_layout)
120
+ self.form_layout().addRow('Output directory', output_dir_layout)
121
+ self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
122
+ layout = QVBoxLayout()
123
+ layout.addLayout(self.form_layout())
124
+ layout.addWidget(self.run_task_button())
125
+ self.setLayout(layout)
126
+ self.setObjectName(PANEL_NAME)
127
+
128
+ def handle_scans_dir_select_button(self):
129
+ last_directory = self.settings().get('last_directory')
130
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
131
+ if directory:
132
+ self.scans_dir_line_edit().setText(directory)
133
+ self.settings().set('last_directory', directory)
134
+
135
+ def handle_masks_dir_select_button(self):
136
+ last_directory = self.settings().get('last_directory')
137
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
138
+ if directory:
139
+ self.masks_dir_line_edit().setText(directory)
140
+ self.settings().set('last_directory', directory)
141
+
142
+ def handle_output_dir_select_button(self):
143
+ last_directory = self.settings().get('last_directory')
144
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
145
+ if directory:
146
+ self.output_dir_line_edit().setText(directory)
147
+ self.settings().set('last_directory', directory)
148
+
149
+ def handle_run_task_button(self):
150
+ errors = self.check_inputs_and_parameters()
151
+ if len(errors) > 0:
152
+ error_message = 'Following errors were encountered:\n'
153
+ for error in errors:
154
+ error_message += f' - {error}\n'
155
+ QMessageBox.information(self, 'Error', error_message)
156
+ else:
157
+ LOG.info('Running task...')
158
+ self.run_task_button().setEnabled(False)
159
+ self.save_inputs_and_parameters()
160
+ self._task = CalculateMaskStatisticsTask(
161
+ inputs={
162
+ 'scans': self.scans_dir_line_edit().text(),
163
+ 'masks': self.masks_dir_line_edit().text(),
164
+ },
165
+ params=None,
166
+ output=self.output_dir_line_edit().text(),
167
+ overwrite=self.overwrite_checkbox().isChecked(),
168
+ )
169
+ self._worker = Worker(self._task)
170
+ self._thread = QThread()
171
+ self._worker.moveToThread(self._thread)
172
+ self._thread.started.connect(self._worker.run)
173
+ self._worker.progress.connect(self.handle_progress)
174
+ self._worker.status.connect(self.handle_status)
175
+ self._worker.finished.connect(self.handle_finished)
176
+ self._worker.finished.connect(self._thread.quit)
177
+ self._worker.finished.connect(self._worker.deleteLater)
178
+ self._thread.finished.connect(self._thread.deleteLater)
179
+ self._thread.start()
180
+
181
+ @Slot(int)
182
+ def handle_progress(self, progress):
183
+ LOG.info(f'Progress: {progress} / 100%')
184
+
185
+ @Slot(str)
186
+ def handle_status(self, status):
187
+ LOG.info(f'Status: {status}')
188
+
189
+ @Slot()
190
+ def handle_finished(self):
191
+ self.run_task_button().setEnabled(True)
192
+
193
+ # HELPERS
194
+
195
+ def check_inputs_and_parameters(self):
196
+ errors = []
197
+ if self.scans_dir_line_edit().text() == '':
198
+ errors.append('Empty scans directory path')
199
+ if not os.path.isdir(self.scans_dir_line_edit().text()):
200
+ errors.append('Scans directory does not exist')
201
+ if self.masks_dir_line_edit().text() == '':
202
+ errors.append('Empty masks directory path')
203
+ if not os.path.isdir(self.masks_dir_line_edit().text()):
204
+ errors.append('Masks directory does not exist')
205
+ if self.output_dir_line_edit().text() == '':
206
+ errors.append('Empty output directory path')
207
+ if os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
208
+ errors.append('Output directory exists but overwrite=False. Please remove output directory first')
209
+ return errors
210
+
211
+ def save_inputs_and_parameters(self):
212
+ self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
213
+ self.settings().set(f'{PANEL_NAME}/masks_dir', self.masks_dir_line_edit().text())
214
+ self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
215
+ self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
@@ -14,6 +14,8 @@ class CustomInteractorStyle(vtk.vtkInteractorStyleImage):
14
14
  self.status_actor = status_actor
15
15
  self.slice_obj = slice_obj
16
16
  self.orientation = orientation
17
+ self._color_window = 0
18
+ self._color_level = 0
17
19
 
18
20
  xmin, xmax, ymin, ymax, zmin, zmax = image_data.GetExtent()
19
21
  if orientation == "axial":
@@ -29,6 +31,22 @@ class CustomInteractorStyle(vtk.vtkInteractorStyleImage):
29
31
  self.slice_mapper.SetSliceNumber(self.slice)
30
32
  self.update_status_message()
31
33
 
34
+ def color_window(self):
35
+ return self._color_window
36
+
37
+ def set_color_window(self, color_window):
38
+ self._color_window = color_window
39
+ self.slice_obj.GetProperty().SetColorWindow(self._color_window)
40
+ self.GetInteractor().GetRenderWindow().Render()
41
+
42
+ def color_level(self):
43
+ return self._color_level
44
+
45
+ def set_color_level(self, color_level):
46
+ self._color_level = color_level
47
+ self.slice_obj.GetProperty().SetColorLevel(self._color_level)
48
+ self.GetInteractor().GetRenderWindow().Render()
49
+
32
50
  def update_status_message(self):
33
51
  window = int(self.slice_obj.GetProperty().GetColorWindow())
34
52
  level = int(self.slice_obj.GetProperty().GetColorLevel())
@@ -11,10 +11,12 @@ IMAGE_SHIFT_SCALE = -1000
11
11
 
12
12
 
13
13
  class SliceViewer(QWidget):
14
- def __init__(self):
14
+ def __init__(self, color_window=COLOR_WINDOW, color_level=COLOR_LEVEL):
15
15
  super(SliceViewer, self).__init__()
16
16
  self._nifti_file_or_dicom_dir = None
17
17
  self._view_orientation = 'axial'
18
+ self._color_window = color_window
19
+ self._color_level = color_level
18
20
  self._vtk_widget = QVTKRenderWindowInteractor(self)
19
21
  self._render_window = self._vtk_widget.GetRenderWindow()
20
22
  self._interactor = self._render_window.GetInteractor()
@@ -27,6 +29,22 @@ class SliceViewer(QWidget):
27
29
  self._render_window.AddRenderer(self._default_renderer)
28
30
  self._render_window.Render()
29
31
 
32
+ def color_window(self):
33
+ return self._color_window
34
+
35
+ def set_color_window(self, color_window):
36
+ self._color_window = color_window
37
+ if self._interactor_style:
38
+ self._interactor_style.set_color_window(self._color_window)
39
+
40
+ def color_level(self):
41
+ return self._color_level
42
+
43
+ def set_color_level(self, color_level):
44
+ self._color_level = color_level
45
+ if self._interactor_style:
46
+ self._interactor_style.set_color_level(self._color_level)
47
+
30
48
  def nifti_file_or_dicom_dir(self):
31
49
  return self._nifti_file_or_dicom_dir
32
50
 
@@ -75,8 +93,8 @@ class SliceViewer(QWidget):
75
93
  slice_mapper.SetOrientationToZ() # axial orientation
76
94
  slice_mapper.SetSliceNumber(axial_index)
77
95
  slice = vtk.vtkImageSlice()
78
- slice.GetProperty().SetColorWindow(400)
79
- slice.GetProperty().SetColorLevel(40)
96
+ slice.GetProperty().SetColorWindow(self.color_window())
97
+ slice.GetProperty().SetColorLevel(self.color_level())
80
98
  slice.SetMapper(slice_mapper)
81
99
  slice_text_actor = self.create_text_actor("", 0.01, 0.01, 12, align_bottom=True, normalized=True)
82
100
  usage_text_actor = self.create_text_actor(
@@ -1,5 +1,7 @@
1
1
  from PySide6.QtWidgets import (
2
2
  QLineEdit,
3
+ QLabel,
4
+ QSpinBox,
3
5
  QHBoxLayout,
4
6
  QVBoxLayout,
5
7
  QFormLayout,
@@ -24,6 +26,9 @@ class SliceVisualization(Visualization):
24
26
  self._image_line_edit = None
25
27
  self._image_select_button = None
26
28
  self._image_dir_select_button = None
29
+ self._color_window_spinbox = None
30
+ self._color_level_spinbox = None
31
+ self._color_window_and_level_reset_button = None
27
32
  self._load_image_button = None
28
33
  self._slice_viewer = None
29
34
  self._form_layout = None
@@ -47,6 +52,22 @@ class SliceVisualization(Visualization):
47
52
  self._image_dir_select_button.clicked.connect(self.handle_image_dir_select_button)
48
53
  return self._image_dir_select_button
49
54
 
55
+ def color_window_spinbox(self):
56
+ if not self._color_window_spinbox:
57
+ self._color_window_spinbox = QSpinBox(self, minimum=0, maximum=1024, value=400)
58
+ return self._color_window_spinbox
59
+
60
+ def color_level_spinbox(self):
61
+ if not self._color_level_spinbox:
62
+ self._color_level_spinbox = QSpinBox(self, minimum=0, maximum=100, value=40)
63
+ return self._color_level_spinbox
64
+
65
+ def color_window_and_level_reset_button(self):
66
+ if not self._color_window_and_level_reset_button:
67
+ self._color_window_and_level_reset_button = QPushButton('Reset')
68
+ self._color_window_and_level_reset_button.clicked.connect(self.handle_color_window_and_level_reset_button)
69
+ return self._color_window_and_level_reset_button
70
+
50
71
  def load_image_button(self):
51
72
  if not self._load_image_button:
52
73
  self._load_image_button = QPushButton('Load')
@@ -55,7 +76,10 @@ class SliceVisualization(Visualization):
55
76
 
56
77
  def slice_viewer(self):
57
78
  if not self._slice_viewer:
58
- self._slice_viewer = SliceViewer()
79
+ self._slice_viewer = SliceViewer(
80
+ color_window=self.color_window_spinbox().value(),
81
+ color_level=self.color_level_spinbox().value(),
82
+ )
59
83
  return self._slice_viewer
60
84
 
61
85
  def form_layout(self):
@@ -75,7 +99,14 @@ class SliceVisualization(Visualization):
75
99
  image_layout.addWidget(self.image_line_edit())
76
100
  image_layout.addWidget(self.image_select_button())
77
101
  image_layout.addWidget(self.image_dir_select_button())
102
+ color_window_and_level_layout = QHBoxLayout()
103
+ color_window_and_level_layout.addWidget(QLabel('Color window'))
104
+ color_window_and_level_layout.addWidget(self.color_window_spinbox())
105
+ color_window_and_level_layout.addWidget(QLabel('Color level'))
106
+ color_window_and_level_layout.addWidget(self.color_level_spinbox())
107
+ color_window_and_level_layout.addWidget(self.color_window_and_level_reset_button())
78
108
  self.form_layout().addRow('NIFTI file or DICOM directory', image_layout)
109
+ self.form_layout().addRow('', color_window_and_level_layout)
79
110
  layout = QVBoxLayout()
80
111
  layout.addLayout(self.form_layout())
81
112
  layout.addWidget(self.load_image_button())
@@ -97,6 +128,10 @@ class SliceVisualization(Visualization):
97
128
  self.image_line_edit().setText(dir_path)
98
129
  self.settings().set('last_directory', dir_path)
99
130
 
131
+ def handle_color_window_and_level_reset_button(self):
132
+ self.slice_viewer().set_color_window(self.color_window_spinbox().value())
133
+ self.slice_viewer().set_color_level(self.color_level_spinbox().value())
134
+
100
135
  def handle_load_image_button(self):
101
136
  self.slice_viewer().set_nifti_file_or_dicom_dir(self.image_line_edit().text())
102
137
  # self.slice_viewer().set_view_orientation('axial')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mosamatic2
3
- Version: 2.0.15
3
+ Version: 2.0.17
4
4
  Summary:
5
5
  Author: Ralph Brecheisen
6
6
  Author-email: r.brecheisen@maastrichtuniversity.nl
@@ -9,9 +9,9 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Requires-Dist: antspyx (>=0.5.4)
11
11
  Requires-Dist: dicom2nifti (>=2.6.2)
12
- Requires-Dist: docker
12
+ Requires-Dist: docker (>=7.1.0)
13
13
  Requires-Dist: flask (>=3.1.2)
14
- Requires-Dist: moosez
14
+ Requires-Dist: moosez (>=3.0.29)
15
15
  Requires-Dist: nibabel (>=5.3.2)
16
16
  Requires-Dist: numpy (>=1.26.4)
17
17
  Requires-Dist: openpyxl (>=3.1.5)
@@ -23,7 +23,7 @@ Requires-Dist: pyqtgraph (>=0.13.7)
23
23
  Requires-Dist: pyside6-essentials (>=6.9)
24
24
  Requires-Dist: python-gdcm (>=3.0.26)
25
25
  Requires-Dist: scipy (>=1.15.3)
26
- Requires-Dist: slicer
26
+ Requires-Dist: slicer (>=0.0.8)
27
27
  Requires-Dist: tensorboard (==2.15.2)
28
28
  Requires-Dist: tensorboard-data-server (==0.7.2)
29
29
  Requires-Dist: tensorflow (==2.15.*) ; platform_system == "Linux"