Commit 794ea5034b68ff1c8de8138d88d266bfd9bfbf85
Committed by
GitHub
1 parent
335b3a7c
Exists in
master
and in
27 other branches
Micro ct (#40)
ADD: This branch is for InVesalius to read BMP, TIF, JPEG and PNG files.
Showing
10 changed files
with
2268 additions
and
11 deletions
Show diff stats
invesalius/constants.py
| ... | ... | @@ -452,7 +452,7 @@ VTK_WARNING = 0 |
| 452 | 452 | [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, |
| 453 | 453 | ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, |
| 454 | 454 | ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES, |
| 455 | -ID_DICOM_NETWORK] = [wx.NewId() for number in range(13)] | |
| 455 | +ID_DICOM_NETWORK, ID_TIFF_JPG_PNG] = [wx.NewId() for number in range(14)] | |
| 456 | 456 | ID_EXIT = wx.ID_EXIT |
| 457 | 457 | ID_ABOUT = wx.ID_ABOUT |
| 458 | 458 | ... | ... |
invesalius/control.py
| ... | ... | @@ -35,8 +35,10 @@ import project as prj |
| 35 | 35 | import reader.analyze_reader as analyze |
| 36 | 36 | import reader.dicom_grouper as dg |
| 37 | 37 | import reader.dicom_reader as dcm |
| 38 | +import reader.bitmap_reader as bmp | |
| 38 | 39 | import session as ses |
| 39 | 40 | |
| 41 | + | |
| 40 | 42 | import utils |
| 41 | 43 | import gui.dialogs as dialogs |
| 42 | 44 | import subprocess |
| ... | ... | @@ -74,13 +76,20 @@ class Controller(): |
| 74 | 76 | 'Save raycasting preset') |
| 75 | 77 | Publisher.subscribe(self.OnOpenDicomGroup, |
| 76 | 78 | 'Open DICOM group') |
| 79 | + Publisher.subscribe(self.OnOpenBitmapFiles, | |
| 80 | + 'Open bitmap files') | |
| 77 | 81 | Publisher.subscribe(self.Progress, "Update dicom load") |
| 82 | + Publisher.subscribe(self.Progress, "Update bitmap load") | |
| 78 | 83 | Publisher.subscribe(self.OnLoadImportPanel, "End dicom load") |
| 84 | + Publisher.subscribe(self.OnLoadImportBitmapPanel, "End bitmap load") | |
| 79 | 85 | Publisher.subscribe(self.OnCancelImport, 'Cancel DICOM load') |
| 86 | + Publisher.subscribe(self.OnCancelImportBitmap, 'Cancel bitmap load') | |
| 87 | + | |
| 80 | 88 | Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project') |
| 81 | 89 | Publisher.subscribe(self.OnOpenProject, 'Open project') |
| 82 | 90 | Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project') |
| 83 | 91 | Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog') |
| 92 | + Publisher.subscribe(self.OnShowBitmapFile, 'Show bitmap dialog') | |
| 84 | 93 | |
| 85 | 94 | Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog') |
| 86 | 95 | |
| ... | ... | @@ -92,7 +101,9 @@ class Controller(): |
| 92 | 101 | Publisher.sendMessage('Hide import panel') |
| 93 | 102 | |
| 94 | 103 | |
| 95 | - | |
| 104 | + def OnCancelImportBitmap(self, pubsub_evt): | |
| 105 | + #self.cancel_import = True | |
| 106 | + Publisher.sendMessage('Hide import bitmap panel') | |
| 96 | 107 | |
| 97 | 108 | ########################### |
| 98 | 109 | ########################### |
| ... | ... | @@ -119,9 +130,35 @@ class Controller(): |
| 119 | 130 | self.LoadProject() |
| 120 | 131 | Publisher.sendMessage("Enable state project", True) |
| 121 | 132 | |
| 122 | - | |
| 133 | + def OnShowBitmapFile(self, pubsub_evt): | |
| 134 | + self.ShowDialogImportBitmapFile() | |
| 123 | 135 | ########################### |
| 124 | 136 | |
| 137 | + def ShowDialogImportBitmapFile(self): | |
| 138 | + # Offer to save current project if necessary | |
| 139 | + session = ses.Session() | |
| 140 | + st = session.project_status | |
| 141 | + if (st == const.PROJ_NEW) or (st == const.PROJ_CHANGE): | |
| 142 | + filename = session.project_path[1] | |
| 143 | + answer = dialog.SaveChangesDialog2(filename) | |
| 144 | + if answer: | |
| 145 | + self.ShowDialogSaveProject() | |
| 146 | + self.CloseProject() | |
| 147 | + #Publisher.sendMessage("Enable state project", False) | |
| 148 | + Publisher.sendMessage('Set project name') | |
| 149 | + Publisher.sendMessage("Stop Config Recording") | |
| 150 | + Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) | |
| 151 | + | |
| 152 | + # Import TIFF, BMP, JPEG or PNG | |
| 153 | + dirpath = dialog.ShowImportBitmapDirDialog() | |
| 154 | + | |
| 155 | + if dirpath and not os.listdir(dirpath): | |
| 156 | + dialog.ImportEmptyDirectory(dirpath) | |
| 157 | + elif dirpath: | |
| 158 | + self.StartImportBitmapPanel(dirpath) | |
| 159 | + # Publisher.sendMessage("Load data to import panel", dirpath) | |
| 160 | + | |
| 161 | + | |
| 125 | 162 | def ShowDialogImportDirectory(self): |
| 126 | 163 | # Offer to save current project if necessary |
| 127 | 164 | session = ses.Session() |
| ... | ... | @@ -291,6 +328,12 @@ class Controller(): |
| 291 | 328 | |
| 292 | 329 | ########################### |
| 293 | 330 | |
| 331 | + def StartImportBitmapPanel(self, path): | |
| 332 | + # retrieve DICOM files splited into groups | |
| 333 | + reader = bmp.ProgressBitmapReader() | |
| 334 | + reader.SetWindowEvent(self.frame) | |
| 335 | + reader.SetDirectoryPath(path) | |
| 336 | + Publisher.sendMessage('End busy cursor') | |
| 294 | 337 | |
| 295 | 338 | def StartImportPanel(self, path): |
| 296 | 339 | |
| ... | ... | @@ -324,6 +367,26 @@ class Controller(): |
| 324 | 367 | Publisher.sendMessage('Show import panel') |
| 325 | 368 | Publisher.sendMessage("Show import panel in frame") |
| 326 | 369 | |
| 370 | + def OnLoadImportBitmapPanel(self, evt): | |
| 371 | + data = evt.data | |
| 372 | + ok = self.LoadImportBitmapPanel(data) | |
| 373 | + if ok: | |
| 374 | + Publisher.sendMessage('Show import bitmap panel in frame') | |
| 375 | + #Publisher.sendMessage("Show import panel in frame") | |
| 376 | + | |
| 377 | + def LoadImportBitmapPanel(self, data): | |
| 378 | + #if patient_series and isinstance(patient_series, list): | |
| 379 | + #Publisher.sendMessage("Load import panel", patient_series) | |
| 380 | + #first_patient = patient_series[0] | |
| 381 | + #Publisher.sendMessage("Load bitmap preview", first_patient) | |
| 382 | + if data: | |
| 383 | + Publisher.sendMessage("Load import bitmap panel", data) | |
| 384 | + | |
| 385 | + return True | |
| 386 | + else: | |
| 387 | + dialog.ImportInvalidFiles() | |
| 388 | + return False | |
| 389 | + | |
| 327 | 390 | |
| 328 | 391 | def LoadImportPanel(self, patient_series): |
| 329 | 392 | if patient_series and isinstance(patient_series, list): |
| ... | ... | @@ -335,6 +398,9 @@ class Controller(): |
| 335 | 398 | dialog.ImportInvalidFiles() |
| 336 | 399 | return False |
| 337 | 400 | |
| 401 | + | |
| 402 | + #----------- to import by command line --------------------------------------------------- | |
| 403 | + | |
| 338 | 404 | def OnImportMedicalImages(self, pubsub_evt): |
| 339 | 405 | directory = pubsub_evt.data |
| 340 | 406 | self.ImportMedicalImages(directory) |
| ... | ... | @@ -358,6 +424,8 @@ class Controller(): |
| 358 | 424 | self.LoadProject() |
| 359 | 425 | Publisher.sendMessage("Enable state project", True) |
| 360 | 426 | |
| 427 | + #------------------------------------------------------------------------------------- | |
| 428 | + | |
| 361 | 429 | def LoadProject(self): |
| 362 | 430 | proj = prj.Project() |
| 363 | 431 | |
| ... | ... | @@ -401,9 +469,13 @@ class Controller(): |
| 401 | 469 | Publisher.sendMessage('Show mask', (mask_index, True)) |
| 402 | 470 | else: |
| 403 | 471 | mask_name = const.MASK_NAME_PATTERN % (1,) |
| 404 | - thresh = const.THRESHOLD_RANGE | |
| 405 | - colour = const.MASK_COLOUR[0] | |
| 406 | 472 | |
| 473 | + if proj.modality != "UNKNOWN": | |
| 474 | + thresh = const.THRESHOLD_RANGE | |
| 475 | + else: | |
| 476 | + thresh = proj.threshold_range | |
| 477 | + | |
| 478 | + colour = const.MASK_COLOUR[0] | |
| 407 | 479 | Publisher.sendMessage('Create new mask', |
| 408 | 480 | (mask_name, thresh, colour)) |
| 409 | 481 | |
| ... | ... | @@ -483,6 +555,138 @@ class Controller(): |
| 483 | 555 | dirpath = session.CreateProject(filename) |
| 484 | 556 | #proj.SavePlistProject(dirpath, filename) |
| 485 | 557 | |
| 558 | + | |
| 559 | + def CreateBitmapProject(self, bmp_data, rec_data, matrix, matrix_filename): | |
| 560 | + name_to_const = {"AXIAL":const.AXIAL, | |
| 561 | + "CORONAL":const.CORONAL, | |
| 562 | + "SAGITTAL":const.SAGITAL} | |
| 563 | + | |
| 564 | + name = rec_data[0] | |
| 565 | + orientation = rec_data[1] | |
| 566 | + sp_x = float(rec_data[2]) | |
| 567 | + sp_y = float(rec_data[3]) | |
| 568 | + sp_z = float(rec_data[4]) | |
| 569 | + interval = int(rec_data[5]) | |
| 570 | + | |
| 571 | + bits = bmp_data.GetFirstPixelSize() | |
| 572 | + sx, sy = size = bmp_data.GetFirstBitmapSize() | |
| 573 | + | |
| 574 | + proj = prj.Project() | |
| 575 | + proj.name = name | |
| 576 | + proj.modality = 'UNKNOWN' | |
| 577 | + proj.SetAcquisitionModality(proj.modality) | |
| 578 | + proj.matrix_shape = matrix.shape | |
| 579 | + proj.matrix_dtype = matrix.dtype.name | |
| 580 | + proj.matrix_filename = matrix_filename | |
| 581 | + #proj.imagedata = imagedata | |
| 582 | + #proj.dicom_sample = dicom | |
| 583 | + proj.original_orientation =\ | |
| 584 | + name_to_const[orientation.upper()] | |
| 585 | + proj.window = float(matrix.max()) | |
| 586 | + proj.level = float(matrix.max()/2) | |
| 587 | + | |
| 588 | + proj.threshold_range = int(matrix.min()), int(matrix.max()) | |
| 589 | + #const.THRESHOLD_RANGE = proj.threshold_range | |
| 590 | + | |
| 591 | + proj.spacing = self.Slice.spacing | |
| 592 | + | |
| 593 | + ###### | |
| 594 | + session = ses.Session() | |
| 595 | + filename = proj.name+".inv3" | |
| 596 | + | |
| 597 | + filename = filename.replace("/", "") #Fix problem case other/Skull_DICOM | |
| 598 | + | |
| 599 | + dirpath = session.CreateProject(filename) | |
| 600 | + | |
| 601 | + def OnOpenBitmapFiles(self, pubsub_evt): | |
| 602 | + rec_data = pubsub_evt.data | |
| 603 | + bmp_data = bmp.BitmapData() | |
| 604 | + matrix, matrix_filename = self.OpenBitmapFiles(bmp_data, rec_data) | |
| 605 | + | |
| 606 | + self.CreateBitmapProject(bmp_data, rec_data, matrix, matrix_filename) | |
| 607 | + | |
| 608 | + self.LoadProject() | |
| 609 | + Publisher.sendMessage("Enable state project", True) | |
| 610 | + | |
| 611 | + | |
| 612 | + def OpenBitmapFiles(self, bmp_data, rec_data): | |
| 613 | + | |
| 614 | + if bmp_data.IsAllBitmapSameSize(): | |
| 615 | + | |
| 616 | + name = rec_data[0] | |
| 617 | + orientation = rec_data[1] | |
| 618 | + sp_x = float(rec_data[2]) | |
| 619 | + sp_y = float(rec_data[3]) | |
| 620 | + sp_z = float(rec_data[4]) | |
| 621 | + interval = int(rec_data[5]) | |
| 622 | + | |
| 623 | + interval += 1 | |
| 624 | + | |
| 625 | + filelist = bmp_data.GetOnlyBitmapPath()[::interval] | |
| 626 | + bits = bmp_data.GetFirstPixelSize() | |
| 627 | + | |
| 628 | + sx, sy = size = bmp_data.GetFirstBitmapSize() | |
| 629 | + n_slices = len(filelist) | |
| 630 | + resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) | |
| 631 | + | |
| 632 | + zspacing = sp_z * interval | |
| 633 | + xyspacing = (sp_y, sp_x) | |
| 634 | + | |
| 635 | + if resolution_percentage < 1.0: | |
| 636 | + | |
| 637 | + re_dialog = dialog.ResizeImageDialog() | |
| 638 | + re_dialog.SetValue(int(resolution_percentage*100)) | |
| 639 | + re_dialog_value = re_dialog.ShowModal() | |
| 640 | + re_dialog.Close() | |
| 641 | + | |
| 642 | + if re_dialog_value == wx.ID_OK: | |
| 643 | + percentage = re_dialog.GetValue() | |
| 644 | + resolution_percentage = percentage / 100.0 | |
| 645 | + else: | |
| 646 | + return | |
| 647 | + | |
| 648 | + xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + self.matrix, scalar_range, self.filename = image_utils.bitmap2memmap(filelist, size, | |
| 653 | + orientation, (sp_z, sp_y, sp_x),resolution_percentage) | |
| 654 | + | |
| 655 | + | |
| 656 | + self.Slice = sl.Slice() | |
| 657 | + self.Slice.matrix = self.matrix | |
| 658 | + self.Slice.matrix_filename = self.filename | |
| 659 | + | |
| 660 | + if orientation == 'AXIAL': | |
| 661 | + self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing | |
| 662 | + elif orientation == 'CORONAL': | |
| 663 | + self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1] | |
| 664 | + elif orientation == 'SAGITTAL': | |
| 665 | + self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0] | |
| 666 | + | |
| 667 | + # 1(a): Fix gantry tilt, if any | |
| 668 | + #tilt_value = dicom.acquisition.tilt | |
| 669 | + #if (tilt_value) and (gui): | |
| 670 | + # # Tell user gantry tilt and fix, according to answer | |
| 671 | + # message = _("Fix gantry tilt applying the degrees below") | |
| 672 | + # value = -1*tilt_value | |
| 673 | + # tilt_value = dialog.ShowNumberDialog(message, value) | |
| 674 | + # image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value) | |
| 675 | + #elif (tilt_value) and not (gui): | |
| 676 | + # tilt_value = -1*tilt_value | |
| 677 | + # image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value) | |
| 678 | + | |
| 679 | + self.Slice.window_level = float(self.matrix.max()/2) | |
| 680 | + self.Slice.window_width = float(self.matrix.max()) | |
| 681 | + | |
| 682 | + scalar_range = int(self.matrix.min()), int(self.matrix.max()) | |
| 683 | + Publisher.sendMessage('Update threshold limits list', scalar_range) | |
| 684 | + | |
| 685 | + return self.matrix, self.filename#, dicom | |
| 686 | + | |
| 687 | + else: | |
| 688 | + print "Error: All slices must be of the same size." | |
| 689 | + | |
| 486 | 690 | def OnOpenDicomGroup(self, pubsub_evt): |
| 487 | 691 | group, interval, file_range = pubsub_evt.data |
| 488 | 692 | matrix, matrix_filename, dicom = self.OpenDicomGroup(group, interval, file_range, gui=True) | ... | ... |
invesalius/data/imagedata_utils.py
| ... | ... | @@ -32,7 +32,9 @@ from vtk.util import numpy_support |
| 32 | 32 | |
| 33 | 33 | import constants as const |
| 34 | 34 | from data import vtk_utils |
| 35 | +from reader import bitmap_reader | |
| 35 | 36 | import utils |
| 37 | +import converters | |
| 36 | 38 | |
| 37 | 39 | # TODO: Test cases which are originally in sagittal/coronal orientation |
| 38 | 40 | # and have gantry |
| ... | ... | @@ -416,6 +418,90 @@ class ImageCreator: |
| 416 | 418 | |
| 417 | 419 | return imagedata |
| 418 | 420 | |
| 421 | +def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage): | |
| 422 | + """ | |
| 423 | + From a list of dicom files it creates memmap file in the temp folder and | |
| 424 | + returns it and its related filename. | |
| 425 | + """ | |
| 426 | + message = _("Generating multiplanar visualization...") | |
| 427 | + update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") | |
| 428 | + | |
| 429 | + temp_file = tempfile.mktemp() | |
| 430 | + | |
| 431 | + if orientation == 'SAGITTAL': | |
| 432 | + if resolution_percentage == 1.0: | |
| 433 | + shape = slice_size[0], slice_size[1], len(files) | |
| 434 | + else: | |
| 435 | + shape = math.ceil(slice_size[0]*resolution_percentage),\ | |
| 436 | + math.ceil(slice_size[1]*resolution_percentage), len(files) | |
| 437 | + | |
| 438 | + elif orientation == 'CORONAL': | |
| 439 | + if resolution_percentage == 1.0: | |
| 440 | + shape = slice_size[1], len(files), slice_size[0] | |
| 441 | + else: | |
| 442 | + shape = math.ceil(slice_size[1]*resolution_percentage), len(files),\ | |
| 443 | + math.ceil(slice_size[0]*resolution_percentage) | |
| 444 | + else: | |
| 445 | + if resolution_percentage == 1.0: | |
| 446 | + shape = len(files), slice_size[1], slice_size[0] | |
| 447 | + else: | |
| 448 | + shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\ | |
| 449 | + math.ceil(slice_size[0]*resolution_percentage) | |
| 450 | + | |
| 451 | + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) | |
| 452 | + cont = 0 | |
| 453 | + max_scalar = None | |
| 454 | + min_scalar = None | |
| 455 | + | |
| 456 | + for n, f in enumerate(files): | |
| 457 | + image_as_array = bitmap_reader.ReadBitmap(f) | |
| 458 | + | |
| 459 | + image = converters.to_vtk(image_as_array, spacing=spacing,\ | |
| 460 | + slice_number=1, orientation=orientation.upper()) | |
| 461 | + | |
| 462 | + if resolution_percentage != 1.0: | |
| 463 | + | |
| 464 | + | |
| 465 | + image_resized = ResampleImage2D(image, px=None, py=None,\ | |
| 466 | + resolution_percentage = resolution_percentage, update_progress = None) | |
| 467 | + | |
| 468 | + image = image_resized | |
| 469 | + | |
| 470 | + min_aux, max_aux = image.GetScalarRange() | |
| 471 | + if min_scalar is None or min_aux < min_scalar: | |
| 472 | + min_scalar = min_aux | |
| 473 | + | |
| 474 | + if max_scalar is None or max_aux > max_scalar: | |
| 475 | + max_scalar = max_aux | |
| 476 | + | |
| 477 | + array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) | |
| 478 | + array = array.astype("int16") | |
| 479 | + | |
| 480 | + array = image_as_array | |
| 481 | + | |
| 482 | + if orientation == 'CORONAL': | |
| 483 | + array.shape = matrix.shape[0], matrix.shape[2] | |
| 484 | + matrix[:, n, :] = array | |
| 485 | + elif orientation == 'SAGITTAL': | |
| 486 | + array.shape = matrix.shape[0], matrix.shape[1] | |
| 487 | + # TODO: Verify if it's necessary to add the slices swapped only in | |
| 488 | + # sagittal rmi or only in # Rasiane's case or is necessary in all | |
| 489 | + # sagittal cases. | |
| 490 | + matrix[:, :, n] = array | |
| 491 | + else: | |
| 492 | + print array.shape, matrix.shape | |
| 493 | + array.shape = matrix.shape[1], matrix.shape[2] | |
| 494 | + matrix[n] = array | |
| 495 | + update_progress(cont,message) | |
| 496 | + cont += 1 | |
| 497 | + | |
| 498 | + matrix.flush() | |
| 499 | + scalar_range = min_scalar, max_scalar | |
| 500 | + | |
| 501 | + return matrix, scalar_range, temp_file | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 419 | 505 | def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
| 420 | 506 | """ |
| 421 | 507 | From a list of dicom files it creates memmap file in the temp folder and |
| ... | ... | @@ -462,7 +548,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
| 462 | 548 | resolution_percentage = resolution_percentage, update_progress = None) |
| 463 | 549 | |
| 464 | 550 | image = image_resized |
| 465 | - print ">>>>>>>>>", image.GetDimensions() | |
| 466 | 551 | |
| 467 | 552 | min_aux, max_aux = image.GetScalarRange() |
| 468 | 553 | if min_scalar is None or min_aux < min_scalar: |
| ... | ... | @@ -482,7 +567,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
| 482 | 567 | # sagittal cases. |
| 483 | 568 | matrix[:, :, n] = array |
| 484 | 569 | else: |
| 485 | - print array.shape, matrix.shape | |
| 486 | 570 | array.shape = matrix.shape[1], matrix.shape[2] |
| 487 | 571 | matrix[n] = array |
| 488 | 572 | update_progress(cont,message) | ... | ... |
| ... | ... | @@ -0,0 +1,903 @@ |
| 1 | +import wx | |
| 2 | +import vtk | |
| 3 | +import vtkgdcm | |
| 4 | +import time | |
| 5 | + | |
| 6 | +from vtk.util import numpy_support | |
| 7 | +from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor | |
| 8 | +from wx.lib.pubsub import pub as Publisher | |
| 9 | + | |
| 10 | +import constants as const | |
| 11 | +import data.vtk_utils as vtku | |
| 12 | +from data import converters | |
| 13 | +from reader import bitmap_reader | |
| 14 | +import utils | |
| 15 | + | |
| 16 | +NROWS = 3 | |
| 17 | +NCOLS = 6 | |
| 18 | +NUM_PREVIEWS = NCOLS*NROWS | |
| 19 | +PREVIEW_WIDTH = 70 | |
| 20 | +PREVIEW_HEIGTH = 70 | |
| 21 | + | |
| 22 | +PREVIEW_BACKGROUND = (255, 255, 255) # White | |
| 23 | + | |
| 24 | +STR_SIZE = _("Image size: %d x %d") | |
| 25 | +STR_SPC = _("Spacing: %.2f") | |
| 26 | +STR_LOCAL = _("Location: %.2f") | |
| 27 | +STR_PATIENT = "%s\n%s" | |
| 28 | +STR_ACQ = _("%s %s\nMade in InVesalius") | |
| 29 | + | |
| 30 | +myEVT_PREVIEW_CLICK = wx.NewEventType() | |
| 31 | +EVT_PREVIEW_CLICK = wx.PyEventBinder(myEVT_PREVIEW_CLICK, 1) | |
| 32 | + | |
| 33 | +myEVT_PREVIEW_DBLCLICK = wx.NewEventType() | |
| 34 | +EVT_PREVIEW_DBLCLICK = wx.PyEventBinder(myEVT_PREVIEW_DBLCLICK, 1) | |
| 35 | + | |
| 36 | +myEVT_CLICK_SLICE = wx.NewEventType() | |
| 37 | +# This event occurs when the user select a preview | |
| 38 | +EVT_CLICK_SLICE = wx.PyEventBinder(myEVT_CLICK_SLICE, 1) | |
| 39 | + | |
| 40 | +myEVT_CLICK_SERIE = wx.NewEventType() | |
| 41 | +# This event occurs when the user select a preview | |
| 42 | +EVT_CLICK_SERIE = wx.PyEventBinder(myEVT_CLICK_SERIE, 1) | |
| 43 | + | |
| 44 | +myEVT_CLICK = wx.NewEventType() | |
| 45 | +EVT_CLICK = wx.PyEventBinder(myEVT_CLICK, 1) | |
| 46 | + | |
| 47 | + | |
| 48 | +class SelectionEvent(wx.PyCommandEvent): | |
| 49 | + pass | |
| 50 | + | |
| 51 | + | |
| 52 | +class PreviewEvent(wx.PyCommandEvent): | |
| 53 | + def __init__(self , evtType, id): | |
| 54 | + super(PreviewEvent, self).__init__(evtType, id) | |
| 55 | + | |
| 56 | + def GetSelectID(self): | |
| 57 | + return self.SelectedID | |
| 58 | + | |
| 59 | + def SetSelectedID(self, id): | |
| 60 | + self.SelectedID = id | |
| 61 | + | |
| 62 | + def GetItemData(self): | |
| 63 | + return self.data | |
| 64 | + | |
| 65 | + def GetPressedShift(self): | |
| 66 | + return self.pressed_shift | |
| 67 | + | |
| 68 | + def SetItemData(self, data): | |
| 69 | + self.data = data | |
| 70 | + | |
| 71 | + def SetShiftStatus(self, status): | |
| 72 | + self.pressed_shift = status | |
| 73 | + | |
| 74 | + | |
| 75 | +class SerieEvent(PreviewEvent): | |
| 76 | + def __init__(self , evtType, id): | |
| 77 | + super(SerieEvent, self).__init__(evtType, id) | |
| 78 | + | |
| 79 | + | |
| 80 | +class BitmapInfo(object): | |
| 81 | + """ | |
| 82 | + Keep the informations and the image used by preview. | |
| 83 | + """ | |
| 84 | + def __init__(self, data): | |
| 85 | + #self.id = id | |
| 86 | + self.id = data[7] | |
| 87 | + self.title = data[6] | |
| 88 | + self.data = data | |
| 89 | + self.pos = data[8] | |
| 90 | + #self.subtitle = subtitle | |
| 91 | + self._preview = None | |
| 92 | + self.selected = False | |
| 93 | + #self.filename = "" | |
| 94 | + self.thumbnail_path = data[1] | |
| 95 | + | |
| 96 | + @property | |
| 97 | + def preview(self): | |
| 98 | + | |
| 99 | + if not self._preview: | |
| 100 | + bmp = wx.Bitmap(self.thumbnail_path, wx.BITMAP_TYPE_PNG) | |
| 101 | + self._preview = bmp.ConvertToImage() | |
| 102 | + return self._preview | |
| 103 | + | |
| 104 | + def release_thumbnail(self): | |
| 105 | + self._preview = None | |
| 106 | + | |
| 107 | +class DicomPaintPanel(wx.Panel): | |
| 108 | + def __init__(self, parent): | |
| 109 | + super(DicomPaintPanel, self).__init__(parent) | |
| 110 | + self._bind_events() | |
| 111 | + self.image = None | |
| 112 | + self.last_size = (10,10) | |
| 113 | + | |
| 114 | + def _bind_events(self): | |
| 115 | + self.Bind(wx.EVT_PAINT, self.OnPaint) | |
| 116 | + self.Bind(wx.EVT_SIZE, self.OnSize) | |
| 117 | + | |
| 118 | + def _build_bitmap(self, image): | |
| 119 | + bmp = wx.BitmapFromImage(image) | |
| 120 | + return bmp | |
| 121 | + | |
| 122 | + def _image_resize(self, image): | |
| 123 | + self.Update() | |
| 124 | + self.Layout() | |
| 125 | + new_size = self.GetSize() | |
| 126 | + # This is necessary due to darwin problem # | |
| 127 | + if new_size != (0,0): | |
| 128 | + self.last_size = new_size | |
| 129 | + return image.Scale(*new_size) | |
| 130 | + else: | |
| 131 | + return image.Scale(*self.last_size) | |
| 132 | + | |
| 133 | + def SetImage(self, image): | |
| 134 | + self.image = image | |
| 135 | + r_img = self._image_resize(image) | |
| 136 | + self.bmp = self._build_bitmap(r_img) | |
| 137 | + self.Refresh() | |
| 138 | + | |
| 139 | + def OnPaint(self, evt): | |
| 140 | + if self.image: | |
| 141 | + dc = wx.PaintDC(self) | |
| 142 | + dc.Clear() | |
| 143 | + dc.DrawBitmap(self.bmp, 0, 0) | |
| 144 | + | |
| 145 | + def OnSize(self, evt): | |
| 146 | + if self.image: | |
| 147 | + self.bmp = self._build_bitmap(self._image_resize(self.image)) | |
| 148 | + self.Refresh() | |
| 149 | + evt.Skip() | |
| 150 | + | |
| 151 | + | |
| 152 | +class Preview(wx.Panel): | |
| 153 | + """ | |
| 154 | + The little previews. | |
| 155 | + """ | |
| 156 | + def __init__(self, parent): | |
| 157 | + super(Preview, self).__init__(parent) | |
| 158 | + # Will it be white? | |
| 159 | + self.select_on = False | |
| 160 | + self.bitmap_info = None | |
| 161 | + self._init_ui() | |
| 162 | + self._bind_events() | |
| 163 | + | |
| 164 | + def _init_ui(self): | |
| 165 | + self.SetBackgroundColour(PREVIEW_BACKGROUND) | |
| 166 | + | |
| 167 | + self.title = wx.StaticText(self, -1, _("Image")) | |
| 168 | + self.subtitle = wx.StaticText(self, -1, _("Image")) | |
| 169 | + self.image_viewer = DicomPaintPanel(self) | |
| 170 | + | |
| 171 | + self.sizer = wx.BoxSizer(wx.VERTICAL) | |
| 172 | + self.sizer.Add(self.title, 0, | |
| 173 | + wx.ALIGN_CENTER_HORIZONTAL) | |
| 174 | + self.sizer.Add(self.subtitle, 0, | |
| 175 | + wx.ALIGN_CENTER_HORIZONTAL) | |
| 176 | + self.sizer.Add(self.image_viewer, 1, wx.ALIGN_CENTRE_HORIZONTAL \ | |
| 177 | + | wx.SHAPED | wx.ALL, 5) | |
| 178 | + self.sizer.Fit(self) | |
| 179 | + | |
| 180 | + self.SetSizer(self.sizer) | |
| 181 | + | |
| 182 | + self.Layout() | |
| 183 | + self.Update() | |
| 184 | + self.Fit() | |
| 185 | + self.SetAutoLayout(1) | |
| 186 | + | |
| 187 | + def _bind_events(self): | |
| 188 | + self.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | |
| 189 | + #self.interactor.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | |
| 190 | + #self.panel.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | |
| 191 | + #self.title.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | |
| 192 | + #self.subtitle.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | |
| 193 | + | |
| 194 | + self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | |
| 195 | + #self.interactor.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | |
| 196 | + #self.panel.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | |
| 197 | + #self.title.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | |
| 198 | + #self.subtitle.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | |
| 199 | + | |
| 200 | + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | |
| 201 | + #self.interactor.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | |
| 202 | + #self.panel.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | |
| 203 | + #self.title.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | |
| 204 | + #self.subtitle.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | |
| 205 | + | |
| 206 | + self.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | |
| 207 | + self.title.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | |
| 208 | + self.subtitle.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | |
| 209 | + self.image_viewer.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | |
| 210 | + | |
| 211 | + #self.Bind(wx.EVT_SIZE, self.OnSize) | |
| 212 | + | |
| 213 | + def SetBitmapToPreview(self, bitmap_info): | |
| 214 | + """ | |
| 215 | + Set a dicom to preview. | |
| 216 | + """ | |
| 217 | + | |
| 218 | + """ | |
| 219 | + self.dicom_info = dicom_info | |
| 220 | + self.SetTitle(dicom_info.title) | |
| 221 | + self.SetSubtitle(dicom_info.subtitle) | |
| 222 | + self.ID = dicom_info.id | |
| 223 | + dicom_info.size = self.image_viewer.GetSize() | |
| 224 | + image = dicom_info.preview | |
| 225 | + self.image_viewer.SetImage(image) | |
| 226 | + self.data = dicom_info.id | |
| 227 | + self.select_on = dicom_info.selected | |
| 228 | + self.Select() | |
| 229 | + self.Update() | |
| 230 | + """ | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + if self.bitmap_info: | |
| 235 | + self.bitmap_info.release_thumbnail() | |
| 236 | + | |
| 237 | + self.bitmap_info = bitmap_info | |
| 238 | + self.SetTitle(self.bitmap_info.title[-10:]) | |
| 239 | + self.SetSubtitle('') | |
| 240 | + | |
| 241 | + ##self.ID = bitmap_info.id | |
| 242 | + ##bitmap_info.size = self.image_viewer.GetSize() | |
| 243 | + image = self.bitmap_info.preview | |
| 244 | + | |
| 245 | + self.image_viewer.SetImage(image) | |
| 246 | + #self.data = bitmap_info.id | |
| 247 | + self.select_on = bitmap_info.selected | |
| 248 | + self.Select() | |
| 249 | + self.Update() | |
| 250 | + | |
| 251 | + def SetTitle(self, title): | |
| 252 | + self.title.SetLabel(title) | |
| 253 | + | |
| 254 | + def SetSubtitle(self, subtitle): | |
| 255 | + self.subtitle.SetLabel(subtitle) | |
| 256 | + | |
| 257 | + def OnEnter(self, evt): | |
| 258 | + if not self.select_on: | |
| 259 | + #c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DHILIGHT) | |
| 260 | + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) | |
| 261 | + self.SetBackgroundColour(c) | |
| 262 | + | |
| 263 | + def OnLeave(self, evt): | |
| 264 | + if not self.select_on: | |
| 265 | + c = (PREVIEW_BACKGROUND) | |
| 266 | + self.SetBackgroundColour(c) | |
| 267 | + | |
| 268 | + def OnSelect(self, evt): | |
| 269 | + | |
| 270 | + shift_pressed = False | |
| 271 | + if evt.m_shiftDown: | |
| 272 | + shift_pressed = True | |
| 273 | + | |
| 274 | + dicom_id = self.bitmap_info.id | |
| 275 | + self.select_on = True | |
| 276 | + self.bitmap_info.selected = True | |
| 277 | + self.Select() | |
| 278 | + | |
| 279 | + # Generating a EVT_PREVIEW_CLICK event | |
| 280 | + my_evt = SerieEvent(myEVT_PREVIEW_CLICK, self.GetId()) | |
| 281 | + | |
| 282 | + my_evt.SetSelectedID(self.bitmap_info.id) | |
| 283 | + my_evt.SetItemData(self.bitmap_info.data) | |
| 284 | + | |
| 285 | + my_evt.SetShiftStatus(shift_pressed) | |
| 286 | + my_evt.SetEventObject(self) | |
| 287 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 288 | + | |
| 289 | + Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos) | |
| 290 | + | |
| 291 | + evt.Skip() | |
| 292 | + | |
| 293 | + | |
| 294 | + def OnSize(self, evt): | |
| 295 | + if self.bitmap_info: | |
| 296 | + self.SetBitmapToPreview(self.bitmap_info) | |
| 297 | + evt.Skip() | |
| 298 | + | |
| 299 | + def Select(self, on=True): | |
| 300 | + if self.select_on: | |
| 301 | + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) | |
| 302 | + else: | |
| 303 | + c = (PREVIEW_BACKGROUND) | |
| 304 | + self.SetBackgroundColour(c) | |
| 305 | + self.Refresh() | |
| 306 | + | |
| 307 | + def OnDClick(self, evt): | |
| 308 | + my_evt = SerieEvent(myEVT_PREVIEW_DBLCLICK, self.GetId()) | |
| 309 | + my_evt.SetSelectedID(self.bitmap_info.id) | |
| 310 | + my_evt.SetItemData(self.bitmap_info.data) | |
| 311 | + my_evt.SetEventObject(self) | |
| 312 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 313 | + evt.Skip() | |
| 314 | + | |
| 315 | + | |
| 316 | +class BitmapPreviewSeries(wx.Panel): | |
| 317 | + """A dicom series preview panel""" | |
| 318 | + def __init__(self, parent): | |
| 319 | + super(BitmapPreviewSeries, self).__init__(parent) | |
| 320 | + # TODO: 3 pixels between the previews is a good idea? | |
| 321 | + # I have to test. | |
| 322 | + #self.sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 323 | + #self.SetSizer(self.sizer) | |
| 324 | + self.displayed_position = 0 | |
| 325 | + self.nhidden_last_display = 0 | |
| 326 | + self.selected_dicom = None | |
| 327 | + self.selected_panel = None | |
| 328 | + self._init_ui() | |
| 329 | + | |
| 330 | + def _init_ui(self): | |
| 331 | + scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | |
| 332 | + self.scroll = scroll | |
| 333 | + | |
| 334 | + self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) | |
| 335 | + | |
| 336 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 337 | + sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
| 338 | + | |
| 339 | + background_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 340 | + background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
| 341 | + background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | |
| 342 | + self.SetSizer(background_sizer) | |
| 343 | + background_sizer.Fit(self) | |
| 344 | + | |
| 345 | + self.Layout() | |
| 346 | + self.Update() | |
| 347 | + self.SetAutoLayout(1) | |
| 348 | + | |
| 349 | + self.sizer = background_sizer | |
| 350 | + | |
| 351 | + self._Add_Panels_Preview() | |
| 352 | + self._bind_events() | |
| 353 | + | |
| 354 | + def _Add_Panels_Preview(self): | |
| 355 | + self.previews = [] | |
| 356 | + for i in xrange(NROWS): | |
| 357 | + for j in xrange(NCOLS): | |
| 358 | + p = Preview(self) | |
| 359 | + p.Bind(EVT_PREVIEW_CLICK, self.OnSelect) | |
| 360 | + #if (i == j == 0): | |
| 361 | + #self._show_shadow(p) | |
| 362 | + #p.Hide() | |
| 363 | + self.previews.append(p) | |
| 364 | + self.grid.Add(p, 1, flag=wx.EXPAND) | |
| 365 | + | |
| 366 | + #def _show_shadow(self, preview): | |
| 367 | + # preview.ShowShadow() | |
| 368 | + | |
| 369 | + def _bind_events(self): | |
| 370 | + # When the user scrolls the window | |
| 371 | + self.Bind(wx.EVT_SCROLL, self.OnScroll) | |
| 372 | + self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) | |
| 373 | + | |
| 374 | + def OnSelect(self, evt): | |
| 375 | + my_evt = SerieEvent(myEVT_CLICK_SERIE, self.GetId()) | |
| 376 | + my_evt.SetSelectedID(evt.GetSelectID()) | |
| 377 | + my_evt.SetItemData(evt.GetItemData()) | |
| 378 | + | |
| 379 | + if self.selected_dicom: | |
| 380 | + self.selected_dicom.selected = self.selected_dicom is \ | |
| 381 | + evt.GetEventObject().bitmap_info | |
| 382 | + self.selected_panel.select_on = self.selected_panel is evt.GetEventObject() | |
| 383 | + self.selected_panel.Select() | |
| 384 | + self.selected_panel = evt.GetEventObject() | |
| 385 | + self.selected_dicom = self.selected_panel.bitmap_info | |
| 386 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 387 | + evt.Skip() | |
| 388 | + | |
| 389 | + def SetBitmapFiles(self, data): | |
| 390 | + #self.files = data | |
| 391 | + self.files = [] | |
| 392 | + | |
| 393 | + bitmap = bitmap_reader.BitmapData() | |
| 394 | + bitmap.SetData(data) | |
| 395 | + | |
| 396 | + pos = 0 | |
| 397 | + for d in data: | |
| 398 | + d.append(pos) | |
| 399 | + info = BitmapInfo(d) | |
| 400 | + self.files.append(info) | |
| 401 | + pos += 1 | |
| 402 | + | |
| 403 | + scroll_range = len(self.files)/NCOLS | |
| 404 | + if scroll_range * NCOLS < len(self.files): | |
| 405 | + scroll_range +=1 | |
| 406 | + self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | |
| 407 | + self._display_previews() | |
| 408 | + | |
| 409 | + | |
| 410 | + #def SetPatientGroups(self, patient): | |
| 411 | + # self.files = [] | |
| 412 | + # self.displayed_position = 0 | |
| 413 | + # self.nhidden_last_display = 0 | |
| 414 | + # group_list = patient.GetGroups() | |
| 415 | + # self.group_list = group_list | |
| 416 | + # n = 0 | |
| 417 | + # for group in group_list: | |
| 418 | + # info = BitmapInfo((group.dicom.patient.id, | |
| 419 | + # group.dicom.acquisition.serie_number), | |
| 420 | + # group.dicom, | |
| 421 | + # group.title, | |
| 422 | + # _("%d images") %(group.nslices)) | |
| 423 | + # self.files.append(info) | |
| 424 | + # n+=1 | |
| 425 | + # scroll_range = len(self.files)/NCOLS | |
| 426 | + # if scroll_range * NCOLS < len(self.files): | |
| 427 | + # scroll_range +=1 | |
| 428 | + # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | |
| 429 | + # self._display_previews() | |
| 430 | + | |
| 431 | + | |
| 432 | + def _display_previews(self): | |
| 433 | + initial = self.displayed_position * NCOLS | |
| 434 | + final = initial + NUM_PREVIEWS | |
| 435 | + if len(self.files) < final: | |
| 436 | + for i in xrange(final-len(self.files)): | |
| 437 | + try: | |
| 438 | + self.previews[-i-1].Hide() | |
| 439 | + except IndexError: | |
| 440 | + utils.debug("doesn't exist!") | |
| 441 | + pass | |
| 442 | + self.nhidden_last_display = final-len(self.files) | |
| 443 | + else: | |
| 444 | + if self.nhidden_last_display: | |
| 445 | + for i in xrange(self.nhidden_last_display): | |
| 446 | + try: | |
| 447 | + self.previews[-i-1].Show() | |
| 448 | + except IndexError: | |
| 449 | + utils.debug("doesn't exist!") | |
| 450 | + pass | |
| 451 | + self.nhidden_last_display = 0 | |
| 452 | + | |
| 453 | + for f, p in zip(self.files[initial:final], self.previews): | |
| 454 | + p.SetBitmapToPreview(f) | |
| 455 | + if f.selected: | |
| 456 | + self.selected_panel = p | |
| 457 | + | |
| 458 | + for f, p in zip(self.files[initial:final], self.previews): | |
| 459 | + p.Show() | |
| 460 | + | |
| 461 | + | |
| 462 | + def OnScroll(self, evt=None): | |
| 463 | + if evt: | |
| 464 | + if self.displayed_position != evt.GetPosition(): | |
| 465 | + self.displayed_position = evt.GetPosition() | |
| 466 | + else: | |
| 467 | + if self.displayed_position != self.scroll.GetThumbPosition(): | |
| 468 | + self.displayed_position = self.scroll.GetThumbPosition() | |
| 469 | + self._display_previews() | |
| 470 | + | |
| 471 | + def OnWheel(self, evt): | |
| 472 | + d = evt.GetWheelDelta() / evt.GetWheelRotation() | |
| 473 | + self.scroll.SetThumbPosition(self.scroll.GetThumbPosition() - d) | |
| 474 | + self.OnScroll() | |
| 475 | + | |
| 476 | + | |
| 477 | +class SingleImagePreview(wx.Panel): | |
| 478 | + def __init__(self, parent): | |
| 479 | + wx.Panel.__init__(self, parent, -1) | |
| 480 | + self.actor = None | |
| 481 | + self.__init_gui() | |
| 482 | + self.__init_vtk() | |
| 483 | + self.__bind_evt_gui() | |
| 484 | + self.__bind_pubsub() | |
| 485 | + self.dicom_list = [] | |
| 486 | + self.nimages = 1 | |
| 487 | + self.current_index = 0 | |
| 488 | + self.window_width = const.WINDOW_LEVEL[_("Bone")][0] | |
| 489 | + self.window_level = const.WINDOW_LEVEL[_("Bone")][1] | |
| 490 | + | |
| 491 | + def __init_vtk(self): | |
| 492 | + text_image_size = vtku.Text() | |
| 493 | + text_image_size.SetPosition(const.TEXT_POS_LEFT_UP) | |
| 494 | + text_image_size.SetValue("") | |
| 495 | + text_image_size.SetSize(const.TEXT_SIZE_SMALL) | |
| 496 | + self.text_image_size = text_image_size | |
| 497 | + | |
| 498 | + text_image_location = vtku.Text() | |
| 499 | + text_image_location.SetVerticalJustificationToBottom() | |
| 500 | + text_image_location.SetPosition(const.TEXT_POS_LEFT_DOWN) | |
| 501 | + text_image_location.SetValue("") | |
| 502 | + text_image_location.SetSize(const.TEXT_SIZE_SMALL) | |
| 503 | + self.text_image_location = text_image_location | |
| 504 | + | |
| 505 | + text_patient = vtku.Text() | |
| 506 | + text_patient.SetJustificationToRight() | |
| 507 | + text_patient.SetPosition(const.TEXT_POS_RIGHT_UP) | |
| 508 | + text_patient.SetValue("") | |
| 509 | + text_patient.SetSize(const.TEXT_SIZE_SMALL) | |
| 510 | + self.text_patient = text_patient | |
| 511 | + | |
| 512 | + text_acquisition = vtku.Text() | |
| 513 | + text_acquisition.SetJustificationToRight() | |
| 514 | + text_acquisition.SetVerticalJustificationToBottom() | |
| 515 | + text_acquisition.SetPosition(const.TEXT_POS_RIGHT_DOWN) | |
| 516 | + text_acquisition.SetValue("") | |
| 517 | + text_acquisition.SetSize(const.TEXT_SIZE_SMALL) | |
| 518 | + self.text_acquisition = text_acquisition | |
| 519 | + | |
| 520 | + renderer = vtk.vtkRenderer() | |
| 521 | + renderer.AddActor(text_image_size.actor) | |
| 522 | + renderer.AddActor(text_image_location.actor) | |
| 523 | + renderer.AddActor(text_patient.actor) | |
| 524 | + renderer.AddActor(text_acquisition.actor) | |
| 525 | + self.renderer = renderer | |
| 526 | + | |
| 527 | + style = vtk.vtkInteractorStyleImage() | |
| 528 | + | |
| 529 | + interactor = wxVTKRenderWindowInteractor(self.panel, -1, | |
| 530 | + size=wx.Size(340,340)) | |
| 531 | + interactor.GetRenderWindow().AddRenderer(renderer) | |
| 532 | + interactor.SetInteractorStyle(style) | |
| 533 | + interactor.Render() | |
| 534 | + self.interactor = interactor | |
| 535 | + | |
| 536 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 537 | + sizer.Add(interactor, 1, wx.GROW|wx.EXPAND) | |
| 538 | + sizer.Fit(self.panel) | |
| 539 | + self.panel.SetSizer(sizer) | |
| 540 | + self.Layout() | |
| 541 | + self.Update() | |
| 542 | + | |
| 543 | + def __init_gui(self): | |
| 544 | + self.panel = wx.Panel(self, -1) | |
| 545 | + | |
| 546 | + slider = wx.Slider(self, | |
| 547 | + id=-1, | |
| 548 | + value=0, | |
| 549 | + minValue=0, | |
| 550 | + maxValue=99, | |
| 551 | + style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS) | |
| 552 | + slider.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | |
| 553 | + slider.SetTickFreq(1, 1) | |
| 554 | + self.slider = slider | |
| 555 | + | |
| 556 | + checkbox = wx.CheckBox(self, -1, _("Auto-play")) | |
| 557 | + self.checkbox = checkbox | |
| 558 | + | |
| 559 | + in_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 560 | + in_sizer.Add(slider, 1, wx.GROW|wx.EXPAND) | |
| 561 | + in_sizer.Add(checkbox, 0) | |
| 562 | + | |
| 563 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 564 | + sizer.Add(self.panel, 20, wx.GROW|wx.EXPAND) | |
| 565 | + sizer.Add(in_sizer, 1, wx.GROW|wx.EXPAND) | |
| 566 | + sizer.Fit(self) | |
| 567 | + | |
| 568 | + self.SetSizer(sizer) | |
| 569 | + self.Layout() | |
| 570 | + self.Update() | |
| 571 | + self.SetAutoLayout(1) | |
| 572 | + | |
| 573 | + def __bind_evt_gui(self): | |
| 574 | + self.slider.Bind(wx.EVT_SLIDER, self.OnSlider) | |
| 575 | + self.checkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) | |
| 576 | + | |
| 577 | + def __bind_pubsub(self): | |
| 578 | + Publisher.subscribe(self.ShowBitmapByPosition, 'Set bitmap in preview panel') | |
| 579 | + | |
| 580 | + def ShowBitmapByPosition(self, evt): | |
| 581 | + pos = evt.data | |
| 582 | + self.ShowSlice(pos) | |
| 583 | + | |
| 584 | + | |
| 585 | + def OnSlider(self, evt): | |
| 586 | + pos = evt.GetInt() | |
| 587 | + self.ShowSlice(pos) | |
| 588 | + evt.Skip() | |
| 589 | + | |
| 590 | + def OnCheckBox(self, evt): | |
| 591 | + self.ischecked = evt.IsChecked() | |
| 592 | + if evt.IsChecked(): | |
| 593 | + wx.CallAfter(self.OnRun) | |
| 594 | + evt.Skip() | |
| 595 | + | |
| 596 | + def OnRun(self): | |
| 597 | + pos = self.slider.GetValue() | |
| 598 | + pos += 1 | |
| 599 | + if not (self.nimages- pos): | |
| 600 | + pos = 0 | |
| 601 | + self.slider.SetValue(pos) | |
| 602 | + self.ShowSlice(pos) | |
| 603 | + time.sleep(0.2) | |
| 604 | + if self.ischecked: | |
| 605 | + try: | |
| 606 | + wx.Yield() | |
| 607 | + #TODO: temporary fix necessary in the Windows XP 64 Bits | |
| 608 | + #BUG in wxWidgets http://trac.wxwidgets.org/ticket/10896 | |
| 609 | + except(wx._core.PyAssertionError): | |
| 610 | + utils.debug("wx._core.PyAssertionError") | |
| 611 | + finally: | |
| 612 | + wx.CallAfter(self.OnRun) | |
| 613 | + | |
| 614 | + def SetBitmapFiles(self, data): | |
| 615 | + #self.dicom_list = group.GetHandSortedList() | |
| 616 | + self.bitmap_list = data | |
| 617 | + self.current_index = 0 | |
| 618 | + self.nimages = len(data) | |
| 619 | + # GUI | |
| 620 | + self.slider.SetMax(self.nimages-1) | |
| 621 | + self.slider.SetValue(0) | |
| 622 | + self.ShowSlice() | |
| 623 | + | |
| 624 | + def ShowSlice(self, index = 0): | |
| 625 | + bitmap = self.bitmap_list[index] | |
| 626 | + | |
| 627 | + # UPDATE GUI | |
| 628 | + ## Text related to size | |
| 629 | + value = STR_SIZE %(bitmap[3], bitmap[4]) | |
| 630 | + self.text_image_size.SetValue(value) | |
| 631 | + | |
| 632 | + value1 = '' | |
| 633 | + value2 = '' | |
| 634 | + | |
| 635 | + value = "%s\n%s" %(value1, value2) | |
| 636 | + self.text_image_location.SetValue(value) | |
| 637 | + | |
| 638 | + | |
| 639 | + #self.text_patient.SetValue(value) | |
| 640 | + self.text_patient.SetValue('') | |
| 641 | + | |
| 642 | + #self.text_acquisition.SetValue(value) | |
| 643 | + self.text_acquisition.SetValue('') | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + n_array = bitmap_reader.ReadBitmap(bitmap[0]) | |
| 648 | + | |
| 649 | + image = converters.to_vtk(n_array, spacing=(1,1,1),\ | |
| 650 | + slice_number=1, orientation="AXIAL") | |
| 651 | + | |
| 652 | + | |
| 653 | + # ADJUST CONTRAST | |
| 654 | + window_level = n_array.max()/2 | |
| 655 | + window_width = n_array.max() | |
| 656 | + | |
| 657 | + colorer = vtk.vtkImageMapToWindowLevelColors() | |
| 658 | + colorer.SetInputData(image) | |
| 659 | + colorer.SetWindow(float(window_width)) | |
| 660 | + colorer.SetLevel(float(window_level)) | |
| 661 | + colorer.Update() | |
| 662 | + | |
| 663 | + if self.actor is None: | |
| 664 | + self.actor = vtk.vtkImageActor() | |
| 665 | + self.renderer.AddActor(self.actor) | |
| 666 | + | |
| 667 | + # PLOT IMAGE INTO VIEWER | |
| 668 | + self.actor.SetInputData(colorer.GetOutput()) | |
| 669 | + self.renderer.ResetCamera() | |
| 670 | + self.interactor.Render() | |
| 671 | + | |
| 672 | + # Setting slider position | |
| 673 | + self.slider.SetValue(index) | |
| 674 | + | |
| 675 | + | |
| 676 | +#class BitmapPreviewSlice(wx.Panel): | |
| 677 | +# def __init__(self, parent): | |
| 678 | +# super(BitmapPreviewSlice, self).__init__(parent) | |
| 679 | +# # TODO: 3 pixels between the previews is a good idea? | |
| 680 | +# # I have to test. | |
| 681 | +# self.displayed_position = 0 | |
| 682 | +# self.nhidden_last_display = 0 | |
| 683 | +# self.selected_dicom = None | |
| 684 | +# self.selected_panel = None | |
| 685 | +# self.first_selection = None | |
| 686 | +# self.last_selection = None | |
| 687 | +# self._init_ui() | |
| 688 | +# | |
| 689 | +# def _init_ui(self): | |
| 690 | +# scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | |
| 691 | +# self.scroll = scroll | |
| 692 | +# | |
| 693 | +# self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) | |
| 694 | +# | |
| 695 | +# sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 696 | +# sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
| 697 | +# | |
| 698 | +# background_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 699 | +# background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
| 700 | +# background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | |
| 701 | +# self.SetSizer(background_sizer) | |
| 702 | +# background_sizer.Fit(self) | |
| 703 | +# | |
| 704 | +# self.Layout() | |
| 705 | +# self.Update() | |
| 706 | +# self.SetAutoLayout(1) | |
| 707 | +# | |
| 708 | +# self.sizer = background_sizer | |
| 709 | +# | |
| 710 | +# self._Add_Panels_Preview() | |
| 711 | +# self._bind_events() | |
| 712 | +# | |
| 713 | +# def _Add_Panels_Preview(self): | |
| 714 | +# self.previews = [] | |
| 715 | +# for i in xrange(NROWS): | |
| 716 | +# for j in xrange(NCOLS): | |
| 717 | +# p = Preview(self) | |
| 718 | +# p.Bind(EVT_PREVIEW_CLICK, self.OnPreviewClick) | |
| 719 | +# #p.Hide() | |
| 720 | +# self.previews.append(p) | |
| 721 | +# self.grid.Add(p, 1, flag=wx.EXPAND) | |
| 722 | +# | |
| 723 | +# def _bind_events(self): | |
| 724 | +# # When the user scrolls the window | |
| 725 | +# self.Bind(wx.EVT_SCROLL, self.OnScroll) | |
| 726 | +# self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) | |
| 727 | +# | |
| 728 | +# def SetDicomDirectory(self, directory): | |
| 729 | +# utils.debug("Setting Dicom Directory %s" % directory) | |
| 730 | +# self.directory = directory | |
| 731 | +# self.series = dicom_reader.GetSeries(directory)[0] | |
| 732 | +# | |
| 733 | +# def SetPatientGroups(self, patient): | |
| 734 | +# self.group_list = patient.GetGroups() | |
| 735 | +# | |
| 736 | +# | |
| 737 | +# #def SetDicomSerie(self, pos): | |
| 738 | +# # self.files = [] | |
| 739 | +# # self.displayed_position = 0 | |
| 740 | +# # self.nhidden_last_display = 0 | |
| 741 | +# # group = self.group_list[pos] | |
| 742 | +# # self.group = group | |
| 743 | +# # #dicom_files = group.GetList() | |
| 744 | +# # dicom_files = group.GetHandSortedList() | |
| 745 | +# # n = 0 | |
| 746 | +# # for dicom in dicom_files: | |
| 747 | +# # info = BitmapInfo(n, dicom, | |
| 748 | +# # _("Image %d") % (dicom.image.number), | |
| 749 | +# # "%.2f" % (dicom.image.position[2])) | |
| 750 | +# # self.files.append(info) | |
| 751 | +# # n+=1 | |
| 752 | +# | |
| 753 | +# # scroll_range = len(self.files)/NCOLS | |
| 754 | +# # if scroll_range * NCOLS < len(self.files): | |
| 755 | +# # scroll_range +=1 | |
| 756 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | |
| 757 | +# | |
| 758 | +# # self._display_previews() | |
| 759 | +# | |
| 760 | +# #def SetDicomGroup(self, group): | |
| 761 | +# # self.files = [] | |
| 762 | +# # self.displayed_position = 0 | |
| 763 | +# # self.nhidden_last_display = 0 | |
| 764 | +# # #dicom_files = group.GetList() | |
| 765 | +# # dicom_files = group.GetHandSortedList() | |
| 766 | +# # n = 0 | |
| 767 | +# # for dicom in dicom_files: | |
| 768 | +# # info = BitmapInfo(n, dicom, | |
| 769 | +# # _("Image %d") % (dicom.image.number), | |
| 770 | +# # "%.2f" % (dicom.image.position[2]), | |
| 771 | +# # ) | |
| 772 | +# # self.files.append(info) | |
| 773 | +# # n+=1 | |
| 774 | +# | |
| 775 | +# # scroll_range = len(self.files)/NCOLS | |
| 776 | +# # if scroll_range * NCOLS < len(self.files): | |
| 777 | +# # scroll_range +=1 | |
| 778 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | |
| 779 | +# | |
| 780 | +# # self._display_previews() | |
| 781 | +# | |
| 782 | +# #def SetDicomGroup(self, group): | |
| 783 | +# # self.files = [] | |
| 784 | +# # self.displayed_position = 0 | |
| 785 | +# # self.nhidden_last_display = 0 | |
| 786 | +# # #dicom_files = group.GetList() | |
| 787 | +# # dicom_files = group.GetHandSortedList() | |
| 788 | +# # n = 0 | |
| 789 | +# # for dicom in dicom_files: | |
| 790 | +# # info = BitmapInfo(n, dicom, | |
| 791 | +# # _("Image %d") % (dicom.image.number), | |
| 792 | +# # "%.2f" % (dicom.image.position[2]), | |
| 793 | +# # ) | |
| 794 | +# # self.files.append(info) | |
| 795 | +# # n+=1 | |
| 796 | +# | |
| 797 | +# # scroll_range = len(self.files)/NCOLS | |
| 798 | +# # if scroll_range * NCOLS < len(self.files): | |
| 799 | +# # scroll_range +=1 | |
| 800 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | |
| 801 | +# | |
| 802 | +# # self._display_previews() | |
| 803 | +# | |
| 804 | +# | |
| 805 | +# def _display_previews(self): | |
| 806 | +# initial = self.displayed_position * NCOLS | |
| 807 | +# final = initial + NUM_PREVIEWS | |
| 808 | +# if len(self.files) < final: | |
| 809 | +# for i in xrange(final-len(self.files)): | |
| 810 | +# try: | |
| 811 | +# self.previews[-i-1].Hide() | |
| 812 | +# except IndexError: | |
| 813 | +# utils.debug("doesn't exist!") | |
| 814 | +# self.nhidden_last_display = final-len(self.files) | |
| 815 | +# else: | |
| 816 | +# if self.nhidden_last_display: | |
| 817 | +# for i in xrange(self.nhidden_last_display): | |
| 818 | +# try: | |
| 819 | +# self.previews[-i-1].Show() | |
| 820 | +# except IndexError: | |
| 821 | +# utils.debug("doesn't exist!") | |
| 822 | +# self.nhidden_last_display = 0 | |
| 823 | +# | |
| 824 | +# for f, p in zip(self.files[initial:final], self.previews): | |
| 825 | +# p.SetBitmapToPreview(f) | |
| 826 | +# if f.selected: | |
| 827 | +# self.selected_panel = p | |
| 828 | +# #p.interactor.Render() | |
| 829 | +# | |
| 830 | +# for f, p in zip(self.files[initial:final], self.previews): | |
| 831 | +# p.Show() | |
| 832 | +# | |
| 833 | +# def OnPreviewClick(self, evt): | |
| 834 | +# | |
| 835 | +# dicom_id = evt.GetSelectID() | |
| 836 | +# | |
| 837 | +# if self.first_selection is None: | |
| 838 | +# self.first_selection = dicom_id | |
| 839 | +# | |
| 840 | +# if self.last_selection is None: | |
| 841 | +# self.last_selection = dicom_id | |
| 842 | +# | |
| 843 | +# | |
| 844 | +# if evt.GetPressedShift(): | |
| 845 | +# | |
| 846 | +# if dicom_id < self.first_selection and dicom_id < self.last_selection: | |
| 847 | +# self.first_selection = dicom_id | |
| 848 | +# else: | |
| 849 | +# self.last_selection = dicom_id | |
| 850 | +# else: | |
| 851 | +# self.first_selection = dicom_id | |
| 852 | +# self.last_selection = dicom_id | |
| 853 | +# | |
| 854 | +# for i in xrange(len(self.files)): | |
| 855 | +# | |
| 856 | +# if i == dicom_id: | |
| 857 | +# self.files[i].selected = True | |
| 858 | +# else: | |
| 859 | +# self.files[i].selected = False | |
| 860 | +# | |
| 861 | +# | |
| 862 | +# my_evt = SerieEvent(myEVT_CLICK_SLICE, self.GetId()) | |
| 863 | +# my_evt.SetSelectedID(evt.GetSelectID()) | |
| 864 | +# my_evt.SetItemData(evt.GetItemData()) | |
| 865 | +# | |
| 866 | +# if self.selected_dicom: | |
| 867 | +# self.selected_dicom.selected = self.selected_dicom is \ | |
| 868 | +# evt.GetEventObject().bitmap_info | |
| 869 | +# self.selected_panel.select_on = self.selected_panel is evt.GetEventObject() | |
| 870 | +# | |
| 871 | +# if self.first_selection != self.last_selection: | |
| 872 | +# for i in xrange(len(self.files)): | |
| 873 | +# if i >= self.first_selection and i <= self.last_selection: | |
| 874 | +# self.files[i].selected = True | |
| 875 | +# else: | |
| 876 | +# self.files[i].selected = False | |
| 877 | +# | |
| 878 | +# else: | |
| 879 | +# self.selected_panel.Select() | |
| 880 | +# | |
| 881 | +# self._display_previews() | |
| 882 | +# self.selected_panel = evt.GetEventObject() | |
| 883 | +# self.selected_dicom = self.selected_panel.bitmap_info | |
| 884 | +# self.GetEventHandler().ProcessEvent(my_evt) | |
| 885 | +# | |
| 886 | +# #Publisher.sendMessage("Selected Import Images", [self.first_selection, \ | |
| 887 | +# # self.last_selection]) | |
| 888 | +# | |
| 889 | +# def OnScroll(self, evt=None): | |
| 890 | +# if evt: | |
| 891 | +# if self.displayed_position != evt.GetPosition(): | |
| 892 | +# self.displayed_position = evt.GetPosition() | |
| 893 | +# else: | |
| 894 | +# if self.displayed_position != self.scroll.GetThumbPosition(): | |
| 895 | +# self.displayed_position = self.scroll.GetThumbPosition() | |
| 896 | +# self._display_previews() | |
| 897 | +# | |
| 898 | +# def OnWheel(self, evt): | |
| 899 | +# d = evt.GetWheelDelta() / evt.GetWheelRotation() | |
| 900 | +# self.scroll.SetThumbPosition(self.scroll.GetThumbPosition() - d) | |
| 901 | +# self.OnScroll() | |
| 902 | + | |
| 903 | + | ... | ... |
invesalius/gui/dialogs.py
| ... | ... | @@ -39,6 +39,12 @@ from gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGE |
| 39 | 39 | |
| 40 | 40 | import numpy as np |
| 41 | 41 | |
| 42 | +try: | |
| 43 | + from agw import floatspin as FS | |
| 44 | +except ImportError: # if it's not there locally, try the wxPython lib. | |
| 45 | + import wx.lib.agw.floatspin as FS | |
| 46 | + | |
| 47 | + | |
| 42 | 48 | class MaskEvent(wx.PyCommandEvent): |
| 43 | 49 | def __init__(self , evtType, id, mask_index): |
| 44 | 50 | wx.PyCommandEvent.__init__(self, evtType, id,) |
| ... | ... | @@ -306,6 +312,48 @@ def ShowImportDirDialog(): |
| 306 | 312 | os.chdir(current_dir) |
| 307 | 313 | return path |
| 308 | 314 | |
| 315 | +def ShowImportBitmapDirDialog(): | |
| 316 | + current_dir = os.path.abspath(".") | |
| 317 | + | |
| 318 | + if (sys.platform == 'win32') or (sys.platform == 'linux2'): | |
| 319 | + session = ses.Session() | |
| 320 | + | |
| 321 | + if (session.GetLastDicomFolder()): | |
| 322 | + folder = session.GetLastDicomFolder() | |
| 323 | + else: | |
| 324 | + folder = '' | |
| 325 | + else: | |
| 326 | + folder = '' | |
| 327 | + | |
| 328 | + dlg = wx.DirDialog(None, _("Choose a folder with TIFF, BMP, JPG or PNG:"), folder, | |
| 329 | + style=wx.DD_DEFAULT_STYLE | |
| 330 | + | wx.DD_DIR_MUST_EXIST | |
| 331 | + | wx.DD_CHANGE_DIR) | |
| 332 | + | |
| 333 | + path = None | |
| 334 | + try: | |
| 335 | + if dlg.ShowModal() == wx.ID_OK: | |
| 336 | + # GetPath returns in unicode, if a path has non-ascii characters a | |
| 337 | + # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 | |
| 338 | + if sys.platform == "win32": | |
| 339 | + path = dlg.GetPath() | |
| 340 | + else: | |
| 341 | + path = dlg.GetPath().encode('utf-8') | |
| 342 | + | |
| 343 | + except(wx._core.PyAssertionError): #TODO: error win64 | |
| 344 | + if (dlg.GetPath()): | |
| 345 | + path = dlg.GetPath() | |
| 346 | + | |
| 347 | + if (sys.platform != 'darwin'): | |
| 348 | + if (path): | |
| 349 | + session.SetLastDicomFolder(path) | |
| 350 | + | |
| 351 | + # Only destroy a dialog after you're done with it. | |
| 352 | + dlg.Destroy() | |
| 353 | + os.chdir(current_dir) | |
| 354 | + return path | |
| 355 | + | |
| 356 | + | |
| 309 | 357 | def ShowSaveAsProjectDialog(default_filename=None): |
| 310 | 358 | current_dir = os.path.abspath(".") |
| 311 | 359 | dlg = wx.FileDialog(None, |
| ... | ... | @@ -1630,3 +1678,114 @@ class ReorientImageDialog(wx.Dialog): |
| 1630 | 1678 | Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT) |
| 1631 | 1679 | Publisher.sendMessage('Enable style', const.STATE_DEFAULT) |
| 1632 | 1680 | self.Destroy() |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
| 1684 | +class ImportBitmapParameters(wx.Dialog): | |
| 1685 | + | |
| 1686 | + def __init__(self): | |
| 1687 | + pre = wx.PreDialog() | |
| 1688 | + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Parameters"),size=wx.Size(380,230),\ | |
| 1689 | + style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) | |
| 1690 | + | |
| 1691 | + self.interval = 0 | |
| 1692 | + | |
| 1693 | + self.PostCreate(pre) | |
| 1694 | + | |
| 1695 | + self._init_gui() | |
| 1696 | + | |
| 1697 | + self.bind_evts() | |
| 1698 | + self.CenterOnScreen() | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + def _init_gui(self): | |
| 1702 | + | |
| 1703 | + | |
| 1704 | + p = wx.Panel(self, -1, style = wx.TAB_TRAVERSAL | |
| 1705 | + | wx.CLIP_CHILDREN | |
| 1706 | + | wx.FULL_REPAINT_ON_RESIZE) | |
| 1707 | + | |
| 1708 | + gbs_principal = self.gbs = wx.GridBagSizer(3,1) | |
| 1709 | + | |
| 1710 | + gbs = self.gbs = wx.GridBagSizer(4, 2) | |
| 1711 | + | |
| 1712 | + stx_name = wx.StaticText(p, -1, _(u"Project name:")) | |
| 1713 | + tx_name = self.tx_name = wx.TextCtrl(p, -1, "InVesalius Bitmap", size=wx.Size(220,-1)) | |
| 1714 | + | |
| 1715 | + stx_orientation = wx.StaticText(p, -1, _(u"Slices orientation:")) | |
| 1716 | + cb_orientation_options = [_(u'Axial'), _(u'Coronal'), _(u'Sagital')] | |
| 1717 | + cb_orientation = self.cb_orientation = wx.ComboBox(p, value="Axial", choices=cb_orientation_options,\ | |
| 1718 | + size=wx.Size(160,-1), style=wx.CB_DROPDOWN|wx.CB_READONLY) | |
| 1719 | + | |
| 1720 | + stx_spacing = wx.StaticText(p, -1, _(u"Spacing (mm):")) | |
| 1721 | + | |
| 1722 | + gbs.Add(stx_name, (0,0)) | |
| 1723 | + gbs.Add(tx_name, (0,1)) | |
| 1724 | + | |
| 1725 | + gbs.Add(stx_orientation, (1,0)) | |
| 1726 | + gbs.Add(cb_orientation, (1,1)) | |
| 1727 | + | |
| 1728 | + gbs.Add(stx_spacing, (2,0)) | |
| 1729 | + | |
| 1730 | + #--- spacing -------------- | |
| 1731 | + gbs_spacing = wx.GridBagSizer(2, 6) | |
| 1732 | + | |
| 1733 | + stx_spacing_x = stx_spacing_x = wx.StaticText(p, -1, _(u"X:")) | |
| 1734 | + fsp_spacing_x = self.fsp_spacing_x = FS.FloatSpin(p, -1, min_val=0,\ | |
| 1735 | + increment=0.25, value=1.0, digits=6) | |
| 1736 | + | |
| 1737 | + stx_spacing_y = stx_spacing_y = wx.StaticText(p, -1, _(u"Y:")) | |
| 1738 | + fsp_spacing_y = self.fsp_spacing_y = FS.FloatSpin(p, -1, min_val=0,\ | |
| 1739 | + increment=0.25, value=1.0, digits=6) | |
| 1740 | + | |
| 1741 | + stx_spacing_z = stx_spacing_z = wx.StaticText(p, -1, _(u"Z:")) | |
| 1742 | + fsp_spacing_z = self.fsp_spacing_z = FS.FloatSpin(p, -1, min_val=0,\ | |
| 1743 | + increment=0.25, value=1.0, digits=6) | |
| 1744 | + | |
| 1745 | + gbs_spacing.Add(stx_spacing_x, (0,0)) | |
| 1746 | + gbs_spacing.Add(fsp_spacing_x, (0,1)) | |
| 1747 | + | |
| 1748 | + gbs_spacing.Add(stx_spacing_y, (0,2)) | |
| 1749 | + gbs_spacing.Add(fsp_spacing_y, (0,3)) | |
| 1750 | + | |
| 1751 | + gbs_spacing.Add(stx_spacing_z, (0,4)) | |
| 1752 | + gbs_spacing.Add(fsp_spacing_z, (0,5)) | |
| 1753 | + | |
| 1754 | + #----- buttons ------------------------ | |
| 1755 | + gbs_button = wx.GridBagSizer(1, 4) | |
| 1756 | + | |
| 1757 | + btn_ok = self.btn_ok= wx.Button(p, wx.ID_OK) | |
| 1758 | + btn_ok.SetDefault() | |
| 1759 | + | |
| 1760 | + btn_cancel = wx.Button(p, wx.ID_CANCEL) | |
| 1761 | + | |
| 1762 | + gbs_button.Add(btn_cancel, (1,2)) | |
| 1763 | + gbs_button.Add(btn_ok, (1,3)) | |
| 1764 | + | |
| 1765 | + | |
| 1766 | + gbs_principal.Add(gbs, (0,0)) | |
| 1767 | + gbs_principal.Add(gbs_spacing, (1,0)) | |
| 1768 | + gbs_principal.Add(gbs_button, (2,0), flag = wx.ALIGN_RIGHT) | |
| 1769 | + | |
| 1770 | + box = wx.BoxSizer() | |
| 1771 | + box.Add(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) | |
| 1772 | + | |
| 1773 | + p.SetSizer(box) | |
| 1774 | + | |
| 1775 | + | |
| 1776 | + def bind_evts(self): | |
| 1777 | + self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk) | |
| 1778 | + | |
| 1779 | + def SetInterval(self, v): | |
| 1780 | + self.interval = v | |
| 1781 | + | |
| 1782 | + def OnOk(self, evt): | |
| 1783 | + self.Close() | |
| 1784 | + self.Destroy() | |
| 1785 | + | |
| 1786 | + values = [self.tx_name.GetValue(), self.cb_orientation.GetValue().upper(),\ | |
| 1787 | + self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\ | |
| 1788 | + self.fsp_spacing_z.GetValue(), self.interval] | |
| 1789 | + Publisher.sendMessage('Open bitmap files', values) | |
| 1790 | + | |
| 1791 | + | ... | ... |
invesalius/gui/frame.py
| ... | ... | @@ -36,6 +36,7 @@ import default_tasks as tasks |
| 36 | 36 | import default_viewers as viewers |
| 37 | 37 | import gui.dialogs as dlg |
| 38 | 38 | import import_panel as imp |
| 39 | +import import_bitmap_panel as imp_bmp | |
| 39 | 40 | import import_network_panel as imp_net |
| 40 | 41 | import project as prj |
| 41 | 42 | import session as ses |
| ... | ... | @@ -126,6 +127,7 @@ class Frame(wx.Frame): |
| 126 | 127 | sub(self._ShowImportPanel, 'Show import panel in frame') |
| 127 | 128 | #sub(self._ShowHelpMessage, 'Show help message') |
| 128 | 129 | sub(self._ShowImportNetwork, 'Show retrieve dicom panel') |
| 130 | + sub(self._ShowImportBitmap, 'Show import bitmap panel in frame') | |
| 129 | 131 | sub(self._ShowTask, 'Show task panel') |
| 130 | 132 | sub(self._UpdateAUI, 'Update AUI') |
| 131 | 133 | sub(self._Exit, 'Exit') |
| ... | ... | @@ -170,8 +172,14 @@ class Frame(wx.Frame): |
| 170 | 172 | # are shown, this should be hiden |
| 171 | 173 | caption = _("Preview medical data to be reconstructed") |
| 172 | 174 | aui_manager.AddPane(imp.Panel(self), wx.aui.AuiPaneInfo(). |
| 173 | - Name("Import").Centre().Hide(). | |
| 174 | - MaximizeButton(True).Floatable(True). | |
| 175 | + Name("Import").CloseButton(False).Centre().Hide(). | |
| 176 | + MaximizeButton(False).Floatable(True). | |
| 177 | + Caption(caption).CaptionVisible(True)) | |
| 178 | + | |
| 179 | + caption = _("Preview bitmap to be reconstructed") | |
| 180 | + aui_manager.AddPane(imp_bmp.Panel(self), wx.aui.AuiPaneInfo(). | |
| 181 | + Name("ImportBMP").CloseButton(False).Centre().Hide(). | |
| 182 | + MaximizeButton(False).Floatable(True). | |
| 175 | 183 | Caption(caption).CaptionVisible(True)) |
| 176 | 184 | |
| 177 | 185 | ncaption = _("Retrieve DICOM from PACS") |
| ... | ... | @@ -298,6 +306,9 @@ class Frame(wx.Frame): |
| 298 | 306 | Publisher.sendMessage("Set layout button full") |
| 299 | 307 | aui_manager = self.aui_manager |
| 300 | 308 | aui_manager.GetPane("Import").Show(0) |
| 309 | + | |
| 310 | + aui_manager.GetPane("ImportBMP").Show(0) | |
| 311 | + | |
| 301 | 312 | aui_manager.GetPane("Data").Show(1) |
| 302 | 313 | aui_manager.GetPane("Tasks").Show(1) |
| 303 | 314 | aui_manager.Update() |
| ... | ... | @@ -314,6 +325,18 @@ class Frame(wx.Frame): |
| 314 | 325 | aui_manager.GetPane("Import").Show(0) |
| 315 | 326 | aui_manager.Update() |
| 316 | 327 | |
| 328 | + def _ShowImportBitmap(self, evt_pubsub): | |
| 329 | + """ | |
| 330 | + Show viewers and task, hide import panel. | |
| 331 | + """ | |
| 332 | + Publisher.sendMessage("Set layout button full") | |
| 333 | + aui_manager = self.aui_manager | |
| 334 | + aui_manager.GetPane("ImportBMP").Show(1) | |
| 335 | + aui_manager.GetPane("Data").Show(0) | |
| 336 | + aui_manager.GetPane("Tasks").Show(0) | |
| 337 | + aui_manager.GetPane("Import").Show(0) | |
| 338 | + aui_manager.Update() | |
| 339 | + | |
| 317 | 340 | def _ShowHelpMessage(self, evt_pubsub): |
| 318 | 341 | aui_manager = self.aui_manager |
| 319 | 342 | pos = aui_manager.GetPane("Data").window.GetScreenPosition() |
| ... | ... | @@ -371,6 +394,8 @@ class Frame(wx.Frame): |
| 371 | 394 | self.ShowOpenProject() |
| 372 | 395 | elif id == const.ID_ANALYZE_IMPORT: |
| 373 | 396 | self.ShowAnalyzeImporter() |
| 397 | + elif id == const.ID_TIFF_JPG_PNG: | |
| 398 | + self.ShowBitmapImporter() | |
| 374 | 399 | elif id == const.ID_PROJECT_SAVE: |
| 375 | 400 | session = ses.Session() |
| 376 | 401 | if session.temp_item: |
| ... | ... | @@ -501,6 +526,12 @@ class Frame(wx.Frame): |
| 501 | 526 | """ |
| 502 | 527 | Publisher.sendMessage('Show analyze dialog', True) |
| 503 | 528 | |
| 529 | + def ShowBitmapImporter(self): | |
| 530 | + """ | |
| 531 | + Tiff, BMP, JPEG and PNG | |
| 532 | + """ | |
| 533 | + Publisher.sendMessage('Show bitmap dialog', True) | |
| 534 | + | |
| 504 | 535 | def FlipVolume(self, axis): |
| 505 | 536 | Publisher.sendMessage('Flip volume', axis) |
| 506 | 537 | Publisher.sendMessage('Reload actual slice') |
| ... | ... | @@ -581,6 +612,7 @@ class MenuBar(wx.MenuBar): |
| 581 | 612 | #Import Others Files |
| 582 | 613 | others_file_menu = wx.Menu() |
| 583 | 614 | others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze") |
| 615 | + others_file_menu.Append(const.ID_TIFF_JPG_PNG, "TIFF,BMP,JPG or PNG") | |
| 584 | 616 | |
| 585 | 617 | # FILE |
| 586 | 618 | file_menu = wx.Menu() | ... | ... |
| ... | ... | @@ -0,0 +1,484 @@ |
| 1 | +#-------------------------------------------------------------------------- | |
| 2 | +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas | |
| 3 | +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer | |
| 4 | +# Homepage: http://www.softwarepublico.gov.br | |
| 5 | +# Contact: invesalius@cti.gov.br | |
| 6 | +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) | |
| 7 | +#-------------------------------------------------------------------------- | |
| 8 | +# Este programa e software livre; voce pode redistribui-lo e/ou | |
| 9 | +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme | |
| 10 | +# publicada pela Free Software Foundation; de acordo com a versao 2 | |
| 11 | +# da Licenca. | |
| 12 | +# | |
| 13 | +# Este programa eh distribuido na expectativa de ser util, mas SEM | |
| 14 | +# QUALQUER GARANTIA; sem mesmo a garantia implicita de | |
| 15 | +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM | |
| 16 | +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais | |
| 17 | +# detalhes. | |
| 18 | +#-------------------------------------------------------------------------- | |
| 19 | +import wx | |
| 20 | +import wx.gizmos as gizmos | |
| 21 | +from wx.lib.pubsub import pub as Publisher | |
| 22 | +import wx.lib.splitter as spl | |
| 23 | + | |
| 24 | +import constants as const | |
| 25 | +import gui.dialogs as dlg | |
| 26 | +import bitmap_preview_panel as bpp | |
| 27 | +import reader.dicom_grouper as dcm | |
| 28 | +from dialogs import ImportBitmapParameters | |
| 29 | + | |
| 30 | +myEVT_SELECT_SERIE = wx.NewEventType() | |
| 31 | +EVT_SELECT_SERIE = wx.PyEventBinder(myEVT_SELECT_SERIE, 1) | |
| 32 | + | |
| 33 | +myEVT_SELECT_SLICE = wx.NewEventType() | |
| 34 | +EVT_SELECT_SLICE = wx.PyEventBinder(myEVT_SELECT_SLICE, 1) | |
| 35 | + | |
| 36 | +myEVT_SELECT_PATIENT = wx.NewEventType() | |
| 37 | +EVT_SELECT_PATIENT = wx.PyEventBinder(myEVT_SELECT_PATIENT, 1) | |
| 38 | + | |
| 39 | +myEVT_SELECT_SERIE_TEXT = wx.NewEventType() | |
| 40 | +EVT_SELECT_SERIE_TEXT = wx.PyEventBinder(myEVT_SELECT_SERIE_TEXT, 1) | |
| 41 | + | |
| 42 | +class SelectEvent(wx.PyCommandEvent): | |
| 43 | + def __init__(self , evtType, id): | |
| 44 | + super(SelectEvent, self).__init__(evtType, id) | |
| 45 | + | |
| 46 | + def GetSelectID(self): | |
| 47 | + return self.SelectedID | |
| 48 | + | |
| 49 | + def SetSelectedID(self, id): | |
| 50 | + self.SelectedID = id | |
| 51 | + | |
| 52 | + def GetItemData(self): | |
| 53 | + return self.data | |
| 54 | + | |
| 55 | + def SetItemData(self, data): | |
| 56 | + self.data = data | |
| 57 | + | |
| 58 | + | |
| 59 | +class Panel(wx.Panel): | |
| 60 | + def __init__(self, parent): | |
| 61 | + wx.Panel.__init__(self, parent, pos=wx.Point(5, 5))#, | |
| 62 | + #size=wx.Size(280, 656)) | |
| 63 | + | |
| 64 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 65 | + sizer.Add(InnerPanel(self), 1, wx.EXPAND|wx.GROW|wx.ALL, 5) | |
| 66 | + | |
| 67 | + self.SetSizer(sizer) | |
| 68 | + sizer.Fit(self) | |
| 69 | + | |
| 70 | + self.Layout() | |
| 71 | + self.Update() | |
| 72 | + self.SetAutoLayout(1) | |
| 73 | + | |
| 74 | + | |
| 75 | +# Inner fold panel | |
| 76 | +class InnerPanel(wx.Panel): | |
| 77 | + def __init__(self, parent): | |
| 78 | + wx.Panel.__init__(self, parent, pos=wx.Point(5, 5))#, | |
| 79 | + #size=wx.Size(680, 656)) | |
| 80 | + | |
| 81 | + self.patients = [] | |
| 82 | + self.first_image_selection = None | |
| 83 | + self.last_image_selection = None | |
| 84 | + self._init_ui() | |
| 85 | + self._bind_events() | |
| 86 | + self._bind_pubsubevt() | |
| 87 | + | |
| 88 | + def _init_ui(self): | |
| 89 | + splitter = spl.MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE) | |
| 90 | + splitter.SetOrientation(wx.VERTICAL) | |
| 91 | + self.splitter = splitter | |
| 92 | + | |
| 93 | + panel = wx.Panel(self) | |
| 94 | + self.btn_cancel = wx.Button(panel, wx.ID_CANCEL) | |
| 95 | + self.btn_ok = wx.Button(panel, wx.ID_OK, _("Import")) | |
| 96 | + | |
| 97 | + btnsizer = wx.StdDialogButtonSizer() | |
| 98 | + btnsizer.AddButton(self.btn_ok) | |
| 99 | + btnsizer.AddButton(self.btn_cancel) | |
| 100 | + btnsizer.Realize() | |
| 101 | + | |
| 102 | + self.combo_interval = wx.ComboBox(panel, -1, "", choices=const.IMPORT_INTERVAL, | |
| 103 | + style=wx.CB_DROPDOWN|wx.CB_READONLY) | |
| 104 | + self.combo_interval.SetSelection(0) | |
| 105 | + | |
| 106 | + inner_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 107 | + inner_sizer.AddSizer(btnsizer, 0, wx.LEFT|wx.TOP, 5) | |
| 108 | + inner_sizer.Add(self.combo_interval, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5) | |
| 109 | + panel.SetSizer(inner_sizer) | |
| 110 | + inner_sizer.Fit(panel) | |
| 111 | + | |
| 112 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 113 | + sizer.Add(splitter, 20, wx.EXPAND) | |
| 114 | + sizer.Add(panel, 0, wx.EXPAND|wx.LEFT, 90) | |
| 115 | + | |
| 116 | + self.text_panel = TextPanel(splitter) | |
| 117 | + splitter.AppendWindow(self.text_panel, 250) | |
| 118 | + | |
| 119 | + self.image_panel = ImagePanel(splitter) | |
| 120 | + splitter.AppendWindow(self.image_panel, 250) | |
| 121 | + | |
| 122 | + self.SetSizer(sizer) | |
| 123 | + sizer.Fit(self) | |
| 124 | + | |
| 125 | + self.Layout() | |
| 126 | + self.Update() | |
| 127 | + self.SetAutoLayout(1) | |
| 128 | + | |
| 129 | + def _bind_pubsubevt(self): | |
| 130 | + Publisher.subscribe(self.ShowBitmapPreview, "Load import bitmap panel") | |
| 131 | + Publisher.subscribe(self.GetSelectedImages ,"Selected Import Images") | |
| 132 | + | |
| 133 | + def ShowBitmapPreview(self, pubsub_evt): | |
| 134 | + data = pubsub_evt.data | |
| 135 | + #self.patients.extend(dicom_groups) | |
| 136 | + self.text_panel.Populate(data) | |
| 137 | + | |
| 138 | + def GetSelectedImages(self, pubsub_evt): | |
| 139 | + self.first_image_selection = pubsub_evt.data[0] | |
| 140 | + self.last_image_selection = pubsub_evt.data[1] | |
| 141 | + | |
| 142 | + def _bind_events(self): | |
| 143 | + self.Bind(EVT_SELECT_SERIE, self.OnSelectSerie) | |
| 144 | + self.Bind(EVT_SELECT_SLICE, self.OnSelectSlice) | |
| 145 | + self.Bind(EVT_SELECT_PATIENT, self.OnSelectPatient) | |
| 146 | + self.btn_ok.Bind(wx.EVT_BUTTON, self.OnClickOk) | |
| 147 | + self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnClickCancel) | |
| 148 | + self.text_panel.Bind(EVT_SELECT_SERIE_TEXT, self.OnDblClickTextPanel) | |
| 149 | + | |
| 150 | + def OnSelectSerie(self, evt): | |
| 151 | + #patient_id, serie_number = evt.GetSelectID() | |
| 152 | + #self.text_panel.SelectSerie(evt.GetSelectID()) | |
| 153 | + #for patient in self.patients: | |
| 154 | + # if patient_id == patient.GetDicomSample().patient.id: | |
| 155 | + # for group in patient.GetGroups(): | |
| 156 | + # if serie_number == group.GetDicomSample().acquisition.serie_number: | |
| 157 | + # self.image_panel.SetSerie(group) | |
| 158 | + | |
| 159 | + pass | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + def OnSelectSlice(self, evt): | |
| 164 | + pass | |
| 165 | + | |
| 166 | + def OnSelectPatient(self, evt): | |
| 167 | + pass | |
| 168 | + | |
| 169 | + def OnDblClickTextPanel(self, evt): | |
| 170 | + group = evt.GetItemData() | |
| 171 | + self.LoadDicom(group) | |
| 172 | + | |
| 173 | + def OnClickOk(self, evt): | |
| 174 | + parm = dlg.ImportBitmapParameters() | |
| 175 | + parm.SetInterval(self.combo_interval.GetSelection()) | |
| 176 | + parm.ShowModal() | |
| 177 | + | |
| 178 | + group = self.text_panel.GetSelection() | |
| 179 | + if group: | |
| 180 | + self.LoadDicom(group) | |
| 181 | + | |
| 182 | + def OnClickCancel(self, evt): | |
| 183 | + Publisher.sendMessage("Cancel DICOM load") | |
| 184 | + | |
| 185 | + def LoadDicom(self, group): | |
| 186 | + #interval = self.combo_interval.GetSelection() | |
| 187 | + #if not isinstance(group, dcm.DicomGroup): | |
| 188 | + # group = max(group.GetGroups(), key=lambda g: g.nslices) | |
| 189 | + | |
| 190 | + #slice_amont = group.nslices | |
| 191 | + #if (self.first_image_selection != None) and (self.first_image_selection != self.last_image_selection): | |
| 192 | + # slice_amont = (self.last_image_selection) - self.first_image_selection | |
| 193 | + # slice_amont += 1 | |
| 194 | + # if slice_amont == 0: | |
| 195 | + # slice_amont = group.nslices | |
| 196 | + | |
| 197 | + #nslices_result = slice_amont / (interval + 1) | |
| 198 | + #if (nslices_result > 1): | |
| 199 | + # Publisher.sendMessage('Open DICOM group', (group, interval, | |
| 200 | + # [self.first_image_selection, self.last_image_selection])) | |
| 201 | + #else: | |
| 202 | + # dlg.MissingFilesForReconstruction() | |
| 203 | + pass | |
| 204 | + | |
| 205 | + | |
| 206 | +class TextPanel(wx.Panel): | |
| 207 | + def __init__(self, parent): | |
| 208 | + wx.Panel.__init__(self, parent, -1) | |
| 209 | + | |
| 210 | + self._selected_by_user = True | |
| 211 | + self.idserie_treeitem = {} | |
| 212 | + self.treeitem_idpatient = {} | |
| 213 | + | |
| 214 | + self.__init_gui() | |
| 215 | + self.__bind_events_wx() | |
| 216 | + self.__bind_pubsub_evt() | |
| 217 | + | |
| 218 | + def __bind_pubsub_evt(self): | |
| 219 | + Publisher.subscribe(self.SelectSeries, 'Select series in import panel') | |
| 220 | + | |
| 221 | + def __bind_events_wx(self): | |
| 222 | + self.Bind(wx.EVT_SIZE, self.OnSize) | |
| 223 | + | |
| 224 | + def __init_gui(self): | |
| 225 | + tree = gizmos.TreeListCtrl(self, -1, style = | |
| 226 | + wx.TR_DEFAULT_STYLE | |
| 227 | + | wx.TR_HIDE_ROOT | |
| 228 | + | wx.TR_ROW_LINES | |
| 229 | + | wx.TR_COLUMN_LINES | |
| 230 | + | wx.TR_FULL_ROW_HIGHLIGHT | |
| 231 | + | wx.TR_SINGLE | |
| 232 | + | wx.TR_HIDE_ROOT | |
| 233 | + ) | |
| 234 | + | |
| 235 | + | |
| 236 | + tree.AddColumn(_("Path")) | |
| 237 | + tree.AddColumn(_("Type")) | |
| 238 | + tree.AddColumn(_("Width x Height")) | |
| 239 | + | |
| 240 | + | |
| 241 | + tree.SetMainColumn(0) | |
| 242 | + tree.SetColumnWidth(0, 880) | |
| 243 | + tree.SetColumnWidth(1, 60) | |
| 244 | + tree.SetColumnWidth(2, 130) | |
| 245 | + | |
| 246 | + self.root = tree.AddRoot(_("InVesalius Database")) | |
| 247 | + self.tree = tree | |
| 248 | + | |
| 249 | + def SelectSeries(self, pubsub_evt): | |
| 250 | + group_index = pubsub_evt.data | |
| 251 | + | |
| 252 | + def Populate(self, data): | |
| 253 | + tree = self.tree | |
| 254 | + for value in data: | |
| 255 | + parent = tree.AppendItem(self.root, value[0]) | |
| 256 | + self.tree.SetItemText(parent, value[2], 1) | |
| 257 | + self.tree.SetItemText(parent, value[5], 2) | |
| 258 | + | |
| 259 | + tree.Expand(self.root) | |
| 260 | + #tree.SelectItem(parent_select) | |
| 261 | + tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivate) | |
| 262 | + tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged) | |
| 263 | + | |
| 264 | + Publisher.sendMessage('Load bitmap into import panel', data) | |
| 265 | + | |
| 266 | + | |
| 267 | + def OnSelChanged(self, evt): | |
| 268 | + item = self.tree.GetSelection() | |
| 269 | + if self._selected_by_user: | |
| 270 | + group = self.tree.GetItemPyData(item) | |
| 271 | + if isinstance(group, dcm.DicomGroup): | |
| 272 | + Publisher.sendMessage('Load group into import panel', | |
| 273 | + group) | |
| 274 | + | |
| 275 | + elif isinstance(group, dcm.PatientGroup): | |
| 276 | + id = group.GetDicomSample().patient.id | |
| 277 | + my_evt = SelectEvent(myEVT_SELECT_PATIENT, self.GetId()) | |
| 278 | + my_evt.SetSelectedID(id) | |
| 279 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 280 | + | |
| 281 | + Publisher.sendMessage('Load bitmap into import panel', | |
| 282 | + | |
| 283 | + group) | |
| 284 | + else: | |
| 285 | + parent_id = self.tree.GetItemParent(item) | |
| 286 | + self.tree.Expand(parent_id) | |
| 287 | + evt.Skip() | |
| 288 | + | |
| 289 | + def OnActivate(self, evt): | |
| 290 | + item = evt.GetItem() | |
| 291 | + group = self.tree.GetItemPyData(item) | |
| 292 | + my_evt = SelectEvent(myEVT_SELECT_SERIE_TEXT, self.GetId()) | |
| 293 | + my_evt.SetItemData(group) | |
| 294 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 295 | + | |
| 296 | + def OnSize(self, evt): | |
| 297 | + self.tree.SetSize(self.GetSize()) | |
| 298 | + | |
| 299 | + def SelectSerie(self, serie): | |
| 300 | + self._selected_by_user = False | |
| 301 | + item = self.idserie_treeitem[serie] | |
| 302 | + self.tree.SelectItem(item) | |
| 303 | + self._selected_by_user = True | |
| 304 | + | |
| 305 | + def GetSelection(self): | |
| 306 | + """Get selected item""" | |
| 307 | + item = self.tree.GetSelection() | |
| 308 | + group = self.tree.GetItemPyData(item) | |
| 309 | + return group | |
| 310 | + | |
| 311 | + | |
| 312 | +class ImagePanel(wx.Panel): | |
| 313 | + def __init__(self, parent): | |
| 314 | + wx.Panel.__init__(self, parent, -1) | |
| 315 | + self._init_ui() | |
| 316 | + self._bind_events() | |
| 317 | + | |
| 318 | + def _init_ui(self): | |
| 319 | + splitter = spl.MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE) | |
| 320 | + splitter.SetOrientation(wx.HORIZONTAL) | |
| 321 | + self.splitter = splitter | |
| 322 | + | |
| 323 | + splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 324 | + | |
| 325 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 326 | + sizer.Add(splitter, 1, wx.EXPAND) | |
| 327 | + self.SetSizer(sizer) | |
| 328 | + | |
| 329 | + self.text_panel = SeriesPanel(splitter) | |
| 330 | + splitter.AppendWindow(self.text_panel, 600) | |
| 331 | + | |
| 332 | + self.image_panel = SlicePanel(splitter) | |
| 333 | + splitter.AppendWindow(self.image_panel, 250) | |
| 334 | + | |
| 335 | + self.SetSizer(sizer) | |
| 336 | + sizer.Fit(self) | |
| 337 | + | |
| 338 | + self.Layout() | |
| 339 | + self.Update() | |
| 340 | + self.SetAutoLayout(1) | |
| 341 | + | |
| 342 | + def _bind_events(self): | |
| 343 | + self.text_panel.Bind(EVT_SELECT_SERIE, self.OnSelectSerie) | |
| 344 | + self.text_panel.Bind(EVT_SELECT_SLICE, self.OnSelectSlice) | |
| 345 | + | |
| 346 | + def OnSelectSerie(self, evt): | |
| 347 | + evt.Skip() | |
| 348 | + | |
| 349 | + def OnSelectSlice(self, evt): | |
| 350 | + self.image_panel.bitmap_preview.ShowSlice(evt.GetSelectID()) | |
| 351 | + evt.Skip() | |
| 352 | + | |
| 353 | + def SetSerie(self, serie): | |
| 354 | + self.image_panel.bitmap_preview.SetDicomGroup(serie) | |
| 355 | + | |
| 356 | + | |
| 357 | +class SeriesPanel(wx.Panel): | |
| 358 | + def __init__(self, parent): | |
| 359 | + wx.Panel.__init__(self, parent, -1) | |
| 360 | + #self.SetBackgroundColour((0,0,0)) | |
| 361 | + | |
| 362 | + self.thumbnail_preview = bpp.BitmapPreviewSeries(self) | |
| 363 | + #self.bitmap_preview = bpp.BitmapPreviewSlice(self) | |
| 364 | + #self.bitmap_preview.Show(0) | |
| 365 | + | |
| 366 | + | |
| 367 | + self.sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 368 | + self.sizer.Add(self.thumbnail_preview, 1, wx.EXPAND | wx.ALL, 5) | |
| 369 | + #self.sizer.Add(self.bitmap_preview, 1, wx.EXPAND | wx.ALL, 5) | |
| 370 | + self.sizer.Fit(self) | |
| 371 | + | |
| 372 | + self.SetSizer(self.sizer) | |
| 373 | + | |
| 374 | + self.Layout() | |
| 375 | + self.Update() | |
| 376 | + self.SetAutoLayout(1) | |
| 377 | + | |
| 378 | + self.__bind_evt() | |
| 379 | + self._bind_gui_evt() | |
| 380 | + | |
| 381 | + def __bind_evt(self): | |
| 382 | + #Publisher.subscribe(self.ShowDicomSeries, 'Load bitmap preview') | |
| 383 | + #Publisher.subscribe(self.SetDicomSeries, 'Load group into import panel') | |
| 384 | + Publisher.subscribe(self.SetBitmapFiles, 'Load bitmap into import panel') | |
| 385 | + | |
| 386 | + def _bind_gui_evt(self): | |
| 387 | + self.thumbnail_preview.Bind(bpp.EVT_CLICK_SERIE, self.OnSelectSerie) | |
| 388 | + #self.bitmap_preview.Bind(bpp.EVT_CLICK_SLICE, self.OnSelectSlice) | |
| 389 | + | |
| 390 | + #def SetDicomSeries(self, pubsub_evt): | |
| 391 | + # group = pubsub_evt.data | |
| 392 | + # self.bitmap_preview.SetDicomGroup(group) | |
| 393 | + # self.bitmap_preview.Show(1) | |
| 394 | + # self.thumbnail_preview.Show(0) | |
| 395 | + # self.sizer.Layout() | |
| 396 | + # self.Update() | |
| 397 | + | |
| 398 | + def GetSelectedImagesRange(self): | |
| 399 | + return [self.bitmap_preview.first_selected, self.dicom_preview_last_selection] | |
| 400 | + | |
| 401 | + def SetBitmapFiles(self, pubsub_evt): | |
| 402 | + | |
| 403 | + | |
| 404 | + bitmap = pubsub_evt.data | |
| 405 | + #self.bitmap_preview.Show(0) | |
| 406 | + self.thumbnail_preview.Show(1) | |
| 407 | + | |
| 408 | + self.thumbnail_preview.SetBitmapFiles(bitmap) | |
| 409 | + #self.bitmap_preview.SetPatientGroups(patient) | |
| 410 | + | |
| 411 | + self.Update() | |
| 412 | + | |
| 413 | + def OnSelectSerie(self, evt): | |
| 414 | + data = evt.GetItemData() | |
| 415 | + | |
| 416 | + my_evt = SelectEvent(myEVT_SELECT_SERIE, self.GetId()) | |
| 417 | + my_evt.SetSelectedID(evt.GetSelectID()) | |
| 418 | + my_evt.SetItemData(evt.GetItemData()) | |
| 419 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 420 | + | |
| 421 | + #self.bitmap_preview.SetDicomGroup(data) | |
| 422 | + #self.bitmap_preview.Show(1) | |
| 423 | + #self.thumbnail_preview.Show(0) | |
| 424 | + self.sizer.Layout() | |
| 425 | + self.Show() | |
| 426 | + self.Update() | |
| 427 | + | |
| 428 | + def OnSelectSlice(self, evt): | |
| 429 | + my_evt = SelectEvent(myEVT_SELECT_SLICE, self.GetId()) | |
| 430 | + my_evt.SetSelectedID(evt.GetSelectID()) | |
| 431 | + my_evt.SetItemData(evt.GetItemData()) | |
| 432 | + self.GetEventHandler().ProcessEvent(my_evt) | |
| 433 | + | |
| 434 | + | |
| 435 | + #def ShowDicomSeries(self, pubsub_evt): | |
| 436 | + # patient = pubsub_evt.data | |
| 437 | + # if isinstance(patient, dcm.PatientGroup): | |
| 438 | + # self.thumbnail_preview.SetPatientGroups(patient) | |
| 439 | + # self.bitmap_preview.SetPatientGroups(patient) | |
| 440 | + | |
| 441 | + | |
| 442 | +class SlicePanel(wx.Panel): | |
| 443 | + def __init__(self, parent): | |
| 444 | + wx.Panel.__init__(self, parent, -1) | |
| 445 | + self.__init_gui() | |
| 446 | + self.__bind_evt() | |
| 447 | + | |
| 448 | + def __bind_evt(self): | |
| 449 | + #Publisher.subscribe(self.ShowDicomSeries, 'Load bitmap preview') | |
| 450 | + #Publisher.subscribe(self.SetDicomSeries, 'Load group into import panel') | |
| 451 | + Publisher.subscribe(self.SetBitmapFiles, 'Load bitmap into import panel') | |
| 452 | + | |
| 453 | + def __init_gui(self): | |
| 454 | + self.SetBackgroundColour((255,255,255)) | |
| 455 | + self.bitmap_preview = bpp.SingleImagePreview(self) | |
| 456 | + | |
| 457 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 458 | + sizer.Add(self.bitmap_preview, 1, wx.GROW|wx.EXPAND) | |
| 459 | + sizer.Fit(self) | |
| 460 | + self.SetSizer(sizer) | |
| 461 | + self.Layout() | |
| 462 | + self.Update() | |
| 463 | + self.SetAutoLayout(1) | |
| 464 | + self.sizer = sizer | |
| 465 | + | |
| 466 | + def SetBitmapFiles(self, pubsub_evt): | |
| 467 | + data = pubsub_evt.data | |
| 468 | + self.bitmap_preview.SetBitmapFiles(data) | |
| 469 | + self.sizer.Layout() | |
| 470 | + self.Update() | |
| 471 | + | |
| 472 | + #def SetDicomSeries(self, evt): | |
| 473 | + # group = evt.data | |
| 474 | + # self.bitmap_preview.SetDicomGroup(group) | |
| 475 | + # self.sizer.Layout() | |
| 476 | + # self.Update() | |
| 477 | + | |
| 478 | + #def ShowDicomSeries(self, pubsub_evt): | |
| 479 | + # patient = pubsub_evt.data | |
| 480 | + # group = patient.GetGroups()[0] | |
| 481 | + # self.bitmap_preview.SetDicomGroup(group) | |
| 482 | + # self.sizer.Layout() | |
| 483 | + # self.Update() | |
| 484 | + | ... | ... |
invesalius/gui/task_slice.py
| ... | ... | @@ -524,6 +524,7 @@ class MaskProperties(wx.Panel): |
| 524 | 524 | self.bind_evt_gradient = False |
| 525 | 525 | self.gradient.SetMinValue(thresh_min) |
| 526 | 526 | self.gradient.SetMaxValue(thresh_max) |
| 527 | + print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", thresh_min, thresh_max | |
| 527 | 528 | self.bind_evt_gradient = True |
| 528 | 529 | thresh = (thresh_min, thresh_max) |
| 529 | 530 | if thresh in Project().threshold_modes.values(): | ... | ... |
invesalius/presets.py
| ... | ... | @@ -73,12 +73,13 @@ class Presets(): |
| 73 | 73 | def UpdateThresholdModes(self, evt): |
| 74 | 74 | |
| 75 | 75 | thresh_min, thresh_max = evt.data |
| 76 | - | |
| 77 | 76 | presets_list = (self.thresh_ct, self.thresh_mri) |
| 78 | 77 | |
| 79 | 78 | for presets in presets_list: |
| 80 | 79 | for key in presets: |
| 81 | 80 | (t_min, t_max) = presets[key] |
| 81 | + | |
| 82 | + | |
| 82 | 83 | if (t_min is None) or (t_max is None): # setting custom preset |
| 83 | 84 | t_min = thresh_min |
| 84 | 85 | t_max = thresh_max |
| ... | ... | @@ -93,7 +94,7 @@ class Presets(): |
| 93 | 94 | t_min = thresh_min |
| 94 | 95 | if (t_max < thresh_min): |
| 95 | 96 | t_max = thresh_max |
| 96 | - | |
| 97 | + | |
| 97 | 98 | presets[key] = (t_min, t_max) |
| 98 | 99 | |
| 99 | 100 | Publisher.sendMessage('Update threshold limits', (thresh_min, | ... | ... |
| ... | ... | @@ -0,0 +1,389 @@ |
| 1 | +#-------------------------------------------------------------------------- | |
| 2 | +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas | |
| 3 | +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer | |
| 4 | +# Homepage: http://www.softwarepublico.gov.br | |
| 5 | +# Contact: invesalius@cti.gov.br | |
| 6 | +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) | |
| 7 | +#-------------------------------------------------------------------------- | |
| 8 | +# Este programa e software livre; voce pode redistribui-lo e/ou | |
| 9 | +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme | |
| 10 | +# publicada pela Free Software Foundation; de acordo com a versao 2 | |
| 11 | +# da Licenca. | |
| 12 | +# | |
| 13 | +# Este programa eh distribuido na expectativa de ser util, mas SEM | |
| 14 | +# QUALQUER GARANTIA; sem mesmo a garantia implicita de | |
| 15 | +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM | |
| 16 | +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais | |
| 17 | +# detalhes. | |
| 18 | +#-------------------------------------------------------------------------- | |
| 19 | +import os | |
| 20 | +import Queue | |
| 21 | +import threading | |
| 22 | +import tempfile | |
| 23 | +import sys | |
| 24 | +import vtk | |
| 25 | +import re | |
| 26 | +import constants as const | |
| 27 | +import wx | |
| 28 | + | |
| 29 | +from wx.lib.pubsub import pub as Publisher | |
| 30 | +from multiprocessing import cpu_count | |
| 31 | + | |
| 32 | +from vtk.util import numpy_support | |
| 33 | +from scipy import misc | |
| 34 | +import numpy | |
| 35 | +import imghdr | |
| 36 | + | |
| 37 | +from data import converters | |
| 38 | + | |
| 39 | +#flag to control vtk error in read files | |
| 40 | +no_error = True | |
| 41 | +vtk_error = False | |
| 42 | + | |
| 43 | +class Singleton: | |
| 44 | + | |
| 45 | + def __init__(self,klass): | |
| 46 | + self.klass = klass | |
| 47 | + self.instance = None | |
| 48 | + | |
| 49 | + def __call__(self,*args,**kwds): | |
| 50 | + if self.instance == None: | |
| 51 | + self.instance = self.klass(*args,**kwds) | |
| 52 | + return self.instance | |
| 53 | + | |
| 54 | +@Singleton | |
| 55 | +class BitmapData: | |
| 56 | + | |
| 57 | + def __init__(self): | |
| 58 | + self.data = None | |
| 59 | + | |
| 60 | + def GetData(self): | |
| 61 | + return self.data | |
| 62 | + | |
| 63 | + def SetData(self, data): | |
| 64 | + self.data = data | |
| 65 | + | |
| 66 | + def GetOnlyBitmapPath(self): | |
| 67 | + paths = [item[0] for item in self.data] | |
| 68 | + return paths | |
| 69 | + | |
| 70 | + def GetFirstBitmapSize(self): | |
| 71 | + return (self.data[0][3], self.data[0][4]) | |
| 72 | + | |
| 73 | + def IsAllBitmapSameSize(self): | |
| 74 | + sizes = [item[5] for item in self.data] | |
| 75 | + | |
| 76 | + k = {} | |
| 77 | + for v in sizes: | |
| 78 | + k[v] = '' | |
| 79 | + | |
| 80 | + if len(k.keys()) > 1: | |
| 81 | + return False | |
| 82 | + else: | |
| 83 | + return True | |
| 84 | + | |
| 85 | + def GetFirstPixelSize(self): | |
| 86 | + | |
| 87 | + path = self.data[0][0] | |
| 88 | + size = ReadBitmap(path).dtype.itemsize * 8 | |
| 89 | + | |
| 90 | + return size | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | +class BitmapFiles: | |
| 95 | + | |
| 96 | + def __init__(self): | |
| 97 | + self.bitmapfiles = [] | |
| 98 | + | |
| 99 | + def Add(self, bmp): | |
| 100 | + self.bitmapfiles.append(bmp) | |
| 101 | + | |
| 102 | + def Sort(self, x): | |
| 103 | + | |
| 104 | + c_re = re.compile('\d+') | |
| 105 | + | |
| 106 | + if len(c_re.findall(x[0])) > 0: | |
| 107 | + return c_re.findall(x[0])[-1] | |
| 108 | + else: | |
| 109 | + return '0' | |
| 110 | + | |
| 111 | + def GetValues(self): | |
| 112 | + bmpfile = self.bitmapfiles | |
| 113 | + bmpfile.sort(key = self.Sort) | |
| 114 | + | |
| 115 | + bmp_data = BitmapData() | |
| 116 | + bmp_data.data = bmpfile | |
| 117 | + | |
| 118 | + return bmpfile | |
| 119 | + | |
| 120 | +class LoadBitmap: | |
| 121 | + | |
| 122 | + def __init__(self, bmp_file, filepath): | |
| 123 | + self.bmp_file = bmp_file | |
| 124 | + if sys.platform == 'win32': | |
| 125 | + self.filepath = filepath.encode(utils.get_system_encoding()) | |
| 126 | + else: | |
| 127 | + self.filepath = filepath | |
| 128 | + | |
| 129 | + self.run() | |
| 130 | + | |
| 131 | + def run(self): | |
| 132 | + global vtk_error | |
| 133 | + | |
| 134 | + #----- verify extension ------------------ | |
| 135 | + #ex = self.filepath.split('.')[-1] | |
| 136 | + extension = VerifyDataType(self.filepath) | |
| 137 | + | |
| 138 | + file_name = self.filepath.split(os.path.sep)[-1] | |
| 139 | + | |
| 140 | + #if extension == 'bmp': | |
| 141 | + # reader = vtk.vtkBMPReader() | |
| 142 | + n_array = ReadBitmap(self.filepath) | |
| 143 | + | |
| 144 | + if not(isinstance(n_array, numpy.ndarray)): | |
| 145 | + return False | |
| 146 | + | |
| 147 | + image = converters.to_vtk(n_array, spacing=(1,1,1),\ | |
| 148 | + slice_number=1, orientation="AXIAL") | |
| 149 | + | |
| 150 | + | |
| 151 | + #reader.SetFileName(self.filepath) | |
| 152 | + #reader.Update() | |
| 153 | + | |
| 154 | + dim = image.GetDimensions() | |
| 155 | + x = dim[0] | |
| 156 | + y = dim[1] | |
| 157 | + | |
| 158 | + img = vtk.vtkImageResample() | |
| 159 | + img.SetInputData(image) | |
| 160 | + img.SetAxisMagnificationFactor ( 0, 0.25 ) | |
| 161 | + img.SetAxisMagnificationFactor ( 1, 0.25 ) | |
| 162 | + img.SetAxisMagnificationFactor ( 2, 1 ) | |
| 163 | + img.Update() | |
| 164 | + | |
| 165 | + tp = img.GetOutput().GetScalarTypeAsString() | |
| 166 | + | |
| 167 | + image_copy = vtk.vtkImageData() | |
| 168 | + image_copy.DeepCopy(img.GetOutput()) | |
| 169 | + | |
| 170 | + thumbnail_path = tempfile.mktemp() | |
| 171 | + | |
| 172 | + write_png = vtk.vtkPNGWriter() | |
| 173 | + write_png.SetInputConnection(img.GetOutputPort()) | |
| 174 | + write_png.AddObserver("WarningEvent", VtkErrorPNGWriter) | |
| 175 | + write_png.SetFileName(thumbnail_path) | |
| 176 | + write_png.Write() | |
| 177 | + | |
| 178 | + if vtk_error: | |
| 179 | + img = vtk.vtkImageCast() | |
| 180 | + img.SetInputData(image_copy) | |
| 181 | + img.SetOutputScalarTypeToUnsignedShort() | |
| 182 | + #img.SetClampOverflow(1) | |
| 183 | + img.Update() | |
| 184 | + | |
| 185 | + write_png = vtk.vtkPNGWriter() | |
| 186 | + write_png.SetInputConnection(img.GetOutputPort()) | |
| 187 | + write_png.SetFileName(thumbnail_path) | |
| 188 | + write_png.Write() | |
| 189 | + | |
| 190 | + vtk_error = False | |
| 191 | + | |
| 192 | + id = wx.NewId() | |
| 193 | + | |
| 194 | + bmp_item = [self.filepath, thumbnail_path, extension, x, y,\ | |
| 195 | + str(x) + ' x ' + str(y), file_name, id] | |
| 196 | + self.bmp_file.Add(bmp_item) | |
| 197 | + | |
| 198 | + | |
| 199 | +def yGetBitmaps(directory, recursive=True, gui=True): | |
| 200 | + """ | |
| 201 | + Return all full paths to DICOM files inside given directory. | |
| 202 | + """ | |
| 203 | + nfiles = 0 | |
| 204 | + # Find total number of files | |
| 205 | + if recursive: | |
| 206 | + for dirpath, dirnames, filenames in os.walk(directory): | |
| 207 | + nfiles += len(filenames) | |
| 208 | + else: | |
| 209 | + dirpath, dirnames, filenames = os.walk(directory) | |
| 210 | + nfiles = len(filenames) | |
| 211 | + | |
| 212 | + | |
| 213 | + counter = 0 | |
| 214 | + bmp_file = BitmapFiles() | |
| 215 | + | |
| 216 | + # Retrieve only TIFF, BMP, JPEG and PNG files | |
| 217 | + if recursive: | |
| 218 | + for dirpath, dirnames, filenames in os.walk(directory): | |
| 219 | + for name in filenames: | |
| 220 | + filepath = os.path.join(dirpath, name) | |
| 221 | + counter += 1 | |
| 222 | + if gui: | |
| 223 | + yield (counter,nfiles) | |
| 224 | + #LoadDicom(grouper, filepath) | |
| 225 | + LoadBitmap(bmp_file, filepath) | |
| 226 | + else: | |
| 227 | + dirpath, dirnames, filenames = os.walk(directory) | |
| 228 | + for name in filenames: | |
| 229 | + filepath = str(os.path.join(dirpath, name)) | |
| 230 | + counter += 1 | |
| 231 | + if gui: | |
| 232 | + yield (counter,nfiles) | |
| 233 | + #q.put(filepath) | |
| 234 | + | |
| 235 | + #for t in threads: | |
| 236 | + # q.put(0) | |
| 237 | + | |
| 238 | + #for t in threads: | |
| 239 | + # t.join() | |
| 240 | + | |
| 241 | + #TODO: Is this commented update necessary? | |
| 242 | + #grouper.Update() | |
| 243 | + yield bmp_file.GetValues() | |
| 244 | + | |
| 245 | + | |
| 246 | +class ProgressBitmapReader: | |
| 247 | + def __init__(self): | |
| 248 | + Publisher.subscribe(self.CancelLoad, "Cancel bitmap load") | |
| 249 | + | |
| 250 | + def CancelLoad(self, evt_pubsub): | |
| 251 | + self.running = False | |
| 252 | + self.stoped = True | |
| 253 | + | |
| 254 | + def SetWindowEvent(self, frame): | |
| 255 | + self.frame = frame | |
| 256 | + | |
| 257 | + def SetDirectoryPath(self, path,recursive=True): | |
| 258 | + self.running = True | |
| 259 | + self.stoped = False | |
| 260 | + self.GetBitmaps(path,recursive) | |
| 261 | + | |
| 262 | + def UpdateLoadFileProgress(self,cont_progress): | |
| 263 | + Publisher.sendMessage("Update bitmap load", cont_progress) | |
| 264 | + | |
| 265 | + def EndLoadFile(self, bitmap_list): | |
| 266 | + Publisher.sendMessage("End bitmap load", bitmap_list) | |
| 267 | + | |
| 268 | + def GetBitmaps(self, path, recursive): | |
| 269 | + | |
| 270 | + #if not const.VTK_WARNING: | |
| 271 | + # log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
| 272 | + # fow = vtk.vtkFileOutputWindow() | |
| 273 | + # fow.SetFileName(log_path) | |
| 274 | + # ow = vtk.vtkOutputWindow() | |
| 275 | + # ow.SetInstance(fow) | |
| 276 | + | |
| 277 | + y = yGetBitmaps(path, recursive) | |
| 278 | + for value_progress in y: | |
| 279 | + if not self.running: | |
| 280 | + break | |
| 281 | + if isinstance(value_progress, tuple): | |
| 282 | + self.UpdateLoadFileProgress(value_progress) | |
| 283 | + else: | |
| 284 | + self.EndLoadFile(value_progress) | |
| 285 | + | |
| 286 | + #Is necessary in the case user cancel | |
| 287 | + #the load, ensure that dicomdialog is closed | |
| 288 | + if(self.stoped): | |
| 289 | + self.UpdateLoadFileProgress(None) | |
| 290 | + self.stoped = False | |
| 291 | + | |
| 292 | +def VtkErrorPNGWriter(obj, f): | |
| 293 | + global vtk_error | |
| 294 | + vtk_error = True | |
| 295 | + | |
| 296 | +def ScipyRead(filepath): | |
| 297 | + try: | |
| 298 | + r = misc.imread(filepath, flatten=True) | |
| 299 | + dt = r.dtype | |
| 300 | + | |
| 301 | + if dt == "float" or dt == "float16"\ | |
| 302 | + or dt == "float32" or dt == "float64": | |
| 303 | + shift=-r.max()/2 | |
| 304 | + simage = numpy.zeros_like(r, dtype='int16') | |
| 305 | + simage[:] = r.astype('int32') + shift | |
| 306 | + | |
| 307 | + return simage | |
| 308 | + else: | |
| 309 | + return r | |
| 310 | + except(IOError): | |
| 311 | + return False | |
| 312 | + | |
| 313 | +def VtkRead(filepath, t): | |
| 314 | + | |
| 315 | + global no_error | |
| 316 | + | |
| 317 | + if t == "bmp": | |
| 318 | + reader = vtk.vtkBMPReader() | |
| 319 | + | |
| 320 | + elif t == "tiff" or t == "tif": | |
| 321 | + reader = vtk.vtkTIFFReader() | |
| 322 | + | |
| 323 | + elif t == "png": | |
| 324 | + reader = vtk.vtkPNGReader() | |
| 325 | + | |
| 326 | + elif t == "jpeg" or t == "jpg": | |
| 327 | + reader = vtk.vtkJPEGReader() | |
| 328 | + | |
| 329 | + else: | |
| 330 | + return False | |
| 331 | + | |
| 332 | + reader.AddObserver("ErrorEvent", VtkErrorToPy) | |
| 333 | + reader.SetFileName(filepath) | |
| 334 | + reader.Update() | |
| 335 | + | |
| 336 | + if no_error: | |
| 337 | + image = reader.GetOutput() | |
| 338 | + dim = image.GetDimensions() | |
| 339 | + | |
| 340 | + if reader.GetNumberOfScalarComponents() > 1: | |
| 341 | + luminanceFilter = vtk.vtkImageLuminance() | |
| 342 | + luminanceFilter.SetInputData(image) | |
| 343 | + luminanceFilter.Update() | |
| 344 | + | |
| 345 | + image = vtk.vtkImageData() | |
| 346 | + image.DeepCopy(luminanceFilter.GetOutput()) | |
| 347 | + | |
| 348 | + img_array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) | |
| 349 | + img_array.shape = (dim[1], dim[0]) | |
| 350 | + | |
| 351 | + return img_array | |
| 352 | + else: | |
| 353 | + no_error = True | |
| 354 | + return False | |
| 355 | + | |
| 356 | + | |
| 357 | +def ReadBitmap(filepath): | |
| 358 | + | |
| 359 | + t = VerifyDataType(filepath) | |
| 360 | + | |
| 361 | + if t == False: | |
| 362 | + return False | |
| 363 | + | |
| 364 | + img_array = VtkRead(filepath, t) | |
| 365 | + | |
| 366 | + if not(isinstance(img_array, numpy.ndarray)): | |
| 367 | + | |
| 368 | + no_error = True | |
| 369 | + | |
| 370 | + img_array = ScipyRead(filepath) | |
| 371 | + | |
| 372 | + if not(isinstance(img_array, numpy.ndarray)): | |
| 373 | + return False | |
| 374 | + | |
| 375 | + return img_array | |
| 376 | + | |
| 377 | + | |
| 378 | +def VtkErrorToPy(obj, evt): | |
| 379 | + global no_error | |
| 380 | + no_error = False | |
| 381 | + | |
| 382 | + | |
| 383 | +def VerifyDataType(filepath): | |
| 384 | + try: | |
| 385 | + t = imghdr.what(filepath) | |
| 386 | + return t | |
| 387 | + except IOError: | |
| 388 | + return False | |
| 389 | + | ... | ... |