forked from Slicer/sliceri18n
-
Notifications
You must be signed in to change notification settings - Fork 0
/
testfile.py
729 lines (608 loc) · 28.8 KB
/
testfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
import copy
import logging
import os
import qt
import slicer
from slicer.util import VTKObservationMixin
from slicer.util import settingsValue, toBool
import DICOMLib
# underscrore function to mark strings as translatable
def _(text):
return slicer.app.translate(__file__, text)
#########################################################
#
#
comment = """
DICOMWidgets are helper classes to build an interface
to manage DICOM data in the context of slicer.
This code is slicer-specific and relies on the slicer python module
for elements like slicer.dicomDatabase and slicer.mrmlScene
"""
#
#########################################################
class SlicerDICOMBrowser(VTKObservationMixin, qt.QWidget):
"""Implement the Qt window showing details and possible
operations to perform on the selected dicom list item.
This is a helper used in the DICOMWidget class.
"""
closed = qt.Signal() # Invoked when the dicom widget is closed using the close method
def __init__(self, dicomBrowser=None, parent="mainWindow"):
VTKObservationMixin.__init__(self)
qt.QWidget.__init__(self, slicer.util.mainWindow() if parent == "mainWindow" else parent)
self.pluginInstances = {}
self.fileLists = []
self.extensionCheckPending = False
self.settings = qt.QSettings()
self.dicomBrowser = dicomBrowser if dicomBrowser is not None else slicer.app.createDICOMBrowserForMainDatabase()
self.browserPersistent = settingsValue(_('DICOM/BrowserPersistent'), False, converter=toBool)
self.advancedView = settingsValue(_('DICOM/advancedView'), 0, converter=int)
self.horizontalTables = settingsValue(_('DICOM/horizontalTables'), 0, converter=int)
self.setup()
self.dicomBrowser.connect('directoryImported()', self.onDirectoryImported)
self.dicomBrowser.connect('sendRequested(QStringList)', self.onSend)
# Load when double-clicked on an item in the browser
self.dicomBrowser.dicomTableManager().connect('patientsDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked)
self.dicomBrowser.dicomTableManager().connect('studiesDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked)
self.dicomBrowser.dicomTableManager().connect('seriesDoubleClicked(QModelIndex)', self.patientStudySeriesDoubleClicked)
def open(self):
self.show()
def close(self):
self.hide()
self.closed.emit()
def onSend(self, fileList):
if len(fileList):
sendDialog = DICOMLib.DICOMSendDialog(fileList, self)
def setup(self, showPreview=False):
"""
main window is a frame with widgets from the app
widget repacked into it along with slicer-specific
extra widgets
"""
self.setWindowTitle(_('DICOM Browser'))
self.setLayout(qt.QVBoxLayout())
self.dicomBrowser.databaseDirectorySelectorVisible = False
self.dicomBrowser.toolbarVisible = False
self.dicomBrowser.sendActionVisible = True
self.dicomBrowser.databaseDirectorySettingsKey = slicer.dicomDatabaseDirectorySettingsKey
self.dicomBrowser.dicomTableManager().dynamicTableLayout = False
horizontal = self.settings.setValue(_('DICOM/horizontalTables'), 0)
self.dicomBrowser.dicomTableManager().tableOrientation = qt.Qt.Horizontal if horizontal else qt.Qt.Vertical
self.layout().addWidget(self.dicomBrowser)
self.userFrame = qt.QWidget()
self.preview = qt.QWidget()
#
# preview related column
#
self.previewLayout = qt.QVBoxLayout()
if showPreview:
self.previewLayout.addWidget(self.preview)
else:
self.preview.hide()
#
# action related column (interacting with slicer)
#
self.loadableTableFrame = qt.QWidget()
self.loadableTableFrame.setMaximumHeight(200)
self.loadableTableLayout = qt.QVBoxLayout(self.loadableTableFrame)
self.layout().addWidget(self.loadableTableFrame)
self.loadableTableLayout.addWidget(self.userFrame)
self.userFrame.hide()
self.loadableTable = DICOMLoadableTable(self.userFrame)
self.loadableTable.itemChanged.connect(self.onLoadableTableItemChanged)
#
# button row for action column
#
self.actionButtonsFrame = qt.QWidget()
self.actionButtonsFrame.setMaximumHeight(40)
self.actionButtonsFrame.objectName = _('ActionButtonsFrame')
self.layout().addWidget(self.actionButtonsFrame)
self.actionButtonLayout = qt.QHBoxLayout()
self.actionButtonsFrame.setLayout(self.actionButtonLayout)
self.uncheckAllButton = qt.QPushButton(_('Uncheck All'))
self.actionButtonLayout.addWidget(self.uncheckAllButton)
self.uncheckAllButton.connect('clicked()', self.uncheckAllLoadables)
self.actionButtonLayout.addStretch(0.05)
self.examineButton = qt.QPushButton(_('Examine'))
self.examineButton.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
self.actionButtonLayout.addWidget(self.examineButton)
self.examineButton.enabled = False
self.examineButton.connect('clicked()', self.examineForLoading)
self.loadButton = qt.QPushButton(_('Load'))
self.loadButton.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
self.loadButton.toolTip = _('Load selected items into the scene')
self.actionButtonLayout.addWidget(self.loadButton)
self.loadButton.connect('clicked()', self.loadCheckedLoadables)
self.actionButtonLayout.addStretch(0.05)
self.advancedViewButton = qt.QCheckBox(_('Advanced'))
self.advancedViewButton.objectName = _('AdvancedViewCheckBox')
self.actionButtonLayout.addWidget(self.advancedViewButton)
self.advancedViewButton.checked = self.advancedView
self.advancedViewButton.toggled.connect(self.onAdvancedViewButton)
if self.advancedView:
self.loadableTableFrame.visible = True
else:
self.loadableTableFrame.visible = False
self.examineButton.visible = False
self.uncheckAllButton.visible = False
#
# Series selection
#
self.dicomBrowser.dicomTableManager().connect('seriesSelectionChanged(QStringList)', self.onSeriesSelected)
#
# Loadable table widget (advanced)
# DICOM Plugins selection widget is moved to module panel
#
self.loadableTableLayout.addWidget(self.loadableTable)
self.updateButtonStates()
def updateButtonStates(self):
if self.advancedView:
#self.loadButton.enabled = loadEnabled = loadEnabled or loadablesByPlugin[plugin] != []
loadablesChecked = self.loadableTable.getNumberOfCheckedItems() > 0
self.loadButton.enabled = loadablesChecked
self.examineButton.enabled = len(self.fileLists) != 0
self.uncheckAllButton.enabled = loadablesChecked
else:
#seriesSelected = self.dicomBrowser.dicomTableManager().seriesTable().tableView().selectedIndexes()
self.loadButton.enabled = self.fileLists
def onDirectoryImported(self):
"""The dicom browser will emit multiple directoryImported
signals during the same operation, so we collapse them
into a single check for compatible extensions."""
if not hasattr(slicer.app, 'extensionsManagerModel'):
# Slicer may not be built with extensions manager support
return
if not self.extensionCheckPending:
self.extensionCheckPending = True
def timerCallback():
# Prompting for extension may be undesirable in custom applications.
# DICOM/PromptForExtensions key can be used to disable this feature.
promptForExtensionsEnabled = settingsValue(_('DICOM/PromptForExtensions'), True, converter=toBool)
if promptForExtensionsEnabled:
self.promptForExtensions()
self.extensionCheckPending = False
qt.QTimer.singleShot(0, timerCallback)
def promptForExtensions(self):
extensionsToOffer = self.checkForExtensions()
if len(extensionsToOffer) != 0:
if len(extensionsToOffer) == 1:
pluralOrNot = _(" is")
else:
pluralOrNot = _("s are")
message = _("The following data type%s in your database:\n\n") % pluralOrNot
displayedTypeDescriptions = []
for extension in extensionsToOffer:
typeDescription = extension[_('typeDescription')]
if not typeDescription in displayedTypeDescriptions:
# only display each data type only once
message += ' ' + typeDescription + '\n'
displayedTypeDescriptions.append(typeDescription)
message += _("\nThe following extension%s not installed, but may help you work with this data:\n\n") % pluralOrNot
displayedExtensionNames = []
for extension in extensionsToOffer:
extensionName = extension[_('name')]
if not extensionName in displayedExtensionNames:
# only display each extension name only once
message += ' ' + extensionName + '\n'
displayedExtensionNames.append(extensionName)
message += _("\nYou can install extensions using the Extensions Manager option from the View menu.")
slicer.util.infoDisplay(message, parent=self, windowTitle='DICOM')
def checkForExtensions(self):
"""Check to see if there
are any registered extensions that might be available to
help the user work with data in the database.
1) load extension json description
2) load info for each series
3) check if data matches
then return matches
See
http://www.na-mic.org/Bug/view.php?id=4146
"""
# 1 - load json
import logging, os, json
logging.info(_('Imported a DICOM directory, checking for extensions'))
modulePath = os.path.dirname(slicer.modules.dicom.path)
extensionDescriptorPath = os.path.join(modulePath, 'DICOMExtensions.json')
try:
with open(extensionDescriptorPath) as extensionDescriptorFP:
extensionDescriptor = extensionDescriptorFP.read()
dicomExtensions = json.loads(extensionDescriptor)
except:
logging.error(_('Cannot access DICOMExtensions.json file'))
return
# 2 - get series info
# - iterate though metadata - should be fast even with large database
# - the fileValue call checks the tag cache so it's fast
modalityTag = "0008,0060"
sopClassUIDTag = "0008,0016"
sopClassUIDs = set()
modalities = set()
for patient in slicer.dicomDatabase.patients():
for study in slicer.dicomDatabase.studiesForPatient(patient):
for series in slicer.dicomDatabase.seriesForStudy(study):
instance0 = slicer.dicomDatabase.filesForSeries(series, 1)[0]
modality = slicer.dicomDatabase.fileValue(instance0, modalityTag)
sopClassUID = slicer.dicomDatabase.fileValue(instance0, sopClassUIDTag)
modalities.add(modality)
sopClassUIDs.add(sopClassUID)
# 3 - check if data matches
extensionsManagerModel = slicer.app.extensionsManagerModel()
installedExtensions = extensionsManagerModel.installedExtensions
extensionsToOffer = []
for extension in dicomExtensions[_('extensions')]:
extensionName = extension[_('name')]
if extensionName not in installedExtensions:
tagValues = extension[_('tagValues')]
if 'Modality' in tagValues:
for modality in tagValues[_('Modality')]:
if modality in modalities:
extensionsToOffer.append(extension)
if 'SOPClassUID' in tagValues:
for sopClassUID in tagValues[_('SOPClassUID')]:
if sopClassUID in sopClassUIDs:
extensionsToOffer.append(extension)
return extensionsToOffer
def setBrowserPersistence(self, state):
self.browserPersistent = state
self.settings.setValue(_('DICOM/BrowserPersistent'), bool(self.browserPersistent))
def onAdvancedViewButton(self, checked):
self.advancedView = checked
advancedWidgets = [self.loadableTableFrame, self.examineButton, self.uncheckAllButton]
for widget in advancedWidgets:
widget.visible = self.advancedView
self.updateButtonStates()
self.settings.setValue(_('DICOM/advancedView'), int(self.advancedView))
def onHorizontalViewCheckBox(self):
horizontal = self.horizontalViewCheckBox.checked
self.dicomBrowser.dicomTableManager().tableOrientation = qt.Qt.Horizontal if horizontal else qt.Qt.Vertical
self.settings.setValue(_('DICOM/horizontalTables'), int(horizontal))
def onSeriesSelected(self, seriesUIDList):
self.loadableTable.setLoadables([])
self.fileLists = self.getFileListsForRole(seriesUIDList, "SeriesUIDList")
self.updateButtonStates()
def getFileListsForRole(self, uidArgument, role):
fileLists = []
if role == "Series":
fileLists.append(slicer.dicomDatabase.filesForSeries(uidArgument))
if role == "SeriesUIDList":
for uid in uidArgument:
uid = uid.replace("'", "")
fileLists.append(slicer.dicomDatabase.filesForSeries(uid))
if role == "Study":
series = slicer.dicomDatabase.seriesForStudy(uidArgument)
for serie in series:
fileLists.append(slicer.dicomDatabase.filesForSeries(serie))
if role == "Patient":
studies = slicer.dicomDatabase.studiesForPatient(uidArgument)
for study in studies:
series = slicer.dicomDatabase.seriesForStudy(study)
for serie in series:
fileList = slicer.dicomDatabase.filesForSeries(serie)
fileLists.append(fileList)
return fileLists
def uncheckAllLoadables(self):
self.loadableTable.uncheckAll()
def onLoadableTableItemChanged(self, item):
self.updateButtonStates()
def examineForLoading(self):
"""For selected plugins, give user the option
of what to load"""
(self.loadablesByPlugin, loadEnabled) = self.getLoadablesFromFileLists(self.fileLists)
DICOMLib.selectHighestConfidenceLoadables(self.loadablesByPlugin)
self.loadableTable.setLoadables(self.loadablesByPlugin)
self.updateButtonStates()
def getLoadablesFromFileLists(self, fileLists):
"""Take list of file lists, return loadables by plugin dictionary
"""
loadablesByPlugin = {}
loadEnabled = False
# Get selected plugins from application settings
# Settings are filled in DICOMWidget using DICOMPluginSelector
settings = qt.QSettings()
selectedPlugins = []
if settings.contains(_('DICOM/disabledPlugins/size')):
size = settings.beginReadArray(_('DICOM/disabledPlugins'))
disabledPlugins = []
for i in range(size):
settings.setArrayIndex(i)
disabledPlugins.append(str(settings.allKeys()[0]))
settings.endArray()
for pluginClass in slicer.modules.dicomPlugins:
if pluginClass not in disabledPlugins:
selectedPlugins.append(pluginClass)
else:
# All DICOM plugins would be enabled by default
for pluginClass in slicer.modules.dicomPlugins:
selectedPlugins.append(pluginClass)
allFileCount = missingFileCount = 0
for fileList in fileLists:
for filePath in fileList:
allFileCount += 1
if not os.path.exists(filePath):
missingFileCount += 1
messages = []
if missingFileCount > 0:
messages.append(_("Warning: %d of %d selected files listed in the database cannot be found on disk.") % (missingFileCount, allFileCount))
if missingFileCount < allFileCount:
progressDialog = slicer.util.createProgressDialog(parent=self, value=0, maximum=100)
def progressCallback(progressDialog, progressLabel, progressValue):
progressDialog.labelText = _('\nChecking %s') % progressLabel
slicer.app.processEvents()
progressDialog.setValue(progressValue)
slicer.app.processEvents()
cancelled = progressDialog.wasCanceled
return cancelled
loadablesByPlugin, loadEnabled = DICOMLib.getLoadablesFromFileLists(fileLists, selectedPlugins, messages,
lambda progressLabel, progressValue, progressDialog=progressDialog: progressCallback(progressDialog, progressLabel, progressValue),
self.pluginInstances)
progressDialog.close()
if messages:
slicer.util.warningDisplay(_("Warning: %s\n\nSee python console for error message.") % ' '.join(messages),
windowTitle="DICOM", parent=self)
return loadablesByPlugin, loadEnabled
def isFileListInCheckedLoadables(self, fileList):
for plugin in self.loadablesByPlugin:
for loadable in self.loadablesByPlugin[plugin]:
if len(loadable.files) != len(fileList) or len(loadable.files) == 0:
continue
inputFileListCopy = copy.deepcopy(fileList)
loadableFileListCopy = copy.deepcopy(loadable.files)
try:
inputFileListCopy.sort()
loadableFileListCopy.sort()
except Exception:
pass
isEqual = True
for pair in zip(inputFileListCopy, loadableFileListCopy):
if pair[0] != pair[1]:
print(f"{pair[0]} != {pair[1]}")
isEqual = False
break
if not isEqual:
continue
return True
return False
def patientStudySeriesDoubleClicked(self):
if self.advancedViewButton.checkState() == 0:
# basic mode
self.loadCheckedLoadables()
else:
# advanced mode, just examine the double-clicked item, do not load
self.examineForLoading()
def loadCheckedLoadables(self):
"""Invoke the load method on each plugin for the loadable
(DICOMLoadable or qSlicerDICOMLoadable) instances that are selected"""
if self.advancedViewButton.checkState() == 0:
self.examineForLoading()
self.loadableTable.updateSelectedFromCheckstate()
# TODO: add check that disables all referenced stuff to be considered?
# get all the references from the checked loadables
referencedFileLists = []
for plugin in self.loadablesByPlugin:
for loadable in self.loadablesByPlugin[plugin]:
if hasattr(loadable, 'referencedInstanceUIDs'):
instanceFileList = []
for instance in loadable.referencedInstanceUIDs:
instanceFile = slicer.dicomDatabase.fileForInstance(instance)
if instanceFile != '':
instanceFileList.append(instanceFile)
if len(instanceFileList) and not self.isFileListInCheckedLoadables(instanceFileList):
referencedFileLists.append(instanceFileList)
# if applicable, find all loadables from the file lists
loadEnabled = False
if len(referencedFileLists):
(self.referencedLoadables, loadEnabled) = self.getLoadablesFromFileLists(referencedFileLists)
automaticallyLoadReferences = int(slicer.util.settingsValue(_('DICOM/automaticallyLoadReferences'), qt.QMessageBox.InvalidRole))
if slicer.app.commandOptions().testingEnabled:
automaticallyLoadReferences = qt.QMessageBox.No
if loadEnabled and automaticallyLoadReferences == qt.QMessageBox.InvalidRole:
self.showReferenceDialogAndProceed()
elif loadEnabled and automaticallyLoadReferences == qt.QMessageBox.Yes:
self.addReferencesAndProceed()
else:
self.proceedWithReferencedLoadablesSelection()
return
def showReferenceDialogAndProceed(self):
referencesDialog = DICOMReferencesDialog(self, loadables=self.referencedLoadables)
answer = referencesDialog.exec_()
if referencesDialog.rememberChoiceAndStopAskingCheckbox.checked == True:
if answer == qt.QMessageBox.Yes:
qt.QSettings().setValue(_('DICOM/automaticallyLoadReferences'), qt.QMessageBox.Yes)
if answer == qt.QMessageBox.No:
qt.QSettings().setValue(_('DICOM/automaticallyLoadReferences'), qt.QMessageBox.No)
if answer == qt.QMessageBox.Yes:
# each check box corresponds to a referenced loadable that was selected by examine;
# if the user confirmed that reference should be loaded, add it to the self.loadablesByPlugin dictionary
for plugin in self.referencedLoadables:
for loadable in [l for l in self.referencedLoadables[plugin] if l.selected]:
if referencesDialog.checkboxes[loadable].checked:
self.loadablesByPlugin[plugin].append(loadable)
self.loadablesByPlugin[plugin] = list(set(self.loadablesByPlugin[plugin]))
self.proceedWithReferencedLoadablesSelection()
elif answer == qt.QMessageBox.No:
self.proceedWithReferencedLoadablesSelection()
def addReferencesAndProceed(self):
for plugin in self.referencedLoadables:
for loadable in [l for l in self.referencedLoadables[plugin] if l.selected]:
self.loadablesByPlugin[plugin].append(loadable)
self.loadablesByPlugin[plugin] = list(set(self.loadablesByPlugin[plugin]))
self.proceedWithReferencedLoadablesSelection()
def proceedWithReferencedLoadablesSelection(self):
if not self.warnUserIfLoadableWarningsAndProceed():
return
progressDialog = slicer.util.createProgressDialog(parent=self, value=0, maximum=100)
def progressCallback(progressDialog, progressLabel, progressValue):
progressDialog.labelText = _('\nLoading %s') % progressLabel
slicer.app.processEvents()
progressDialog.setValue(progressValue)
slicer.app.processEvents()
cancelled = progressDialog.wasCanceled
return cancelled
qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
messages = []
loadedNodeIDs = DICOMLib.loadLoadables(self.loadablesByPlugin, messages,
lambda progressLabel, progressValue, progressDialog=progressDialog: progressCallback(progressDialog, progressLabel, progressValue))
loadedFileParameters = {}
loadedFileParameters['nodeIDs'] = loadedNodeIDs
slicer.app.ioManager().emitNewFileLoaded(loadedFileParameters)
qt.QApplication.restoreOverrideCursor()
progressDialog.close()
if messages:
slicer.util.warningDisplay('\n'.join(messages), windowTitle=_('DICOM loading'))
self.onLoadingFinished()
def warnUserIfLoadableWarningsAndProceed(self):
warningsInSelectedLoadables = False
details = ""
for plugin in self.loadablesByPlugin:
for loadable in self.loadablesByPlugin[plugin]:
if loadable.selected and loadable.warning != "":
warningsInSelectedLoadables = True
logging.warning(_('Warning in DICOM plugin ') + plugin.loadType + _(' when examining loadable ') + loadable.name +
': ' + loadable.warning)
details += loadable.name + " [" + plugin.loadType + "]: " + loadable.warning + "\n"
if warningsInSelectedLoadables:
warning = _("Warnings detected during load. Examine data in Advanced mode for details. Load anyway?")
if not slicer.util.confirmOkCancelDisplay(warning, parent=self, detailedText=details):
return False
return True
def onLoadingFinished(self):
if not self.browserPersistent:
self.close()
class DICOMReferencesDialog(qt.QMessageBox):
WINDOW_TITLE = _("Referenced datasets found")
WINDOW_TEXT = _("The loaded DICOM objects contain references to other datasets you did not select for loading. Please " \
"select Yes if you would like to load the following referenced datasets, No if you only want to load the " \
"originally selected series, or Cancel to abort loading.")
def __init__(self, parent, loadables):
super().__init__(parent)
self.loadables = loadables
self.checkboxes = dict()
self.setup()
def setup(self):
self._setBasicProperties()
self._addTextLabel()
self._addLoadableCheckboxes()
self.rememberChoiceAndStopAskingCheckbox = qt.QCheckBox(_('Remember choice and stop asking'))
self.rememberChoiceAndStopAskingCheckbox.toolTip = _('Can be changed later in Application Settings / DICOM')
self.yesButton = self.addButton(self.Yes)
self.yesButton.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Preferred))
self.noButton = self.addButton(self.No)
self.noButton.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Preferred))
self.cancelButton = self.addButton(self.Cancel)
self.cancelButton.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Preferred))
self.layout().addWidget(self.yesButton, 3, 0, 1, 1)
self.layout().addWidget(self.noButton, 3, 1, 1, 1)
self.layout().addWidget(self.cancelButton, 3, 2, 1, 1)
self.layout().addWidget(self.rememberChoiceAndStopAskingCheckbox, 2, 0, 1, 3)
def _setBasicProperties(self):
self.layout().setSpacing(9)
self.setWindowTitle(self.WINDOW_TITLE)
fontMetrics = qt.QFontMetrics(qt.QApplication.font(self))
try:
self.setMinimumWidth(fontMetrics.horizontalAdvance(self.WINDOW_TITLE))
except AttributeError:
# Support Qt < 5.11 lacking QFontMetrics::horizontalAdvance()
self.setMinimumWidth(fontMetrics.width(self.WINDOW_TITLE))
def _addTextLabel(self):
label = qt.QLabel(self.WINDOW_TEXT)
label.wordWrap = True
self.layout().addWidget(label, 0, 0, 1, 3)
print(_("test"))
def _addLoadableCheckboxes(self):
self.checkBoxGroupBox = qt.QGroupBox(_("References"))
self.checkBoxGroupBox.setLayout(qt.QFormLayout())
for plugin in self.loadables:
for loadable in [l for l in self.loadables[plugin] if l.selected]:
checkBoxText = loadable.name + ' (' + plugin.loadType + ') '
cb = qt.QCheckBox(checkBoxText, self)
cb.checked = True
cb.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Preferred))
self.checkboxes[loadable] = cb
self.checkBoxGroupBox.layout().addWidget(cb)
self.layout().addWidget(self.checkBoxGroupBox, 1, 0, 1, 3)
class DICOMLoadableTable(qt.QTableWidget):
"""Implement the Qt code for a table of
selectable slicer data to be made from
the given dicom files
"""
def __init__(self, parent, width=350, height=100):
super().__init__(parent)
self.setMinimumHeight(height)
self.setMinimumWidth(width)
self.loadables = {}
self.setLoadables([])
self.configure()
slicer.app.connect('aboutToQuit()', self.deleteLater)
def getNumberOfCheckedItems(self):
return sum(1 for row in range(self.rowCount) if self.item(row, 0).checkState() == qt.Qt.Checked)
def configure(self):
self.setColumnCount(3)
self.setHorizontalHeaderLabels([_('DICOM Data'), _('Reader'), _('Warnings')])
self.setSelectionBehavior(qt.QTableView.SelectRows)
self.horizontalHeader().setSectionResizeMode(qt.QHeaderView.Stretch)
self.horizontalHeader().setSectionResizeMode(0, qt.QHeaderView.Interactive)
self.horizontalHeader().setSectionResizeMode(1, qt.QHeaderView.ResizeToContents)
self.horizontalHeader().setSectionResizeMode(2, qt.QHeaderView.Stretch)
self.horizontalScrollMode = qt.QAbstractItemView.ScrollPerPixel
def addLoadableRow(self, loadable, row, reader):
self.insertRow(row)
self.loadables[row] = loadable
item = qt.QTableWidgetItem(loadable.name)
self.setItem(row, 0, item)
self.setCheckState(item, loadable)
self.addReaderColumn(item, reader, row)
self.addWarningColumn(item, loadable, row)
def setCheckState(self, item, loadable):
item.setCheckState(qt.Qt.Checked if loadable.selected else qt.Qt.Unchecked)
item.setToolTip(loadable.tooltip)
def addReaderColumn(self, item, reader, row):
if not reader:
return
readerItem = qt.QTableWidgetItem(reader)
readerItem.setFlags(readerItem.flags() ^ qt.Qt.ItemIsEditable)
self.setItem(row, 1, readerItem)
readerItem.setToolTip(item.toolTip())
def addWarningColumn(self, item, loadable, row):
warning = loadable.warning if loadable.warning else ''
warnItem = qt.QTableWidgetItem(warning)
warnItem.setFlags(warnItem.flags() ^ qt.Qt.ItemIsEditable)
self.setItem(row, 2, warnItem)
item.setToolTip(item.toolTip() + "\n" + warning)
warnItem.setToolTip(item.toolTip())
def setLoadables(self, loadablesByPlugin):
"""Load the table widget with a list
of volume options (of class DICOMVolume)
"""
self.clearContents()
self.setRowCount(0)
self.loadables = {}
# For each plugin, keep only a single loadable selected for the same file set
# to prevent loading same data multiple times.
for plugin in loadablesByPlugin:
for thisLoadableId in range(len(loadablesByPlugin[plugin])):
for prevLoadableId in range(0, thisLoadableId):
thisLoadable = loadablesByPlugin[plugin][thisLoadableId]
prevLoadable = loadablesByPlugin[plugin][prevLoadableId]
# fileDifferences will contain all the files that only present in one or the other list (or tuple)
fileDifferences = set(thisLoadable.files).symmetric_difference(set(prevLoadable.files))
if (not fileDifferences) and (prevLoadable.selected):
thisLoadable.selected = False
break
row = 0
for selectState in (True, False):
for plugin in loadablesByPlugin:
for loadable in loadablesByPlugin[plugin]:
if loadable.selected == selectState:
self.addLoadableRow(loadable, row, plugin.loadType)
row += 1
self.setVerticalHeaderLabels(row * [""])
def uncheckAll(self):
for row in range(self.rowCount):
item = self.item(row, 0)
item.setCheckState(False)
def updateSelectedFromCheckstate(self):
for row in range(self.rowCount):
item = self.item(row, 0)
self.loadables[row].selected = (item.checkState() != 0)
# updating the names
self.loadables[row].name = item.text()
print(_("Oumar"))