Beispiel #1
0
    def __init__(self, parent, size=(800,-1), **kwargs):
        wx.Frame.__init__(self, parent, -1, size=size, title='Plate Viewer', **kwargs)
        CPATool.__init__(self)
        self.SetName(self.tool_name)
        self.SetBackgroundColour("white") # Fixing the color

        # Check for required properties fields.
        fail = False
        for field in required_fields:
            if not p.field_defined(field):
                fail = True
                raise Exception('Properties field "%s" is required for PlateViewer.'%(field))
        if fail:    
            self.Destroy()
            return

        self.chMap = p.image_channel_colors[:]

        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)
        self.fileMenu = wx.Menu()
        self.exitMenuItem = self.fileMenu.Append(id=wx.ID_EXIT, text='Exit\tCtrl+Q', help='Close Plate Viewer')
        self.GetMenuBar().Append(self.fileMenu, 'File')
        self.menuBar.Append(cpa.helpmenu.make_help_menu(self), 'Help')
        save_csv_menu_item = self.fileMenu.Append(-1, 'Save table to CSV\tCtrl+S')
        self.Bind(wx.EVT_MENU, self.on_save_csv, save_csv_menu_item)
        
        wx.EVT_MENU(self, wx.ID_EXIT, lambda _:self.Close())

        dataSourceSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Source:'), wx.VERTICAL)
        dataSourceSizer.Add(wx.StaticText(self, label='Data source:'))
        self.sourceChoice = TableComboBox(self, -1, size=fixed_width)
        dataSourceSizer.Add(self.sourceChoice)
        dataSourceSizer.AddSpacer((-1,3))
        dataSourceSizer.Add(wx.StaticText(self, label='Measurement:'))
        measurements = get_non_blob_types_from_table(p.image_table)
        self.measurementsChoice = ComboBox(self, choices=measurements, size=fixed_width)
        self.measurementsChoice.Select(0)
        dataSourceSizer.Add(self.measurementsChoice)
        dataSourceSizer.Add(wx.StaticText(self, label='Filter:'))
        self.filterChoice = FilterComboBox(self, size=fixed_width)
        dataSourceSizer.Add(self.filterChoice)
        
        groupingSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Data aggregation:'), wx.VERTICAL)
        groupingSizer.Add(wx.StaticText(self, label='Aggregation method:'))
        aggregation = ['mean', 'sum', 'median', 'stdev', 'cv%', 'min', 'max']
        self.aggregationMethodsChoice = ComboBox(self, choices=aggregation, size=fixed_width)
        self.aggregationMethodsChoice.Select(0)
        groupingSizer.Add(self.aggregationMethodsChoice)

        viewSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='View options:'), wx.VERTICAL)
        viewSizer.Add(wx.StaticText(self, label='Color map:'))
        maps = [m for m in matplotlib.cm.datad.keys() if not m.endswith("_r")]
        maps.sort()
        self.colorMapsChoice = ComboBox(self, choices=maps, size=fixed_width)
        self.colorMapsChoice.SetSelection(maps.index('jet'))
        viewSizer.Add(self.colorMapsChoice)

        viewSizer.AddSpacer((-1,3))
        viewSizer.Add(wx.StaticText(self, label='Well display:'))
        if p.image_thumbnail_cols:
            choices = pmp.all_well_shapes
        else:
            choices = list(pmp.all_well_shapes)
            choices.remove(pmp.THUMBNAIL)
        self.wellDisplayChoice = ComboBox(self, choices=choices, size=fixed_width)
        self.wellDisplayChoice.Select(0)
        viewSizer.Add(self.wellDisplayChoice)

        viewSizer.AddSpacer((-1,3))
        viewSizer.Add(wx.StaticText(self, label='Number of plates:'))
        self.numberOfPlatesTE = wx.TextCtrl(self, -1, '1', style=wx.TE_PROCESS_ENTER)
        viewSizer.Add(self.numberOfPlatesTE)
        if not p.plate_id:
            self.numberOfPlatesTE.Disable()

        annotationSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Annotation:'), wx.VERTICAL)
        annotationSizer.Add(wx.StaticText(self, label='Annotation column:'))
        annotationColSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.annotation_cols = dict([(col, db.GetColumnType(p.image_table, col)) 
                                     for col in db.GetUserColumnNames(p.image_table)])
        self.annotationCol = ComboBox(self, choices=self.annotation_cols.keys(), size=(120,-1))
        if len(self.annotation_cols) > 0:
            self.annotationCol.SetSelection(0)
        annotationColSizer.Add(self.annotationCol, flag=wx.ALIGN_CENTER_VERTICAL)
        annotationColSizer.AddSpacer((3,-1))
        self.addAnnotationColBtn = wx.Button(self, -1, 'Add', size=(44,-1))
        annotationColSizer.Add(self.addAnnotationColBtn, flag=wx.ALIGN_CENTER_VERTICAL)
        annotationSizer.Add(annotationColSizer)
        annotationSizer.AddSpacer((-1,3))
        annotationSizer.Add(wx.StaticText(self, label='Label:'))
        self.annotationLabel = wx.TextCtrl(self, -1, 'Select wells')#, style=wx.TE_PROCESS_ENTER)
        self.annotationLabel.Disable()
        self.annotationLabel.SetForegroundColour(wx.Colour(80,80,80))
        self.annotationLabel.SetBackgroundColour(wx.LIGHT_GREY)
        annotationSizer.Add(self.annotationLabel)
        annotationSizer.AddSpacer((-1,3))
        self.outlineMarked = wx.CheckBox(self, -1, label='Outline annotated wells')
        annotationSizer.Add(self.outlineMarked)
        annotationSizer.AddSpacer((-1,3))
        self.annotationShowVals = wx.CheckBox(self, -1, label='Show values on plate')
        annotationSizer.Add(self.annotationShowVals)
        if len(db.GetUserColumnNames(p.image_table)) == 0:
            self.outlineMarked.Disable()
            self.annotationShowVals.Disable()
            
        controlSizer = wx.BoxSizer(wx.VERTICAL)
        controlSizer.Add(dataSourceSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(groupingSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(viewSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(annotationSizer, 0 , wx.EXPAND)

        self.plateMapSizer = wx.GridSizer(1,1,5,5)
        self.plateMaps = []
        self.plateMapChoices = []

        self.rightSizer = wx.BoxSizer(wx.VERTICAL)
        self.rightSizer.Add(self.plateMapSizer, 1, wx.EXPAND|wx.BOTTOM, 10)
        self.colorBar = ColorBarPanel(self, 'jet', size=(-1,25))
        self.rightSizer.Add(self.colorBar, 0, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL)

        mainSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer.Add(controlSizer, 0, wx.LEFT|wx.TOP|wx.BOTTOM, 10)
        mainSizer.Add(self.rightSizer, 1, wx.EXPAND|wx.ALL, 10)

        self.SetSizer(mainSizer)
        self.SetClientSize((self.Size[0],self.Sizer.CalcMin()[1]))

        self.sourceChoice.Bind(wx.EVT_COMBOBOX, self.UpdateMeasurementChoice)
        self.measurementsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectMeasurement)
        self.measurementsChoice.Select(0)
        self.aggregationMethodsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectAggregationMethod)
        self.colorMapsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectColorMap)
        self.numberOfPlatesTE.Bind(wx.EVT_TEXT_ENTER, self.OnEnterNumberOfPlates)
        self.wellDisplayChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectWellDisplay)
        self.annotationCol.Bind(wx.EVT_COMBOBOX, self.OnSelectAnnotationCol)
        self.addAnnotationColBtn.Bind(wx.EVT_BUTTON, self.OnAddAnnotationCol)
        self.annotationLabel.Bind(wx.EVT_KEY_UP, self.OnEnterAnnotation)
        self.outlineMarked.Bind(wx.EVT_CHECKBOX, self.OnOutlineMarked)
        self.annotationShowVals.Bind(wx.EVT_CHECKBOX, self.OnShowAnnotationValues)
        self.filterChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectFilter)

        self.AddPlateMap()
        self.OnSelectMeasurement()
Beispiel #2
0
 def __init__(self,Yield):
     """Create a PELvis frame
       
     Keyword arguments:
     Yield -- A function to give control back to the main event loop
       
     """
     wx.Frame.__init__(self,None,wx.ID_ANY,"PEL Visualizer")
     self.data = PelFile()
     self.Yield = Yield
     self.mask = np.ones((128,16),dtype=np.bool)
     #Create items in the frame
     self.yPanel = GraphPanel(self,(2,8),64,GraphPanel.VERTICAL)
     self.xPanel = GraphPanel(self,(8,2),64,GraphPanel.INVERTED)
     self.colorbar = ColorBarPanel(self,cm.jet)
     self.opPanel = PelvisOptionPanel(self)
     self.posPanel = PositionPanel(self)
     self.imPanel = ImagePanel(self,self.posPanel.set,
                               self.opPanel.setPosMin,self.opPanel.setPosMax)
     self.specDlg = SpectrumDialog(self)
       
     self.cmp = None #color map
     self.imageSaveDialog=wx.FileDialog(self,"Choose graphics file",wildcard="Portable Network Graphic (png)|*.PNG|Windows Bitmap (bmp)|*.BMP|Joint Photographic Experts Group (jpg)|*.JPG|Portable Network Monocrome (pnm)|*.PNM|Tagged Image File Format (tif)|*.TIF|Archaic, useless format (pcx)|*.PCX",style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
       
     self.update = self.updateSingle#update the image
     self.updateData = self.updateSingleData#update the data in the image
       
     #Create the menu
     menubar = wx.MenuBar()
     filemenu = wx.Menu()
     editmenu = wx.Menu()
     scalemenu = wx.Menu()
     analysismenu = wx.Menu()
     noisemenu = wx.Menu()
       
     #populate the menu
     filemenu.Append(self.ID_OPEN,"&Open\tCtrl-O"," Open a PEL file")
     filemenu.Append(self.ID_OPENTWO,"&Polarized Set"," Open two PEL files")
     filemenu.Append(self.ID_SAVE,"&Save\tCtrl-S"," Save an image file")
     filemenu.Append(self.ID_SPECTRUM,"Spectrum"," View the TOF spectrum")
     filemenu.Append(self.ID_IMAGE_ARRAY,"&Export Images..."," Save a series of TOF snapshots")
     filemenu.Append(self.ID_EXIT,"&Quit\tCtrl-Q"," Quit")
       
     editmenu.Append(self.ID_COPY,"&Copy\tCtrl-c","Copy the current image to the clipboard")
       
     scalemenu.Append(self.ID_GREY,"Greyscale\tCtrl-G","Monochrome images")
     scalemenu.Append(self.ID_HUEVAL,"Hue and Value\tCtrl-H","Scaled Rainbow Images")
     scalemenu.Append(self.ID_SPECTRAL,"spectral","Uses spectrum of light")
     scalemenu.Append(self.ID_PICKER,"Map Picker..."," Select from the full list of colormaps")
       
     analysismenu.Append(self.ID_POLAR,"Check Polarization\tCtrl-P","2d plot of polarization data")
     analysismenu.Append(self.ID_FLIPPING,"Check Flipping Ratio\tCtrl-F","2d plot of  spin up over spin down")
     analysismenu.Append(self.ID_SPIN_UP,"View Spin Up State\tCtrl-U","2d plot of  spin up")
     analysismenu.Append(self.ID_SPIN_DOWN,"View Spin Down State\tCtrl-D","2d plot of  spin down")
       
     noisemenu.Append(self.ID_FLAT,"&Load Flat"," Load a blank run for background subtraction")
     noisemenu.Append(self.ID_FAKEFLAT,"Si&mulate Flat"," Drop out background within the same image")
     noisemenu.Append(self.ID_ROD,"Region of &Disinterest"," Drop out background within the same image")
     noisemenu.Append(self.ID_EXPORT_ROI,"Export ROI"," Export a binary file corresponding to where the data is above the minimum intensity.")
     noisemenu.Append(self.ID_IMPORT_ROI,"Import ROI"," Add another exclusion mask.")
       
       
     #Bind events to the menu
     self.Connect(self.ID_EXIT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnExit)
     self.Connect(self.ID_OPEN,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnOpen)
     self.Connect(self.ID_OPENTWO,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnOpenSet)
     self.Connect(self.ID_SAVE,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSave)
     self.Connect(self.ID_SPECTRUM,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSpectrum)
     self.Connect(self.ID_IMAGE_ARRAY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnImageArray)
     self.Connect(self.ID_GREY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnGrey)
     self.Connect(self.ID_HUEVAL,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnHueVal)
     self.Connect(self.ID_SPECTRAL,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSpectral)
     self.Connect(self.ID_PICKER,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnPicker)
     self.Connect(self.ID_POLAR,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnPolar)
     self.Connect(self.ID_FLIPPING,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFlipping)
     self.Connect(self.ID_SPIN_UP,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnAnalysisSpinUp)
     self.Connect(self.ID_SPIN_DOWN,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnAnalysisSpinDown)
       
     self.Connect(self.ID_FLAT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFlat)
     self.Connect(self.ID_FAKEFLAT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFakeFlat)
     self.Connect(self.ID_ROD,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnROD)
     self.Connect(self.ID_EXPORT_ROI,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnExportROI)
     self.Connect(self.ID_IMPORT_ROI,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnImportROI)
     self.Connect(self.ID_COPY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnCopy)
     
       
     menubar.Append(filemenu,"&File")
     menubar.Append(editmenu,"&Edit")
     menubar.Append(scalemenu,"&Color")
     menubar.Append(analysismenu,"&Analysis")
     menubar.Append(noisemenu,"&Noise")
     self.SetMenuBar(menubar)
       
     #arrange window
     sizer = wx.GridBagSizer()
     sizer.Add(self.colorbar,pos=wx.GBPosition(0,9),span=wx.GBSpan(9,1))
     sizer.Add(self.imPanel,pos=wx.GBPosition(0,1),span=wx.GBSpan(8,8))
     sizer.Add(self.yPanel,pos=wx.GBPosition(0,0),span=wx.GBSpan(8,1))
     sizer.Add(self.xPanel,pos=wx.GBPosition(8,1),span=wx.GBSpan(1,8))
     sizer.Add(self.opPanel,pos=wx.GBPosition(0,10),span=wx.GBSpan(8,1),flag=wx.EXPAND)
     sizer.Add(self.posPanel,pos=wx.GBPosition(8,0),flag=wx.EXPAND)
     self.progress = wx.Gauge(self,range=1000)
     sizer.Add(self.progress,pos=wx.GBPosition(9,0),span=wx.GBSpan(1,11),flag=wx.EXPAND)
       
     updateButton = wx.Button(self,-1,"Update")
     updateButton.Bind(wx.EVT_BUTTON,self.OnUpdateButton)
     sizer.Add(updateButton,flag=wx.EXPAND,pos=wx.GBPosition(8,10))
       
     self.data = self.makePel()
     self.flatrun = None#background data
     
     sizer.SetSizeHints(self)
     self.SetSizer(sizer)
     self.Show(True)
Beispiel #3
0
class PlateViewer(wx.Frame, CPATool):
    def __init__(self, parent, size=(800,-1), **kwargs):
        wx.Frame.__init__(self, parent, -1, size=size, title='Plate Viewer', **kwargs)
        CPATool.__init__(self)
        self.SetName(self.tool_name)
        self.SetBackgroundColour("white") # Fixing the color

        # Check for required properties fields.
        fail = False
        for field in required_fields:
            if not p.field_defined(field):
                fail = True
                raise Exception('Properties field "%s" is required for PlateViewer.'%(field))
        if fail:    
            self.Destroy()
            return

        self.chMap = p.image_channel_colors[:]

        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)
        self.fileMenu = wx.Menu()
        self.exitMenuItem = self.fileMenu.Append(id=wx.ID_EXIT, text='Exit\tCtrl+Q', help='Close Plate Viewer')
        self.GetMenuBar().Append(self.fileMenu, 'File')
        self.menuBar.Append(cpa.helpmenu.make_help_menu(self), 'Help')
        save_csv_menu_item = self.fileMenu.Append(-1, 'Save table to CSV\tCtrl+S')
        self.Bind(wx.EVT_MENU, self.on_save_csv, save_csv_menu_item)
        
        wx.EVT_MENU(self, wx.ID_EXIT, lambda _:self.Close())

        dataSourceSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Source:'), wx.VERTICAL)
        dataSourceSizer.Add(wx.StaticText(self, label='Data source:'))
        self.sourceChoice = TableComboBox(self, -1, size=fixed_width)
        dataSourceSizer.Add(self.sourceChoice)
        dataSourceSizer.AddSpacer((-1,3))
        dataSourceSizer.Add(wx.StaticText(self, label='Measurement:'))
        measurements = get_non_blob_types_from_table(p.image_table)
        self.measurementsChoice = ComboBox(self, choices=measurements, size=fixed_width)
        self.measurementsChoice.Select(0)
        dataSourceSizer.Add(self.measurementsChoice)
        dataSourceSizer.Add(wx.StaticText(self, label='Filter:'))
        self.filterChoice = FilterComboBox(self, size=fixed_width)
        dataSourceSizer.Add(self.filterChoice)
        
        groupingSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Data aggregation:'), wx.VERTICAL)
        groupingSizer.Add(wx.StaticText(self, label='Aggregation method:'))
        aggregation = ['mean', 'sum', 'median', 'stdev', 'cv%', 'min', 'max']
        self.aggregationMethodsChoice = ComboBox(self, choices=aggregation, size=fixed_width)
        self.aggregationMethodsChoice.Select(0)
        groupingSizer.Add(self.aggregationMethodsChoice)

        viewSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='View options:'), wx.VERTICAL)
        viewSizer.Add(wx.StaticText(self, label='Color map:'))
        maps = [m for m in matplotlib.cm.datad.keys() if not m.endswith("_r")]
        maps.sort()
        self.colorMapsChoice = ComboBox(self, choices=maps, size=fixed_width)
        self.colorMapsChoice.SetSelection(maps.index('jet'))
        viewSizer.Add(self.colorMapsChoice)

        viewSizer.AddSpacer((-1,3))
        viewSizer.Add(wx.StaticText(self, label='Well display:'))
        if p.image_thumbnail_cols:
            choices = pmp.all_well_shapes
        else:
            choices = list(pmp.all_well_shapes)
            choices.remove(pmp.THUMBNAIL)
        self.wellDisplayChoice = ComboBox(self, choices=choices, size=fixed_width)
        self.wellDisplayChoice.Select(0)
        viewSizer.Add(self.wellDisplayChoice)

        viewSizer.AddSpacer((-1,3))
        viewSizer.Add(wx.StaticText(self, label='Number of plates:'))
        self.numberOfPlatesTE = wx.TextCtrl(self, -1, '1', style=wx.TE_PROCESS_ENTER)
        viewSizer.Add(self.numberOfPlatesTE)
        if not p.plate_id:
            self.numberOfPlatesTE.Disable()

        annotationSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Annotation:'), wx.VERTICAL)
        annotationSizer.Add(wx.StaticText(self, label='Annotation column:'))
        annotationColSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.annotation_cols = dict([(col, db.GetColumnType(p.image_table, col)) 
                                     for col in db.GetUserColumnNames(p.image_table)])
        self.annotationCol = ComboBox(self, choices=self.annotation_cols.keys(), size=(120,-1))
        if len(self.annotation_cols) > 0:
            self.annotationCol.SetSelection(0)
        annotationColSizer.Add(self.annotationCol, flag=wx.ALIGN_CENTER_VERTICAL)
        annotationColSizer.AddSpacer((3,-1))
        self.addAnnotationColBtn = wx.Button(self, -1, 'Add', size=(44,-1))
        annotationColSizer.Add(self.addAnnotationColBtn, flag=wx.ALIGN_CENTER_VERTICAL)
        annotationSizer.Add(annotationColSizer)
        annotationSizer.AddSpacer((-1,3))
        annotationSizer.Add(wx.StaticText(self, label='Label:'))
        self.annotationLabel = wx.TextCtrl(self, -1, 'Select wells')#, style=wx.TE_PROCESS_ENTER)
        self.annotationLabel.Disable()
        self.annotationLabel.SetForegroundColour(wx.Colour(80,80,80))
        self.annotationLabel.SetBackgroundColour(wx.LIGHT_GREY)
        annotationSizer.Add(self.annotationLabel)
        annotationSizer.AddSpacer((-1,3))
        self.outlineMarked = wx.CheckBox(self, -1, label='Outline annotated wells')
        annotationSizer.Add(self.outlineMarked)
        annotationSizer.AddSpacer((-1,3))
        self.annotationShowVals = wx.CheckBox(self, -1, label='Show values on plate')
        annotationSizer.Add(self.annotationShowVals)
        if len(db.GetUserColumnNames(p.image_table)) == 0:
            self.outlineMarked.Disable()
            self.annotationShowVals.Disable()
            
        controlSizer = wx.BoxSizer(wx.VERTICAL)
        controlSizer.Add(dataSourceSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(groupingSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(viewSizer, 0, wx.EXPAND)
        controlSizer.AddSpacer((-1,3))
        controlSizer.Add(annotationSizer, 0 , wx.EXPAND)

        self.plateMapSizer = wx.GridSizer(1,1,5,5)
        self.plateMaps = []
        self.plateMapChoices = []

        self.rightSizer = wx.BoxSizer(wx.VERTICAL)
        self.rightSizer.Add(self.plateMapSizer, 1, wx.EXPAND|wx.BOTTOM, 10)
        self.colorBar = ColorBarPanel(self, 'jet', size=(-1,25))
        self.rightSizer.Add(self.colorBar, 0, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL)

        mainSizer = wx.BoxSizer(wx.HORIZONTAL)
        mainSizer.Add(controlSizer, 0, wx.LEFT|wx.TOP|wx.BOTTOM, 10)
        mainSizer.Add(self.rightSizer, 1, wx.EXPAND|wx.ALL, 10)

        self.SetSizer(mainSizer)
        self.SetClientSize((self.Size[0],self.Sizer.CalcMin()[1]))

        self.sourceChoice.Bind(wx.EVT_COMBOBOX, self.UpdateMeasurementChoice)
        self.measurementsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectMeasurement)
        self.measurementsChoice.Select(0)
        self.aggregationMethodsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectAggregationMethod)
        self.colorMapsChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectColorMap)
        self.numberOfPlatesTE.Bind(wx.EVT_TEXT_ENTER, self.OnEnterNumberOfPlates)
        self.wellDisplayChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectWellDisplay)
        self.annotationCol.Bind(wx.EVT_COMBOBOX, self.OnSelectAnnotationCol)
        self.addAnnotationColBtn.Bind(wx.EVT_BUTTON, self.OnAddAnnotationCol)
        self.annotationLabel.Bind(wx.EVT_KEY_UP, self.OnEnterAnnotation)
        self.outlineMarked.Bind(wx.EVT_CHECKBOX, self.OnOutlineMarked)
        self.annotationShowVals.Bind(wx.EVT_CHECKBOX, self.OnShowAnnotationValues)
        self.filterChoice.Bind(wx.EVT_COMBOBOX, self.OnSelectFilter)

        self.AddPlateMap()
        self.OnSelectMeasurement()


    def AddPlateMap(self, plateIndex=0):
        '''
        Adds a new blank plateMap to the PlateMapSizer.
        '''
        data = np.ones(p.plate_shape)

        # Try to get explicit labels for all wells.
        res = db.execute('SELECT DISTINCT %s FROM %s WHERE %s != "" and %s IS NOT NULL'%
                         (dbconnect.UniqueWellClause(), p.image_table, p.well_id, p.well_id))

        if p.plate_id:
            self.plateMapChoices += [ComboBox(self, choices=db.GetPlateNames(), size=(400,-1))]
            self.plateMapChoices[-1].Select(plateIndex)
            self.plateMapChoices[-1].Bind(wx.EVT_COMBOBOX, self.OnSelectPlate)
    
            #plate_col_type = db.GetColumnType(p.image_table, p.plate_id)
            #plate_id = plate_col_type(self.plateMapChoices[-1].GetString(plateIndex))
            
            plateMapChoiceSizer = wx.BoxSizer(wx.HORIZONTAL)
            plateMapChoiceSizer.Add(wx.StaticText(self, label='Plate:'), flag=wx.ALIGN_CENTER_VERTICAL)
            plateMapChoiceSizer.Add(self.plateMapChoices[-1], flag=wx.ALIGN_CENTER_VERTICAL)
        well_keys = res

        platemap = pmp.PlateMapPanel(self, data, well_keys, p.plate_shape,
                                     colormap = self.colorMapsChoice.Value,
                                     well_disp = self.wellDisplayChoice.Value)
        platemap.add_well_selection_handler(self.OnSelectWell)
        self.plateMaps += [platemap]

        singlePlateMapSizer = wx.BoxSizer(wx.VERTICAL)
        if p.plate_id:
            singlePlateMapSizer.Add(plateMapChoiceSizer, 0, wx.ALIGN_CENTER)
        singlePlateMapSizer.Add(platemap, 1, wx.EXPAND|wx.ALIGN_CENTER)

        self.plateMapSizer.Add(singlePlateMapSizer, 1, wx.EXPAND|wx.ALIGN_CENTER)

    def UpdatePlateMaps(self):
        self.measurement = self.measurementsChoice.Value
        measurement = self.measurement
        table       = self.sourceChoice.Value
        self.aggMethod   = self.aggregationMethodsChoice.Value
        categorical = measurement not in get_numeric_columns_from_table(table)
        fltr        = self.filterChoice.Value
        self.colorBar.ClearNotifyWindows()

        q = sql.QueryBuilder()
        well_key_cols = [sql.Column(p.image_table, col) for col in well_key_columns()]
        select = list(well_key_cols)
        if not categorical:
            if self.aggMethod=='mean':
                select += [sql.Column(table, measurement, 'AVG')]
            elif self.aggMethod=='stdev':
                select += [sql.Column(table, measurement, 'STDDEV')]
            elif self.aggMethod=='cv%':
                # stddev(col) / avg(col) * 100
                select += [sql.Expression(
                              sql.Column(table, measurement, 'STDDEV'), ' / ',
                              sql.Column(table, measurement, 'AVG'), ' * 100')]
            elif self.aggMethod=='sum':
                select += [sql.Column(table, measurement, 'SUM')]
            elif self.aggMethod=='min':
                select += [sql.Column(table, measurement, 'MIN')]
            elif self.aggMethod=='max':
                select += [sql.Column(table, measurement, 'MAX')]
            elif self.aggMethod=='median':
                select += [sql.Column(table, measurement, 'MEDIAN')]
            elif self.aggMethod=='none':
                select += [sql.Column(table, measurement)]
        else:
            select += [sql.Column(table, measurement)]
        
        q.set_select_clause(select)
        q.set_group_columns(well_key_cols)
        if fltr not in (FilterComboBox.NO_FILTER, FilterComboBox.NEW_FILTER, ''):
            if fltr in p._filters:
                q.add_filter(p._filters[fltr])
            elif fltr in p.gates:
                q.add_filter(p.gates[fltr].as_filter())
            else:
                raise Exception('Could not find filter "%s" in gates or filters'%(fltr))
        wellkeys_and_values = db.execute(str(q))
        wellkeys_and_values = np.array(wellkeys_and_values, dtype=object)

        # Replace measurement None's with nan
        for row in wellkeys_and_values:
            if row[-1] is None:
                row[-1] = np.nan

        data = []
        key_lists = []
        dmax = -np.inf
        dmin = np.inf
        if p.plate_id:
            for plateChoice, plateMap in zip(self.plateMapChoices, self.plateMaps):
                plate = plateChoice.Value
                plateMap.SetPlate(plate)
                self.colorBar.AddNotifyWindow(plateMap)
                self.keys_and_vals = [v for v in wellkeys_and_values if str(v[0])==plate]
                platedata, wellkeys, ignore = FormatPlateMapData(self.keys_and_vals, categorical)
                data += [platedata]
                key_lists += [wellkeys]
                if not categorical:
                    dmin = np.nanmin([float(kv[-1]) for kv in self.keys_and_vals]+[dmin])
                    dmax = np.nanmax([float(kv[-1]) for kv in self.keys_and_vals]+[dmax])
        else:
            self.colorBar.AddNotifyWindow(self.plateMaps[0])
            platedata, wellkeys, ignore = FormatPlateMapData(wellkeys_and_values, categorical)
            data += [platedata]
            key_lists += [wellkeys]
            if not categorical:
                dmin = np.nanmin([float(kv[-1]) for kv in wellkeys_and_values])
                dmax = np.nanmax([float(kv[-1]) for kv in wellkeys_and_values])
            
        if not categorical:
            if len(wellkeys_and_values) > 0:
                # Compute the global extents if there is any data whatsoever
                gmin = np.nanmin([float(vals[-1]) for vals in wellkeys_and_values])
                gmax = np.nanmax([float(vals[-1]) for vals in wellkeys_and_values])
                if np.isinf(dmin) or np.isinf(dmax):
                    gmin = gmax = dmin = dmax = 1.
                    # Warn if there was no data for this plate (and no filter was used)
                    if fltr == FilterComboBox.NO_FILTER:
                        wx.MessageBox('No numeric data was found in "%s.%s" for plate "%s"'
                                      %(table, measurement, plate), 'Warning')
            else:
                gmin = gmax = 1.
                if fltr == FilterComboBox.NO_FILTER:
                    wx.MessageBox('No numeric data was found in %s.%s'
                                  %(table, measurement), 'Warning')

        if categorical:
            self.colorBar.Hide()
        else:
            self.colorBar.Show()
            self.colorBar.SetLocalExtents([dmin,dmax])
            self.colorBar.SetGlobalExtents([gmin,gmax])
        self.rightSizer.Layout()

        for keys, d, plateMap in zip(key_lists, data, self.plateMaps):
            plateMap.SetWellKeys(keys)
            if categorical:
                plateMap.SetData(np.ones(d.shape) * np.nan)
                plateMap.SetTextData(d)
            else:
                plateMap.SetData(d, data_range=self.colorBar.GetLocalExtents(), 
                                 clip_interval=self.colorBar.GetLocalInterval(), 
                                 clip_mode=self.colorBar.GetClipMode())

        for keys, d, plateMap in zip(key_lists, data, self.plateMaps):
            plateMap.SetWellKeys(keys)
            if categorical:
                plateMap.SetData(np.ones(d.shape) * np.nan)
                plateMap.SetTextData(d)
            else:
                plateMap.SetData(d, data_range=self.colorBar.GetLocalExtents(), 
                                 clip_interval=self.colorBar.GetLocalInterval(), 
                                 clip_mode=self.colorBar.GetClipMode())

    def UpdateMeasurementChoice(self, evt=None):
        '''
        Handles the selection of a source table (per-image or per-object) from
        a choice box.  The measurement choice box is populated with the names
        of numeric columns from the selected table.
        '''
        table = self.sourceChoice.Value
        if table == TableComboBox.OTHER_TABLE:
            t = get_other_table_from_user(self)
            if t is not None:
                self.sourceChoice.Items = self.sourceChoice.Items[:-1] + [t] + self.sourceChoice.Items[-1:]
                self.sourceChoice.Select(self.sourceChoice.Items.index(t))
                table = t
            else:
                self.sourceChoice.Select(0)
                return
        self.measurementsChoice.SetItems(get_non_blob_types_from_table(table))
        self.measurementsChoice.Select(0)
        self.colorBar.ResetInterval()
        self.UpdatePlateMaps()

    def on_save_csv(self, evt):
        defaultFileName = 'my_plate_table.csv'
        saveDialog = wx.FileDialog(self, message="Save as:",
                                   defaultDir=os.getcwd(),
                                   defaultFile=defaultFileName,
                                   wildcard='csv|*',
                                   style=(wx.SAVE | wx.FD_OVERWRITE_PROMPT |
                                          wx.FD_CHANGE_DIR))
        if saveDialog.ShowModal() == wx.ID_OK:
            filename = saveDialog.GetPath()
            self.save_to_csv(filename)
            self.Title = filename
        saveDialog.Destroy()

    def save_to_csv(self, filename):
        with open(filename, 'wb') as f:
            w = csv.writer(f)
            w.writerow(['Plate', 'Well', self.measurement + ' ' + self.aggMethod])
            w.writerows(self.keys_and_vals)

        logging.info('Table saved to %s'%filename)

    def OnSelectPlate(self, evt):
        ''' Handles the selection of a plate from the plate choice box. '''
        self.UpdatePlateMaps()

    def OnSelectMeasurement(self, evt=None):
        ''' Handles the selection of a measurement to plot from a choice box. '''
        selected_measurement = self.measurementsChoice.Value 
        table = self.sourceChoice.Value
        numeric_measurements = get_numeric_columns_from_table(table)
        if (selected_measurement in numeric_measurements):
            self.aggregationMethodsChoice.Enable()
            self.colorMapsChoice.Enable()
        else:
            self.aggregationMethodsChoice.Disable()
            self.colorMapsChoice.Disable()
        self.colorBar.ResetInterval()
        self.UpdatePlateMaps()

    def OnSelectAggregationMethod(self, evt=None):
        ''' Handles the selection of an aggregation method from the choice box. '''
        self.colorBar.ResetInterval()
        self.UpdatePlateMaps()

    def OnSelectColorMap(self, evt=None):
        ''' Handles the selection of a color map from a choice box. '''
        map = self.colorMapsChoice.Value
        cm = matplotlib.cm.get_cmap(map)
        self.colorBar.SetMap(map)
        for plateMap in self.plateMaps:
            plateMap.SetColorMap(map)

    def OnSelectWellDisplay(self, evt=None):
        ''' Handles the selection of a well display choice from a choice box. '''
        sel = self.wellDisplayChoice.Value
        if sel.lower() == 'image':
            dlg = wx.MessageDialog(self, 
                'This mode will render each well as a shrunken image loaded '
                'from that well. This feature is currently VERY SLOW since it '
                'requires loading hundreds of full sized images. Are you sure '
                'you want to continue?',
                'Load all images?', wx.OK|wx.CANCEL|wx.ICON_QUESTION)
            if dlg.ShowModal() != wx.ID_OK:
                self.wellDisplayChoice.SetSelection(0)
                return
        if sel.lower() in ['image', 'thumbnail']:
            self.colorBar.Hide()
        else:
            self.colorBar.Show()
        for platemap in self.plateMaps:
            platemap.SetWellDisplay(sel)

    def OnEnterNumberOfPlates(self, evt=None):
        ''' Handles the entry of a plates to view from a choice box. '''
        try:
            nPlates = int(self.numberOfPlatesTE.GetValue())
        except:
            logging.warn('Invalid # of plates! Please enter a number between 1 and 100')
            return
        if nPlates>100:
            logging.warn('Too many plates! Please enter a number between 1 and 100')
            return
        if nPlates<1:
            logging.warn('You must display at least 1 plate.')
            self.numberOfPlatesTE.SetValue('1')
            nPlates = 1
        # Record the indices of the plates currently selected.
        # Pad the list with sequential plate indices then crop to the new number of plates.
        currentPlates = [plateChoice.GetSelection() for plateChoice in self.plateMapChoices]
        currentPlates = (currentPlates+[(currentPlates[-1]+1+p) % len(db.GetPlateNames()) for p in range(nPlates)])[:nPlates]
        # Remove all plateMaps
        self.plateMapSizer.Clear(deleteWindows=True)
        self.plateMaps = []
        self.plateMapChoices = []
        # Restructure the plateMapSizer appropriately
        rows = cols = np.ceil(np.sqrt(nPlates))
        self.plateMapSizer.SetRows(rows)
        self.plateMapSizer.SetCols(cols)
        # Add the plate maps
        for plateIndex in currentPlates:
            self.AddPlateMap(plateIndex)
        self.UpdatePlateMaps()
        self.plateMapSizer.Layout()
        # Update outlines
        self.OnOutlineMarked()
        
    def OnSelectWell(self):
        '''When wells are selected: display their annotation in the annotation
        label control. If multiple annotations are found then make sure the
        user knows.
        '''
        wellkeys = []
        for pm in self.plateMaps:
            wellkeys += pm.get_selected_well_keys()
        if len(wellkeys) > 0 and self.annotationCol.Value != '':
            self.annotationLabel.Enable()
            self.annotationLabel.SetForegroundColour(wx.BLACK)
            self.annotationLabel.SetBackgroundColour(wx.WHITE)
            annotations = db.execute('SELECT %s FROM %s WHERE %s'%(
                                self.annotationCol.Value, 
                                p.image_table, 
                                GetWhereClauseForWells(wellkeys)))
            annotations = list(set([a[0] for a in annotations]))
            if len(annotations) == 1:
                if annotations[0] == None:
                    self.annotationLabel.SetValue('')
                else:
                    self.annotationLabel.SetValue(str(annotations[0]))
            else:
                self.annotationLabel.SetValue(','.join([str(a) for a in annotations if a is not None]))
        else:
            self.annotationLabel.Disable()
            self.annotationLabel.SetForegroundColour(wx.Colour(80,80,80))
            self.annotationLabel.SetBackgroundColour(wx.LIGHT_GREY)
            self.annotationLabel.SetValue('Select wells')
                
    def OnAddAnnotationCol(self, evt):
        '''Add a new user annotation column to the database.
        '''
        dlg = wx.TextEntryDialog(self, 'New annotation column name: User_','Add Annotation Column')
        if dlg.ShowModal() != wx.ID_OK:
            return
        new_column = 'User_'+dlg.GetValue()
        # user-type ==> (sql-type, python-type)
        coltypes = {'Text'   : ('VARCHAR(255)', str), 
                    'Number' : ('FLOAT', float)}
        dlg = wx.SingleChoiceDialog(self, 
                'What type of annotation column would you like to add?\nThis can not be changed.',
                'Add Annotation Column', coltypes.keys(), wx.CHOICEDLG_STYLE)
        if dlg.ShowModal() != wx.ID_OK:
            return
        usertype = dlg.GetStringSelection()
        db.AppendColumn(p.image_table, new_column, coltypes[usertype][0])
        self.annotation_cols[new_column] = coltypes[usertype][1]
        self.annotationCol.Items += [new_column]
        self.annotationCol.SetSelection(len(self.annotation_cols) - 1)
        current_selection = self.measurementsChoice.Selection
        self.measurementsChoice.SetItems(self.measurementsChoice.Strings + [new_column])
        if self.annotationShowVals.IsChecked():
            column = self.annotationCol.Value
            self.sourceChoice.SetStringSelection(p.image_table)
            self.measurementsChoice.SetStringSelection(column)
            self.UpdatePlateMaps()
        else:
            self.measurementsChoice.SetSelection(current_selection[0])
        self.annotationShowVals.Enable()
        self.outlineMarked.Enable()
        self.OnSelectWell()
        
    def OnSelectAnnotationCol(self, evt=None):
        '''Handles selection of an annotation column.
        '''
        col = self.annotationCol.Value
        if col == '':
            return
        coltype = self.annotation_cols[col]
        self.OnSelectWell()
        self.OnOutlineMarked()
        if self.annotationShowVals.IsChecked():
            if coltype != str:
                self.colorMapsChoice.Enable()
            else:
                self.colorMapsChoice.Disable()
            self.measurementsChoice.SetStringSelection(col)
            self.UpdatePlateMaps()
        
    def OnEnterAnnotation(self, evt=None):
        '''Store the annotation value in the annotation column of the db.
        '''
        if evt.KeyCode < 32 or evt.KeyCode > 127:
            return
        column = self.annotationCol.Value
        value = self.annotationLabel.Value
        wellkeys = []
        for pm in self.plateMaps:
            wellkeys += pm.get_selected_well_keys()
        if value == '':
            value = None
        elif self.annotation_cols[column] == float:
            try:
                value = float(value)
                self.annotationLabel.SetForegroundColour(wx.BLACK)
            except:
                self.annotationLabel.SetForegroundColour(wx.Color(255,0,0))
                return
        db.UpdateWells(p.image_table, column, value, wellkeys)
        if self.outlineMarked.IsChecked():
            for pm in self.plateMaps:
                if value is None:
                    pm.UnOutlineWells(wellkeys)
                else:
                    pm.OutlineWells(wellkeys)
        if (self.sourceChoice.Value == p.image_table and 
            self.measurementsChoice.Value == column):
            self.UpdatePlateMaps()

    def OnOutlineMarked(self, evt=None):
        '''Outlines all non-NULL values of the current annotation
        '''
        # Disable filters when outlining marked wells
        #if self.outlineMarked.IsChecked():
            #self.filterChoice.SetStringSelection(FilterComboBox.NO_FILTER)
            #self.filterChoice.Disable()
        #else:
            #if not self.annotationShowVals.IsChecked():
                #self.filterChoice.Enable()
        # Update outlined wells in PlateMapPanels
        for pm in self.plateMaps:
            if self.outlineMarked.IsChecked():
                column = self.annotationCol.Value
                if p.plate_id:
                    res = db.execute('SELECT %s, %s FROM %s WHERE %s="%s"'%(
                        dbconnect.UniqueWellClause(), column, p.image_table, 
                        p.plate_id, pm.plate))
                else:
                    # if there's no plate_id, we assume there is only 1 plate
                    # and fetch all the data
                    res = db.execute('SELECT %s, %s FROM %s'%(
                        dbconnect.UniqueWellClause(), column, p.image_table))
                keys = [tuple(r[:-1]) for r in res if r[-1] is not None]
                pm.SetOutlinedWells(keys)
            else:
                pm.SetOutlinedWells([])
        self.UpdatePlateMaps()
                
    def OnShowAnnotationValues(self, evt=None):
        '''Handler for the show values checkbox.
        '''
        if self.annotationShowVals.IsChecked():
            column = self.annotationCol.Value
            self.sourceChoice.SetStringSelection(p.image_table)
            self.measurementsChoice.SetItems(get_non_blob_types_from_table(p.image_table))            
            self.measurementsChoice.SetStringSelection(column)
            self.filterChoice.SetStringSelection(FilterComboBox.NO_FILTER)
            self.sourceChoice.Disable()
            self.measurementsChoice.Disable()
            self.filterChoice.Disable()
            self.aggregationMethodsChoice.Disable()
            self.aggregationMethodsChoice.SetValue('none')
        else:
            self.sourceChoice.Enable()
            self.measurementsChoice.Enable()
            if not self.outlineMarked.IsChecked():
                self.filterChoice.Enable()
            if db.GetColumnType(self.sourceChoice.Value, self.measurementsChoice.Value) != str:
                self.aggregationMethodsChoice.Enable()
                self.aggregationMethodsChoice.SetSelection(0)
        self.UpdatePlateMaps() 
        
    def OnSelectFilter(self, evt):
        self.filterChoice.on_select(evt)
        self.UpdatePlateMaps()
        self.colorBar.ResetInterval()
        
    def save_settings(self):
        '''save_settings is called when saving a workspace to file.
        
        returns a dictionary mapping setting names to values encoded as strings
        '''
        settings = {'table' : self.sourceChoice.Value,
                    'measurement' : self.measurementsChoice.Value,
                    'aggregation' : self.aggregationMethodsChoice.Value,
                    'colormap' : self.colorMapsChoice.Value,
                    'well display' : self.wellDisplayChoice.Value,
                    'number of plates' : self.numberOfPlatesTE.GetValue(),
                    }
        for i, choice in enumerate(self.plateMapChoices):
            settings['plate %d'%(i+1)] = choice.Value
        return settings
    
    def load_settings(self, settings):
        '''load_settings is called when loading a workspace from file.
        
        settings - a dictionary mapping setting names to values encoded as
                   strings.
        '''
        if 'table' in settings:
            self.sourceChoice.SetStringSelection(settings['table'])
            self.UpdateMeasurementChoice()
        if 'measurement' in settings:
            self.measurementsChoice.SetStringSelection(settings['measurement'])
            self.OnSelectMeasurement()
        if 'aggregation' in settings:
            self.aggregationMethodsChoice.SetStringSelection(settings['aggregation'])
            self.OnSelectAggregationMethod()
        if 'colormap' in settings:
            self.colorMapsChoice.SetStringSelection(settings['colormap'])
            self.OnSelectColorMap()
        if 'number of plates' in settings:
            self.numberOfPlatesTE.SetValue(settings['number of plates'])
            self.OnEnterNumberOfPlates()
        for s, v in settings.items():
            if s.startswith('plate '):
                self.plateMapChoices[int(s.strip('plate ')) - 1].SetValue(v)
        # set well display last since each step currently causes a redraw and
        # this could take a long time if they are displaying images
        if 'well display' in settings:
            self.wellDisplayChoice.SetStringSelection(settings['well display'])
            self.OnSelectWellDisplay()
Beispiel #4
0
class PelvisFrame(wx.Frame):
    """The main application window for PELvis"""

    #Menu ID constants
    ID_OPEN = 100
    ID_OPENTWO = 110
    ID_SAVE = 130
    ID_SPECTRUM=140
    ID_IMAGE_ARRAY=160
    ID_EXIT = 190

    ID_GREY = 200
    ID_HUEVAL = 220
    ID_SPECTRAL = 230
    ID_PICKER = 290
    ID_POLAR = 300
    ID_FLIPPING = 310
    ID_SPIN_UP = 320
    ID_SPIN_DOWN = 330

    ID_FLAT = 420
    ID_FAKEFLAT = 430
    ID_ROD = 440
    ID_EXPORT_ROI = 450
    ID_IMPORT_ROI = 460

    ID_COPY = 500

    def __init__(self,Yield):
        """Create a PELvis frame
          
        Keyword arguments:
        Yield -- A function to give control back to the main event loop
          
        """
        wx.Frame.__init__(self,None,wx.ID_ANY,"PEL Visualizer")
        self.data = PelFile()
        self.Yield = Yield
        self.mask = np.ones((128,16),dtype=np.bool)
        #Create items in the frame
        self.yPanel = GraphPanel(self,(2,8),64,GraphPanel.VERTICAL)
        self.xPanel = GraphPanel(self,(8,2),64,GraphPanel.INVERTED)
        self.colorbar = ColorBarPanel(self,cm.jet)
        self.opPanel = PelvisOptionPanel(self)
        self.posPanel = PositionPanel(self)
        self.imPanel = ImagePanel(self,self.posPanel.set,
                                  self.opPanel.setPosMin,self.opPanel.setPosMax)
        self.specDlg = SpectrumDialog(self)
          
        self.cmp = None #color map
        self.imageSaveDialog=wx.FileDialog(self,"Choose graphics file",wildcard="Portable Network Graphic (png)|*.PNG|Windows Bitmap (bmp)|*.BMP|Joint Photographic Experts Group (jpg)|*.JPG|Portable Network Monocrome (pnm)|*.PNM|Tagged Image File Format (tif)|*.TIF|Archaic, useless format (pcx)|*.PCX",style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
          
        self.update = self.updateSingle#update the image
        self.updateData = self.updateSingleData#update the data in the image
          
        #Create the menu
        menubar = wx.MenuBar()
        filemenu = wx.Menu()
        editmenu = wx.Menu()
        scalemenu = wx.Menu()
        analysismenu = wx.Menu()
        noisemenu = wx.Menu()
          
        #populate the menu
        filemenu.Append(self.ID_OPEN,"&Open\tCtrl-O"," Open a PEL file")
        filemenu.Append(self.ID_OPENTWO,"&Polarized Set"," Open two PEL files")
        filemenu.Append(self.ID_SAVE,"&Save\tCtrl-S"," Save an image file")
        filemenu.Append(self.ID_SPECTRUM,"Spectrum"," View the TOF spectrum")
        filemenu.Append(self.ID_IMAGE_ARRAY,"&Export Images..."," Save a series of TOF snapshots")
        filemenu.Append(self.ID_EXIT,"&Quit\tCtrl-Q"," Quit")
          
        editmenu.Append(self.ID_COPY,"&Copy\tCtrl-c","Copy the current image to the clipboard")
          
        scalemenu.Append(self.ID_GREY,"Greyscale\tCtrl-G","Monochrome images")
        scalemenu.Append(self.ID_HUEVAL,"Hue and Value\tCtrl-H","Scaled Rainbow Images")
        scalemenu.Append(self.ID_SPECTRAL,"spectral","Uses spectrum of light")
        scalemenu.Append(self.ID_PICKER,"Map Picker..."," Select from the full list of colormaps")
          
        analysismenu.Append(self.ID_POLAR,"Check Polarization\tCtrl-P","2d plot of polarization data")
        analysismenu.Append(self.ID_FLIPPING,"Check Flipping Ratio\tCtrl-F","2d plot of  spin up over spin down")
        analysismenu.Append(self.ID_SPIN_UP,"View Spin Up State\tCtrl-U","2d plot of  spin up")
        analysismenu.Append(self.ID_SPIN_DOWN,"View Spin Down State\tCtrl-D","2d plot of  spin down")
          
        noisemenu.Append(self.ID_FLAT,"&Load Flat"," Load a blank run for background subtraction")
        noisemenu.Append(self.ID_FAKEFLAT,"Si&mulate Flat"," Drop out background within the same image")
        noisemenu.Append(self.ID_ROD,"Region of &Disinterest"," Drop out background within the same image")
        noisemenu.Append(self.ID_EXPORT_ROI,"Export ROI"," Export a binary file corresponding to where the data is above the minimum intensity.")
        noisemenu.Append(self.ID_IMPORT_ROI,"Import ROI"," Add another exclusion mask.")
          
          
        #Bind events to the menu
        self.Connect(self.ID_EXIT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnExit)
        self.Connect(self.ID_OPEN,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnOpen)
        self.Connect(self.ID_OPENTWO,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnOpenSet)
        self.Connect(self.ID_SAVE,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSave)
        self.Connect(self.ID_SPECTRUM,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSpectrum)
        self.Connect(self.ID_IMAGE_ARRAY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnImageArray)
        self.Connect(self.ID_GREY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnGrey)
        self.Connect(self.ID_HUEVAL,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnHueVal)
        self.Connect(self.ID_SPECTRAL,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnSpectral)
        self.Connect(self.ID_PICKER,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnPicker)
        self.Connect(self.ID_POLAR,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnPolar)
        self.Connect(self.ID_FLIPPING,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFlipping)
        self.Connect(self.ID_SPIN_UP,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnAnalysisSpinUp)
        self.Connect(self.ID_SPIN_DOWN,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnAnalysisSpinDown)
          
        self.Connect(self.ID_FLAT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFlat)
        self.Connect(self.ID_FAKEFLAT,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnFakeFlat)
        self.Connect(self.ID_ROD,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnROD)
        self.Connect(self.ID_EXPORT_ROI,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnExportROI)
        self.Connect(self.ID_IMPORT_ROI,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnImportROI)
        self.Connect(self.ID_COPY,-1,wx.wxEVT_COMMAND_MENU_SELECTED,self.OnCopy)
        
          
        menubar.Append(filemenu,"&File")
        menubar.Append(editmenu,"&Edit")
        menubar.Append(scalemenu,"&Color")
        menubar.Append(analysismenu,"&Analysis")
        menubar.Append(noisemenu,"&Noise")
        self.SetMenuBar(menubar)
          
        #arrange window
        sizer = wx.GridBagSizer()
        sizer.Add(self.colorbar,pos=wx.GBPosition(0,9),span=wx.GBSpan(9,1))
        sizer.Add(self.imPanel,pos=wx.GBPosition(0,1),span=wx.GBSpan(8,8))
        sizer.Add(self.yPanel,pos=wx.GBPosition(0,0),span=wx.GBSpan(8,1))
        sizer.Add(self.xPanel,pos=wx.GBPosition(8,1),span=wx.GBSpan(1,8))
        sizer.Add(self.opPanel,pos=wx.GBPosition(0,10),span=wx.GBSpan(8,1),flag=wx.EXPAND)
        sizer.Add(self.posPanel,pos=wx.GBPosition(8,0),flag=wx.EXPAND)
        self.progress = wx.Gauge(self,range=1000)
        sizer.Add(self.progress,pos=wx.GBPosition(9,0),span=wx.GBSpan(1,11),flag=wx.EXPAND)
          
        updateButton = wx.Button(self,-1,"Update")
        updateButton.Bind(wx.EVT_BUTTON,self.OnUpdateButton)
        sizer.Add(updateButton,flag=wx.EXPAND,pos=wx.GBPosition(8,10))
          
        self.data = self.makePel()
        self.flatrun = None#background data
        
        sizer.SetSizeHints(self)
        self.SetSizer(sizer)
        self.Show(True)

    def makePel(self):
        """Create a blank Pel object for loading detector data"""
        data = PelFile()
        def statusfunc(x):
            self.progress.SetValue(x)
            self.Yield()
        data.statusfunc = statusfunc
        return data
    def loadPel(self,message):
        """Load a .pel file and its monitor data.
        
        Keyword arguments:
        message -- The title for the load file dialog.
        
        """
        dlg=wx.FileDialog(self,message,wildcard="He3 data|*neutron_event.dat|Preformatted Histograms|*.npy",style=wx.FD_OPEN)
        if dlg.ShowModal()==wx.ID_OK:
        #            self.SetCursor(wx.CURSOR_WAIT)
            path = dlg.GetPath()
            if path[-3:] == "dat":
                data = self.makePel()
                data.readfileimage(path)
            elif path[-3:] == "npy":
                data = np.load(path)
        #            self.SetCursor(wx.CURSOR_ARROW)
        else:
            return (None,None)
        mon = MonFile(path[:-17]+"bmon_histo.dat")
        return (data,mon)

    
    def OnImageArray(self,event):
        """Exports the 2d detector image by wavelength"""
        dlg = self.imageSaveDialog
        if dlg.ShowModal()==wx.ID_OK:
            path=dlg.GetPath()
            ext = path[-4:]
            path = path[:-4]
            (lmin,lmax) = self.opPanel.getLambdaRange()
            for i in range(lmin,lmax):
                file=path+("%03i"%i)+ext
                self.opPanel.setLambdaRange(0.1*i,0.1*(i+1))
                self.updateData()
                self.update()
                self.imPanel.saveImage(file)
                self.progress.SetValue(1000*(i-lmin)/(lmax-lmin))
                self.Yield()
            self.opPanel.setLambdaRange(lmin*0.1,lmax*0.1)
            self.updateData()
            self.progress.SetValue(0)

    
    def loadNormPel(self,message):
        """Load a .pel file, normalize it by monitor, and subtract background"""
        (data,mon) = self.loadPel(message)
        if isinstance(data,PelFile):
            data = np.asarray(data.make3d(),np.float32)
        if mon is None:
            return (data,1)
        if self.flatrun != None:
            flatrun = np.load(self.flatrun)
            self.flatrun.seek(0)
            flatrun *= mon.time
            data -= flatrun
        spec = mon.spec
        monsum = np.sum(spec)
        print("Integrated monitor counts: " + str(monsum))
        data /= monsum
        return (data,np.sum(mon.spec))
                
#    def getLambdaRange(self):
#        try:
#            lmin = int(float(self.lambdaMin.GetValue())*10)
#        except ValueError:
#            lmin = 0
#        try:
#            lmax = int(float(self.lambdaMax.GetValue())*10)
#        except ValueError:
#            lmax = 200 
#        return (lmin,lmax)

    def updateSingleData(self,event=None):
        """Update changes in wavelength on a single file"""
        print("Make 2d")
        (lmin,lmax) = self.opPanel.getLambdaRange()
        self.flatdata = np.sum(self.data[:,:,lmin:lmax],2)
        self.update()
      
    def updateDataFlip(self,event=None):
        """Update changes in wavelength for flipping ratios"""
        (lmin,lmax) = self.opPanel.getLambdaRange()
        (u3d,d3d)=self.data
        u = np.sum(u3d[:,:,lmin:lmax],2)
        d = np.sum(d3d[:,:,lmin:lmax],2)
        self.flatdata = u/(d+1e-6)
        self.update()
      
    def updateDataPolar(self,event=None):
        """Update changes in wavelength for polarizations"""
        (lmin,lmax) = self.opPanel.getLambdaRange()
        (u3d,d3d)=self.data
        u = np.sum(u3d[:,:,lmin:lmax],2)
        d = np.sum(d3d[:,:,lmin:lmax],2)
        self.flatdata = (u-d)/(u+d+1e-6)
        self.update()
    
    def updateDataUp(self,event=None):
        """Update changes in wavelength for the spin up state"""
        (lmin,lmax) = self.opPanel.getLambdaRange()
        (u3d,_)=self.data
        self.flatdata = np.sum(u3d[:,:,lmin:lmax],2)
        self.update()
      
    def updateDataDown(self,event=None):
        """Update changes in wavelength for the spin down state"""
        (lmin,lmax) = self.opPanel.getLambdaRange()
        (_,d3d)=self.data
        self.flatdata = np.sum(d3d[:,:,lmin:lmax],2)
        self.update()
      
    def updateSingle(self,event=None):
        """Update the 2D data for the region of interest and intensity"""
        (vMin,vMax) = self.opPanel.getIntensityRange()
        (xMin,xMax,yMin,yMax) = self.opPanel.getRoi()
        data = self.flatdata[:,:]
      
        #Mask to zero during the summing parts
        data[np.logical_not(self.mask)] = 0
        self.posPanel.data = data
        self.posPanel.setRange(xMin,yMin,xMax,yMax)
        x=np.arange(128,0,-1)
        y=np.sum(data[:,xMin:xMax],axis=1)
        self.yPanel.SetPlot(x,y)
        #handle the x-plot
        x=np.arange(0,16,1)
        y=np.sum(data[yMin:yMax,:],axis=0)
        self.xPanel.SetPlot(x,y)
        if vMin is None:
            vMin = np.min(data)
        if vMax is None:
            vMax = np.max(data)
        self.colorbar.setRange(vMin,vMax)
        self.colorbar.update()
        #mask to vmin for the plotting
        data[np.logical_not(self.mask)] = vMin
        self.imPanel.update(self.flatdata,vMin,vMax)
    

    def OnUpdateButton(self,event):
        """Refresh the data when the user pushes the "Update" button"""
        #This function is needed for wxWidgets to allow
        #for dynamically changing the bound function
        self.updateData(event)

    def OnOpen(self,event):
        """Load a single .pel file for display"""
        data,scale = self.loadNormPel("Choose the Pel File to Open")
        if data is None:
            return
        self.data = data
        self.scale = scale
        self.progress.SetValue(0)
        self.specDlg.setMode("up")
        self.updateData = self.updateSingleData
        self.update = self.updateSingle
        self.updateData()

    def OnOpenSet(self,event):
        """Load a spin flip measurement for display"""
        if self.loadUpAndDown():
            self.OnPolar(event)

    def OnFlat(self,event):
        """Load a blank run for background subtraction"""
        (data,mon) = self.loadPel("Choose a Blank Run")
        if data == None:
            return
        if isinstance(data,PelFile):
            flatrun = data.make3d()
        elif isinstance(data,np.ndarray):
            flatrun = data
        flatrun = np.sum(flatrun,axis=2)
        flatrun /= RESOLUTION
        flatrun /= float(mon.time)
        flatrun = np.expand_dims(flatrun,2)
        self.flatrun = TemporaryFile()
        np.save(self.flatrun,flatrun)
        self.flatrun.seek(0)
        self.progress.SetValue(0)
      
    def OnFakeFlat(self,event):
        """Create a fake background run from outside the region of interest."""
        (xMin,xMax,yMin,yMax)=self.opPanel.getRoi()
        totarea = 512*512
        centarea = (yMax-yMin)*(xMax-xMin)
        backgroundarea = totarea-centarea
        if type(self.data) is tuple:
            (u,d)=self.data
      
            totu = np.sum(u)
            totd = np.sum(d)
            centu = np.sum(u[yMin:yMax,xMin:xMax,:])
            centd = np.sum(d[yMin:yMax,xMin:xMax,:])
      
            backgroundu = totu-centu
            backgroundd = totd-centd
            backgroundrateu = backgroundu/backgroundarea
            backgroundrated = backgroundd/backgroundarea
            backgroundrateu /= (RESOLUTION + 1) #normalize against the wavelengths
            backgroundrated /= (RESOLUTION + 1) #normalize against the wavelengths
            ###Stupid Memory Errors
            del self.data
            u -= backgroundrateu
            d -= backgroundrated
            ###
            self.data=(u,d)
        else:
            d=self.data
            tot = np.sum(d)
            cent = np.sum(d[yMin:yMax,xMin:xMax,:])
            background = tot-cent
            backgroundrate = background/backgroundarea
            backgroundrate /= (RESOLUTION + 1) #normalize against the wavelengths
            self.data-=backgroundrate
        self.updateData()

    #Subtract out the region of disinterest
    def OnROD(self,event):
        """Take the region of interest as background noise"""
        (xMin,xMax,yMin,yMax)=self.opPanel.getRoi()
        area = (yMax-yMin)*(xMax-xMin)
        if type(self.data) is tuple:
            u,d=self.data
            del self.data
            totu = np.sum(np.sum(u[yMin:yMax,xMin:xMax,:],axis=0),axis=0)
            totd = np.sum(np.sum(d[yMin:yMax,xMin:xMax,:],axis=0),axis=0)
            totu /= area
            totd /= area
            u -= totu
            d -= totd
            self.data=(u,d)
        else:
            d=self.data
            totd = np.sum(np.sum(d[yMin:yMax,xMin:xMax,:],axis=0),axis=0)
            #totd = np.atleast_3d(totd)
            totd /= area
            #print(totd.shape)
            #print(self.data.shape)
            self.data -= totd
        self.updateData()
    
    def OnExportROI(self,event):
        """Save a file containing a map of where the current data
        image is greater than vmin"""
        vMin,_ = self.opPanel.getIntensityRange()
        mask = self.flatdata > vMin
        # (vMin,vMax) = self.opPanel.getIntensityRange()#
        # (xMin,xMax,yMin,yMax) = self.opPanel.getRoi()#
        # (lMin,lMax) = self.opPanel.getLambdaRange()#
        # (lMin,lMax) = (lMin/10,lMax/10)#
        # mask = [["xMin",xMin], ["xMax",xMax], ["yMin",yMin], ["yMax",yMax], \
        #         ["lMin",lMin], ["lMax",lMax], ["vMin",vMin], ["vMax",vMax]]#
        dlg = wx.FileDialog(self,
                            "Where to save the mask file?",
                            wildcard="Numpy dump (npy)|*.npy|Text (dat)|*.dat",
                            style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
        if dlg.ShowModal()==wx.ID_OK:
            path=dlg.GetPath()
            ext = path[-4:]
            if ext == ".dat":
                np.savetxt(path,mask,fmt="%d")
            else:
                np.save(path,mask)
    
    def OnImportROI(self,event):
        """Adds another mask to the current system mask"""
        dlg = wx.FileDialog(self,
                            "Which Mask File?",
                            wildcard="Numpy dump (npy)|*.npy|Text (dat)|*.dat",
                            style=wx.FD_OPEN)
        time.sleep(.1)
        if dlg.ShowModal()==wx.ID_OK:
            path = dlg.GetPath()
            ext = path[-4:]
            if ext == ".dat":
                newmask = np.loadtxt(path,dtype=np.bool)
                newmask=dict(newmask)#
            else:
                newmask = np.load(path)
            self.mask = np.logical_and(self.mask,newmask)
            #self.opPanel.setPosMin(newmask["xMin"],newmask["yMin"])#
            #self.opPanel.setPosMax(newmask["xMax"],newmask["yMax"])#
            #self.opPanel.setLambdaRange(newmask["lMin"],newmask["lMax"])#
            #self.opPanel.setIntensityRange(newmask["vMin"],newmask["vMax"])#
            self.updateData()
    
    
    def OnSave(self,event):
        """Save the current 2D image to a file"""
        print("OnSave")
    #        dlg=wx.FileDialog(self,"Choose graphics file",wildcard="Windows Bitmap (bmp)|*.BMP|Portable Network Graphic (png)|*.PNG|Joint Photographic Experts Group (jpg)|*.JPG|Portable Network Monocrome (pnm)|*.PNM|Tagged Image File Format (tif)|*.TIF|Archaic, useless format (pcx)|*.PCX",style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
        dlg = self.imageSaveDialog
        if dlg.ShowModal()==wx.ID_OK:
            self.imPanel.saveImage(dlg.GetPath())
    
    def OnSpectrum(self,event):
        """Display a plot of the region of interest versus wavelength"""
        print("OnSpectrum")
        (xMin,xMax,yMin,yMax)=self.opPanel.getRoi()
        if type(self.data) is tuple:
            u3d,d3d = self.data
            u3d = u3d[:,:,:]
            d3d = d3d[:,:,:]
            u3d[np.logical_not(self.mask)] = 0
            d3d[np.logical_not(self.mask)] = 0
            u = np.sum(np.sum(u3d[yMin:yMax,xMin:xMax],0),0)
            d = np.sum(np.sum(d3d[yMin:yMax,xMin:xMax],0),0)
            uscale,dscale = self.scale
            self.specDlg.setScale(uscale,dscale)            
            self.specDlg.setData(u,d)
        else:
            copy = self.data[:,:,:]
            copy[np.logical_not(self.mask)] = 0
            u = np.sum(np.sum(copy[yMin:yMax,xMin:xMax],0),0)
            #            u *= self.scale
            self.specDlg.setScale(self.scale)
            self.specDlg.setData(u)
        self.specDlg.setIntensityRange(self.opPanel.getIntensityRange())
        self.specDlg.Show()
      
    def OnGrey(self,event):
        """Set the colormap to gray"""
        self.imPanel.cmap = cm.gray
        self.colorbar.setCmap(cm.gray)
        self.update()
    
    def OnHueVal(self,event):
        """Set the colormap to a rainbow"""
        self.imPanel.cmap = cm.jet
        self.colorbar.setCmap(cm.jet)
        self.update()
    
    def OnSpectral(self,event):
        """Set the colormap to the spectral map"""
        self.imPanel.cmap = cm.spectral
        self.colorbar.setCmap(cm.spectral)
        self.update()
    
    def OnPicker(self,event):
        """Let the user pick a color map from a list"""
        if self.cmp is None:
            self.cmp = ColorMapPicker(self,self.setColorMap)
        self.cmp.Show()
      
    def setColorMap(self,cmap):
        """Changes to the given colormap"""
        self.imPanel.cmap = cmap
        self.colorbar.setCmap(cmap)
        self.update()


      
    def OnExit(self,event):
          """Quit the program"""
          self.Close()
    def loadUpAndDown(self):
        """Read in spin flip data"""
        u3d,uscale = self.loadNormPel("Spin Up State")
        if u3d is None:
            return False
        del self.data
        d3d,dscale = self.loadNormPel("Spin Down State")
        self.data = (u3d,d3d)
        self.scale = (uscale,dscale)
        return True

    def OnPolar(self,event):
        """Display neutron polarization"""
        print("OnPolar")
        self.specDlg.setMode("polar")
        self.updateData = self.updateDataPolar
        self.update = self.updateSingle
        self.updateData()
      
    def OnFlipping(self,event):
        """Display the flipping ratio"""
        print("OnFlip")
        self.specDlg.setMode("flipping")
        self.updateData = self.updateDataFlip
        self.update = self.updateSingle
        self.updateData()
      
    def OnAnalysisSpinUp(self,event):
        """Display the Spin Up data"""
        print("OnSpinUp")
        self.specDlg.setMode("up")
        self.updateData = self.updateDataUp
        self.update = self.updateSingle
        self.updateData()
      
    def OnAnalysisSpinDown(self,event):
        """Display the Spin Down data"""
        print("OnSpinDown")
        self.specDlg.setMode("down")
        self.updateData = self.updateDataDown
        self.update = self.updateSingle
        self.updateData()
        
    def OnCopy(self,event):
        """Copy the image to a clipboard"""
        self.imPanel.copyToClipboard()