Commit 794ea5034b68ff1c8de8138d88d266bfd9bfbf85

Authored by Paulo Henrique Junqueira Amorim
Committed by GitHub
1 parent 335b3a7c
Exists in master

Micro ct (#40)

ADD: This branch is for InVesalius to read BMP, TIF, JPEG and PNG files.
invesalius/constants.py
@@ -452,7 +452,7 @@ VTK_WARNING = 0 @@ -452,7 +452,7 @@ VTK_WARNING = 0
452 [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, 452 [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE,
453 ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, 453 ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET,
454 ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES, 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 ID_EXIT = wx.ID_EXIT 456 ID_EXIT = wx.ID_EXIT
457 ID_ABOUT = wx.ID_ABOUT 457 ID_ABOUT = wx.ID_ABOUT
458 458
invesalius/control.py
@@ -35,8 +35,10 @@ import project as prj @@ -35,8 +35,10 @@ import project as prj
35 import reader.analyze_reader as analyze 35 import reader.analyze_reader as analyze
36 import reader.dicom_grouper as dg 36 import reader.dicom_grouper as dg
37 import reader.dicom_reader as dcm 37 import reader.dicom_reader as dcm
  38 +import reader.bitmap_reader as bmp
38 import session as ses 39 import session as ses
39 40
  41 +
40 import utils 42 import utils
41 import gui.dialogs as dialogs 43 import gui.dialogs as dialogs
42 import subprocess 44 import subprocess
@@ -74,13 +76,20 @@ class Controller(): @@ -74,13 +76,20 @@ class Controller():
74 'Save raycasting preset') 76 'Save raycasting preset')
75 Publisher.subscribe(self.OnOpenDicomGroup, 77 Publisher.subscribe(self.OnOpenDicomGroup,
76 'Open DICOM group') 78 'Open DICOM group')
  79 + Publisher.subscribe(self.OnOpenBitmapFiles,
  80 + 'Open bitmap files')
77 Publisher.subscribe(self.Progress, "Update dicom load") 81 Publisher.subscribe(self.Progress, "Update dicom load")
  82 + Publisher.subscribe(self.Progress, "Update bitmap load")
78 Publisher.subscribe(self.OnLoadImportPanel, "End dicom load") 83 Publisher.subscribe(self.OnLoadImportPanel, "End dicom load")
  84 + Publisher.subscribe(self.OnLoadImportBitmapPanel, "End bitmap load")
79 Publisher.subscribe(self.OnCancelImport, 'Cancel DICOM load') 85 Publisher.subscribe(self.OnCancelImport, 'Cancel DICOM load')
  86 + Publisher.subscribe(self.OnCancelImportBitmap, 'Cancel bitmap load')
  87 +
80 Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project') 88 Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project')
81 Publisher.subscribe(self.OnOpenProject, 'Open project') 89 Publisher.subscribe(self.OnOpenProject, 'Open project')
82 Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project') 90 Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project')
83 Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog') 91 Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog')
  92 + Publisher.subscribe(self.OnShowBitmapFile, 'Show bitmap dialog')
84 93
85 Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog') 94 Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog')
86 95
@@ -92,7 +101,9 @@ class Controller(): @@ -92,7 +101,9 @@ class Controller():
92 Publisher.sendMessage('Hide import panel') 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,9 +130,35 @@ class Controller():
119 self.LoadProject() 130 self.LoadProject()
120 Publisher.sendMessage("Enable state project", True) 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 def ShowDialogImportDirectory(self): 162 def ShowDialogImportDirectory(self):
126 # Offer to save current project if necessary 163 # Offer to save current project if necessary
127 session = ses.Session() 164 session = ses.Session()
@@ -291,6 +328,12 @@ class Controller(): @@ -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 def StartImportPanel(self, path): 338 def StartImportPanel(self, path):
296 339
@@ -324,6 +367,26 @@ class Controller(): @@ -324,6 +367,26 @@ class Controller():
324 Publisher.sendMessage('Show import panel') 367 Publisher.sendMessage('Show import panel')
325 Publisher.sendMessage("Show import panel in frame") 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 def LoadImportPanel(self, patient_series): 391 def LoadImportPanel(self, patient_series):
329 if patient_series and isinstance(patient_series, list): 392 if patient_series and isinstance(patient_series, list):
@@ -335,6 +398,9 @@ class Controller(): @@ -335,6 +398,9 @@ class Controller():
335 dialog.ImportInvalidFiles() 398 dialog.ImportInvalidFiles()
336 return False 399 return False
337 400
  401 +
  402 + #----------- to import by command line ---------------------------------------------------
  403 +
338 def OnImportMedicalImages(self, pubsub_evt): 404 def OnImportMedicalImages(self, pubsub_evt):
339 directory = pubsub_evt.data 405 directory = pubsub_evt.data
340 self.ImportMedicalImages(directory) 406 self.ImportMedicalImages(directory)
@@ -358,6 +424,8 @@ class Controller(): @@ -358,6 +424,8 @@ class Controller():
358 self.LoadProject() 424 self.LoadProject()
359 Publisher.sendMessage("Enable state project", True) 425 Publisher.sendMessage("Enable state project", True)
360 426
  427 + #-------------------------------------------------------------------------------------
  428 +
361 def LoadProject(self): 429 def LoadProject(self):
362 proj = prj.Project() 430 proj = prj.Project()
363 431
@@ -401,9 +469,13 @@ class Controller(): @@ -401,9 +469,13 @@ class Controller():
401 Publisher.sendMessage('Show mask', (mask_index, True)) 469 Publisher.sendMessage('Show mask', (mask_index, True))
402 else: 470 else:
403 mask_name = const.MASK_NAME_PATTERN % (1,) 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 Publisher.sendMessage('Create new mask', 479 Publisher.sendMessage('Create new mask',
408 (mask_name, thresh, colour)) 480 (mask_name, thresh, colour))
409 481
@@ -483,6 +555,138 @@ class Controller(): @@ -483,6 +555,138 @@ class Controller():
483 dirpath = session.CreateProject(filename) 555 dirpath = session.CreateProject(filename)
484 #proj.SavePlistProject(dirpath, filename) 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 def OnOpenDicomGroup(self, pubsub_evt): 690 def OnOpenDicomGroup(self, pubsub_evt):
487 group, interval, file_range = pubsub_evt.data 691 group, interval, file_range = pubsub_evt.data
488 matrix, matrix_filename, dicom = self.OpenDicomGroup(group, interval, file_range, gui=True) 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,7 +32,9 @@ from vtk.util import numpy_support
32 32
33 import constants as const 33 import constants as const
34 from data import vtk_utils 34 from data import vtk_utils
  35 +from reader import bitmap_reader
35 import utils 36 import utils
  37 +import converters
36 38
37 # TODO: Test cases which are originally in sagittal/coronal orientation 39 # TODO: Test cases which are originally in sagittal/coronal orientation
38 # and have gantry 40 # and have gantry
@@ -416,6 +418,90 @@ class ImageCreator: @@ -416,6 +418,90 @@ class ImageCreator:
416 418
417 return imagedata 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 def dcm2memmap(files, slice_size, orientation, resolution_percentage): 505 def dcm2memmap(files, slice_size, orientation, resolution_percentage):
420 """ 506 """
421 From a list of dicom files it creates memmap file in the temp folder and 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,7 +548,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage):
462 resolution_percentage = resolution_percentage, update_progress = None) 548 resolution_percentage = resolution_percentage, update_progress = None)
463 549
464 image = image_resized 550 image = image_resized
465 - print ">>>>>>>>>", image.GetDimensions()  
466 551
467 min_aux, max_aux = image.GetScalarRange() 552 min_aux, max_aux = image.GetScalarRange()
468 if min_scalar is None or min_aux < min_scalar: 553 if min_scalar is None or min_aux < min_scalar:
@@ -482,7 +567,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): @@ -482,7 +567,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage):
482 # sagittal cases. 567 # sagittal cases.
483 matrix[:, :, n] = array 568 matrix[:, :, n] = array
484 else: 569 else:
485 - print array.shape, matrix.shape  
486 array.shape = matrix.shape[1], matrix.shape[2] 570 array.shape = matrix.shape[1], matrix.shape[2]
487 matrix[n] = array 571 matrix[n] = array
488 update_progress(cont,message) 572 update_progress(cont,message)
invesalius/gui/bitmap_preview_panel.py 0 → 100644
@@ -0,0 +1,903 @@ @@ -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,6 +39,12 @@ from gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGE
39 39
40 import numpy as np 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 class MaskEvent(wx.PyCommandEvent): 48 class MaskEvent(wx.PyCommandEvent):
43 def __init__(self , evtType, id, mask_index): 49 def __init__(self , evtType, id, mask_index):
44 wx.PyCommandEvent.__init__(self, evtType, id,) 50 wx.PyCommandEvent.__init__(self, evtType, id,)
@@ -306,6 +312,48 @@ def ShowImportDirDialog(): @@ -306,6 +312,48 @@ def ShowImportDirDialog():
306 os.chdir(current_dir) 312 os.chdir(current_dir)
307 return path 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 def ShowSaveAsProjectDialog(default_filename=None): 357 def ShowSaveAsProjectDialog(default_filename=None):
310 current_dir = os.path.abspath(".") 358 current_dir = os.path.abspath(".")
311 dlg = wx.FileDialog(None, 359 dlg = wx.FileDialog(None,
@@ -1630,3 +1678,114 @@ class ReorientImageDialog(wx.Dialog): @@ -1630,3 +1678,114 @@ class ReorientImageDialog(wx.Dialog):
1630 Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT) 1678 Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT)
1631 Publisher.sendMessage('Enable style', const.STATE_DEFAULT) 1679 Publisher.sendMessage('Enable style', const.STATE_DEFAULT)
1632 self.Destroy() 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,6 +36,7 @@ import default_tasks as tasks
36 import default_viewers as viewers 36 import default_viewers as viewers
37 import gui.dialogs as dlg 37 import gui.dialogs as dlg
38 import import_panel as imp 38 import import_panel as imp
  39 +import import_bitmap_panel as imp_bmp
39 import import_network_panel as imp_net 40 import import_network_panel as imp_net
40 import project as prj 41 import project as prj
41 import session as ses 42 import session as ses
@@ -126,6 +127,7 @@ class Frame(wx.Frame): @@ -126,6 +127,7 @@ class Frame(wx.Frame):
126 sub(self._ShowImportPanel, 'Show import panel in frame') 127 sub(self._ShowImportPanel, 'Show import panel in frame')
127 #sub(self._ShowHelpMessage, 'Show help message') 128 #sub(self._ShowHelpMessage, 'Show help message')
128 sub(self._ShowImportNetwork, 'Show retrieve dicom panel') 129 sub(self._ShowImportNetwork, 'Show retrieve dicom panel')
  130 + sub(self._ShowImportBitmap, 'Show import bitmap panel in frame')
129 sub(self._ShowTask, 'Show task panel') 131 sub(self._ShowTask, 'Show task panel')
130 sub(self._UpdateAUI, 'Update AUI') 132 sub(self._UpdateAUI, 'Update AUI')
131 sub(self._Exit, 'Exit') 133 sub(self._Exit, 'Exit')
@@ -170,8 +172,14 @@ class Frame(wx.Frame): @@ -170,8 +172,14 @@ class Frame(wx.Frame):
170 # are shown, this should be hiden 172 # are shown, this should be hiden
171 caption = _("Preview medical data to be reconstructed") 173 caption = _("Preview medical data to be reconstructed")
172 aui_manager.AddPane(imp.Panel(self), wx.aui.AuiPaneInfo(). 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 Caption(caption).CaptionVisible(True)) 183 Caption(caption).CaptionVisible(True))
176 184
177 ncaption = _("Retrieve DICOM from PACS") 185 ncaption = _("Retrieve DICOM from PACS")
@@ -298,6 +306,9 @@ class Frame(wx.Frame): @@ -298,6 +306,9 @@ class Frame(wx.Frame):
298 Publisher.sendMessage("Set layout button full") 306 Publisher.sendMessage("Set layout button full")
299 aui_manager = self.aui_manager 307 aui_manager = self.aui_manager
300 aui_manager.GetPane("Import").Show(0) 308 aui_manager.GetPane("Import").Show(0)
  309 +
  310 + aui_manager.GetPane("ImportBMP").Show(0)
  311 +
301 aui_manager.GetPane("Data").Show(1) 312 aui_manager.GetPane("Data").Show(1)
302 aui_manager.GetPane("Tasks").Show(1) 313 aui_manager.GetPane("Tasks").Show(1)
303 aui_manager.Update() 314 aui_manager.Update()
@@ -314,6 +325,18 @@ class Frame(wx.Frame): @@ -314,6 +325,18 @@ class Frame(wx.Frame):
314 aui_manager.GetPane("Import").Show(0) 325 aui_manager.GetPane("Import").Show(0)
315 aui_manager.Update() 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 def _ShowHelpMessage(self, evt_pubsub): 340 def _ShowHelpMessage(self, evt_pubsub):
318 aui_manager = self.aui_manager 341 aui_manager = self.aui_manager
319 pos = aui_manager.GetPane("Data").window.GetScreenPosition() 342 pos = aui_manager.GetPane("Data").window.GetScreenPosition()
@@ -371,6 +394,8 @@ class Frame(wx.Frame): @@ -371,6 +394,8 @@ class Frame(wx.Frame):
371 self.ShowOpenProject() 394 self.ShowOpenProject()
372 elif id == const.ID_ANALYZE_IMPORT: 395 elif id == const.ID_ANALYZE_IMPORT:
373 self.ShowAnalyzeImporter() 396 self.ShowAnalyzeImporter()
  397 + elif id == const.ID_TIFF_JPG_PNG:
  398 + self.ShowBitmapImporter()
374 elif id == const.ID_PROJECT_SAVE: 399 elif id == const.ID_PROJECT_SAVE:
375 session = ses.Session() 400 session = ses.Session()
376 if session.temp_item: 401 if session.temp_item:
@@ -501,6 +526,12 @@ class Frame(wx.Frame): @@ -501,6 +526,12 @@ class Frame(wx.Frame):
501 """ 526 """
502 Publisher.sendMessage('Show analyze dialog', True) 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 def FlipVolume(self, axis): 535 def FlipVolume(self, axis):
505 Publisher.sendMessage('Flip volume', axis) 536 Publisher.sendMessage('Flip volume', axis)
506 Publisher.sendMessage('Reload actual slice') 537 Publisher.sendMessage('Reload actual slice')
@@ -581,6 +612,7 @@ class MenuBar(wx.MenuBar): @@ -581,6 +612,7 @@ class MenuBar(wx.MenuBar):
581 #Import Others Files 612 #Import Others Files
582 others_file_menu = wx.Menu() 613 others_file_menu = wx.Menu()
583 others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze") 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 # FILE 617 # FILE
586 file_menu = wx.Menu() 618 file_menu = wx.Menu()
invesalius/gui/import_bitmap_panel.py 0 → 100644
@@ -0,0 +1,484 @@ @@ -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,6 +524,7 @@ class MaskProperties(wx.Panel):
524 self.bind_evt_gradient = False 524 self.bind_evt_gradient = False
525 self.gradient.SetMinValue(thresh_min) 525 self.gradient.SetMinValue(thresh_min)
526 self.gradient.SetMaxValue(thresh_max) 526 self.gradient.SetMaxValue(thresh_max)
  527 + print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", thresh_min, thresh_max
527 self.bind_evt_gradient = True 528 self.bind_evt_gradient = True
528 thresh = (thresh_min, thresh_max) 529 thresh = (thresh_min, thresh_max)
529 if thresh in Project().threshold_modes.values(): 530 if thresh in Project().threshold_modes.values():
invesalius/presets.py
@@ -73,12 +73,13 @@ class Presets(): @@ -73,12 +73,13 @@ class Presets():
73 def UpdateThresholdModes(self, evt): 73 def UpdateThresholdModes(self, evt):
74 74
75 thresh_min, thresh_max = evt.data 75 thresh_min, thresh_max = evt.data
76 -  
77 presets_list = (self.thresh_ct, self.thresh_mri) 76 presets_list = (self.thresh_ct, self.thresh_mri)
78 77
79 for presets in presets_list: 78 for presets in presets_list:
80 for key in presets: 79 for key in presets:
81 (t_min, t_max) = presets[key] 80 (t_min, t_max) = presets[key]
  81 +
  82 +
82 if (t_min is None) or (t_max is None): # setting custom preset 83 if (t_min is None) or (t_max is None): # setting custom preset
83 t_min = thresh_min 84 t_min = thresh_min
84 t_max = thresh_max 85 t_max = thresh_max
@@ -93,7 +94,7 @@ class Presets(): @@ -93,7 +94,7 @@ class Presets():
93 t_min = thresh_min 94 t_min = thresh_min
94 if (t_max < thresh_min): 95 if (t_max < thresh_min):
95 t_max = thresh_max 96 t_max = thresh_max
96 - 97 +
97 presets[key] = (t_min, t_max) 98 presets[key] = (t_min, t_max)
98 99
99 Publisher.sendMessage('Update threshold limits', (thresh_min, 100 Publisher.sendMessage('Update threshold limits', (thresh_min,
invesalius/reader/bitmap_reader.py 0 → 100644
@@ -0,0 +1,389 @@ @@ -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 +