Exemplo n.º 1
0
class DataSourcePanel(wx.Panel):
    '''
    A panel with controls for selecting the source data for a histogramplot 
    '''
    def __init__(self, parent, figpanel, **kwargs):
        wx.Panel.__init__(self, parent, **kwargs)

        # the panel to draw charts on
        self.SetBackgroundColour('white')  # color for the background of panel
        self.figpanel = figpanel

        sizer = wx.BoxSizer(wx.VERTICAL)

        self.table_choice = ui.TableComboBox(self, -1, style=wx.CB_READONLY)
        self.x_choice = ComboBox(self,
                                 -1,
                                 size=(200, -1),
                                 choices=[''],
                                 style=wx.CB_READONLY)
        self.x_choice.Select(0)
        self.bins_input = wx.SpinCtrl(self, -1, '100')
        self.bins_input.SetRange(1, 400)
        self.x_scale_choice = ComboBox(
            self,
            -1,
            choices=[LINEAR_SCALE, LOG_SCALE, LOG2_SCALE],
            style=wx.CB_READONLY)
        self.x_scale_choice.Select(0)
        self.y_scale_choice = ComboBox(self,
                                       -1,
                                       choices=[LINEAR_SCALE, LOG_SCALE],
                                       style=wx.CB_READONLY)
        self.y_scale_choice.Select(0)
        self.filter_choice = ui.FilterComboBox(self, style=wx.CB_READONLY)
        self.filter_choice.Select(0)
        self.gate_choice = ui.GateComboBox(self, style=wx.CB_READONLY)
        self.gate_choice.set_gatable_columns([self.x_column])
        self.update_chart_btn = wx.Button(self, -1, "Update Chart")

        self.update_column_fields()

        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "x-axis:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.table_choice, 1, wx.EXPAND)
        sz.AddSpacer((3, -1))
        sz.Add(self.x_choice, 2, wx.EXPAND)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1, 2))

        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "x-scale:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.x_scale_choice, 1, wx.EXPAND)
        sz.AddSpacer((5, -1))
        sz.Add(wx.StaticText(self, -1, "y-scale:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.y_scale_choice, 1, wx.EXPAND)
        sz.AddSpacer((5, -1))
        sz.Add(wx.StaticText(self, -1, "bins:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.bins_input)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1, 2))

        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "filter:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.filter_choice, 1, wx.EXPAND)
        sz.AddSpacer((5, -1))
        sz.Add(wx.StaticText(self, -1, "gate:"), 0, wx.TOP, 4)
        sz.AddSpacer((2, -1))
        sz.Add(self.gate_choice, 1, wx.EXPAND)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1, 2))

        sizer.Add(self.update_chart_btn)

        wx.EVT_COMBOBOX(self.table_choice, -1, self.on_table_selected)
        wx.EVT_BUTTON(self.update_chart_btn, -1, self.update_figpanel)
        self.gate_choice.addobserver(self.on_gate_selected)

        self.SetSizer(sizer)
        self.Show(1)

    @property
    def x_column(self):
        return sql.Column(
            self.table_choice.GetString(self.table_choice.GetSelection()),
            self.x_choice.GetString(self.x_choice.GetSelection()))

    @property
    def filter(self):
        return self.filter_choice.get_filter_or_none()

    def on_table_selected(self, evt):
        table = self.table_choice.Value
        if table == ui.TableComboBox.OTHER_TABLE:
            t = ui.get_other_table_from_user(self)
            if t is not None:
                self.table_choice.Items = self.table_choice.Items[:-1] + [
                    t
                ] + self.table_choice.Items[-1:]
                self.table_choice.Select(self.table_choice.Items.index(t))
            else:
                self.table_choice.Select(0)
                return
        self.update_column_fields()

    def on_gate_selected(self, gate_name):
        self.update_gate_helper()

    def update_gate_helper(self):
        gate_name = self.gate_choice.get_gatename_or_none()
        if gate_name:
            self.figpanel.gate_helper.set_displayed_gate(
                p.gates[gate_name], self.x_column, None)
        else:
            self.figpanel.gate_helper.disable()

    def update_column_fields(self):
        tablename = self.table_choice.GetString(
            self.table_choice.GetSelection())
        fieldnames = self.get_numeric_columns_from_table(tablename)
        self.x_choice.Clear()
        self.x_choice.AppendItems(fieldnames)
        self.x_choice.SetSelection(0)

    def get_numeric_columns_from_table(self, table):
        ''' Fetches names of numeric columns for the given table. '''
        measurements = db.GetColumnNames(table)
        types = db.GetColumnTypes(table)
        return [
            m for m, t in zip(measurements, types) if t in [float, int, long]
        ]

    def _plotting_per_object_data(self):
        return (p.object_table and p.object_table
                in [self.x_column.table, self.x_column.table]
                or (self.x_column.table != p.image_table
                    and db.adjacent(p.object_table, self.x_column.table)))

    def update_figpanel(self, evt=None):
        self.gate_choice.set_gatable_columns([self.x_column])
        points = self._load_points()
        bins = int(self.bins_input.GetValue())
        self.figpanel.set_x_label(self.x_column.col)
        self.figpanel.set_x_scale(
            self.x_scale_choice.GetString(self.x_scale_choice.GetSelection()))
        self.figpanel.set_y_scale(
            self.y_scale_choice.GetString(self.y_scale_choice.GetSelection()))
        self.figpanel.setpoints(points, bins)
        self.update_gate_helper()
        self.figpanel.draw()

    def _load_points(self):
        q = sql.QueryBuilder()
        select = [self.x_column]
        q.set_select_clause(select)
        if self.filter is not None:
            q.add_filter(self.filter)

        return np.array(db.execute(str(q))).T[0]

    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
        '''
        d = {
            'table':
            self.table_choice.GetString(self.table_choice.GetSelection()),
            'x-axis':
            self.x_choice.GetString(self.x_choice.GetSelection()),
            'bins':
            self.bins_input.GetValue(),
            'x-scale':
            self.x_scale_choice.GetString(self.x_scale_choice.GetSelection()),
            'y-scale':
            self.y_scale_choice.GetString(self.y_scale_choice.GetSelection()),
            'filter':
            self.filter_choice.GetString(self.filter_choice.GetSelection()),
            'x-lim':
            self.figpanel.subplot.get_xlim(),
            'y-lim':
            self.figpanel.subplot.get_ylim(),
        }
        if self.gate_choice.get_gatename_or_none():
            d['gate'] = self.gate_choice.GetString(
                self.gate_choice.GetSelection())
        return d

    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.table_choice.SetStringSelection(settings['table'])
            self.update_column_fields()
        if 'x-axis' in settings:
            self.x_choice.SetStringSelection(settings['x-axis'])
        if 'bins' in settings:
            self.bins_input.SetValue(int(settings['bins']))
        if 'x-scale' in settings:
            self.x_scale_choice.SetStringSelection(settings['x-scale'])
        if 'y-scale' in settings:
            self.y_scale_choice.SetStringSelection(settings['y-scale'])
        if 'filter' in settings:
            self.filter_choice.SetStringSelection(settings['filter'])
        self.update_figpanel()
        if 'x-lim' in settings:
            self.figpanel.subplot.set_xlim(eval(settings['x-lim']))
        if 'y-lim' in settings:
            self.figpanel.subplot.set_ylim(eval(settings['y-lim']))
        if 'gate' in settings:
            self.gate_choice.SetStringSelection(settings['gate'])
            self.figpanel.gate_helper.set_displayed_gate(
                p.gates[settings['gate']], self.x_column, None)
        self.figpanel.draw()
Exemplo n.º 2
0
class ColumnFilterPanel(wx.Panel):
    '''
    Creates a UI that allows the user to create WHERE clauses by selecting 
    1) a DB column name, 2) a comparator, and 3) a value
    '''
    def __init__(self,
                 parent,
                 tables,
                 allow_delete=True,
                 expression=None,
                 **kwargs):
        wx.Panel.__init__(self, parent, **kwargs)

        self.fieldSets = []
        self.tables = tables
        self.types = {}
        self.types[p.image_table] = db.GetColumnTypes(p.image_table)
        self.tableChoice = ComboBox(self,
                                    choices=self.tables,
                                    size=(150, -1),
                                    style=wx.CB_READONLY)
        self.tableChoice.Select(0)
        self.colChoice = ComboBox(self,
                                  choices=db.GetColumnNames(p.image_table),
                                  size=(150, -1),
                                  style=wx.CB_READONLY)
        self.colChoice.Select(0)
        self.comparatorChoice = ComboBox(self, size=(80, -1))
        self.update_comparator_choice()
        self.valueField = wx.ComboBox(self, -1, value='')
        if allow_delete:
            self.x_btn = wx.Button(self, -1, 'x', size=(30, -1))

##        if expression is not None:
##            self.set_expression(expression)

        colSizer = wx.BoxSizer(wx.HORIZONTAL)
        colSizer.Add(self.tableChoice, 1, wx.EXPAND)
        colSizer.AddSpacer((5, -1))
        colSizer.Add(self.colChoice, 1, wx.EXPAND)
        colSizer.AddSpacer((5, -1))
        colSizer.Add(self.comparatorChoice, 0.5, wx.EXPAND)
        colSizer.AddSpacer((5, -1))
        colSizer.Add(self.valueField, 1, wx.EXPAND)
        if allow_delete:
            colSizer.AddSpacer((5, -1))
            colSizer.Add(self.x_btn, 0, wx.EXPAND)

        self.SetSizer(colSizer)
        self.tableChoice.Bind(wx.EVT_COMBOBOX, self.on_select_table)
        self.colChoice.Bind(wx.EVT_COMBOBOX, self.on_select_col)
        if allow_delete:
            self.x_btn.Bind(wx.EVT_BUTTON, self.on_remove)
        self.Fit()

    def on_remove(self, evt):
        self.GrandParent.remove(self)

    def on_select_col(self, evt):
        self.update_comparator_choice()
        self.update_value_choice()

    def on_select_table(self, evt):
        self.update_col_choice()
        self.update_comparator_choice()
        self.update_value_choice()

    def update_col_choice(self):
        table = self.tableChoice.Value
        self.colChoice.SetItems(db.GetColumnNames(table))
        self.colChoice.Select(0)

    def _get_col_type(self):
        table = self.tableChoice.Value
        colidx = self.colChoice.GetSelection()
        return db.GetColumnTypes(table)[colidx]

    def update_comparator_choice(self):
        coltype = self._get_col_type()
        comparators = []
        if coltype in [str, unicode]:
            comparators = ['=', '!=', 'REGEXP', 'IS', 'IS NOT']
        if coltype in [int, float, long]:
            comparators = ['=', '!=', '<', '>', '<=', '>=', 'IS', 'IS NOT']
        self.comparatorChoice.SetItems(comparators)
        self.comparatorChoice.Select(0)

    def update_value_choice(self):
        table = self.tableChoice.Value
        column = self.colChoice.Value
        colidx = self.colChoice.GetSelection()
        coltype = db.GetColumnTypes(table)[colidx]
        vals = []
        # if coltype == str:# or coltype == int or coltype == long:
        #     res = db.execute('SELECT DISTINCT %s FROM %s ORDER BY %s'%(column, table, column))
        #     vals = [str(row[0]) for row in res]
        self.valueField.SetItems(vals)

    def get_filter(self):
        table = self.tableChoice.Value
        column = self.colChoice.Value
        comparator = self.comparatorChoice.GetValue()
        value = self.valueField.GetValue()
        if self._get_col_type() in [int, float, long]:
            # Don't quote numbers
            return sql.Filter(sql.Column(table, column), comparator,
                              '%s' % (value))
        if comparator.upper() in ['IS', 'IS NOT'] and value.upper() == 'NULL':
            # Don't quote comparisons to NULL
            return sql.Filter(sql.Column(table, column), comparator,
                              '%s' % (value))
        return sql.Filter(sql.Column(table, column), comparator,
                          '"%s"' % (value))
Exemplo n.º 3
0
class DataSourcePanel(wx.Panel):
    '''
    A panel with controls for selecting the source data for a boxplot 
    '''
    def __init__(self, parent, figpanel, **kwargs):
        wx.Panel.__init__(self, parent, **kwargs)
        
        # the panel to draw charts on
        self.SetBackgroundColour('white') # color for the background of panel
        self.figpanel = figpanel
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        self.x_columns = [] # column names to plot if selecting multiple columns

        self.table_choice = ui.TableComboBox(self, -1, style=wx.CB_READONLY)
        self.x_choice = ComboBox(self, -1, size=(200,-1))
        self.x_multiple = wx.Button(self, -1, 'select multiple')
        self.group_choice = ComboBox(self, -1, choices=[NO_GROUP]+p._groups_ordered, style=wx.CB_READONLY)
        self.group_choice.Select(0)
        self.filter_choice = ui.FilterComboBox(self, style=wx.CB_READONLY)
        self.filter_choice.Select(0)
        self.update_chart_btn = wx.Button(self, -1, "Update Chart")
        
        self.update_column_fields()
        
        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "table:"), 0, wx.TOP, 4)
        sz.AddSpacer((3,-1))
        sz.Add(self.table_choice, 1, wx.EXPAND)
        sz.AddSpacer((3,-1))
        sz.Add(wx.StaticText(self, -1, "measurement:"), 0, wx.TOP, 4)
        sz.AddSpacer((3,-1))
        sz.Add(self.x_choice, 2, wx.EXPAND)
        sz.AddSpacer((3,-1))
        sz.Add(self.x_multiple, 0, wx.EXPAND|wx.TOP, 2)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1,3))
        
        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "group x-axis by:"), 0, wx.TOP, 4)
        sz.AddSpacer((3,-1))
        sz.Add(self.group_choice, 1, wx.EXPAND)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1,3))

        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(wx.StaticText(self, -1, "filter:"), 0, wx.TOP, 4)
        sz.AddSpacer((3,-1))
        sz.Add(self.filter_choice, 1, wx.EXPAND)
        sizer.Add(sz, 1, wx.EXPAND)
        sizer.AddSpacer((-1,3))
        
        sizer.Add(self.update_chart_btn)    
        
        wx.EVT_BUTTON(self.x_multiple, -1, self.on_select_multiple)
        wx.EVT_COMBOBOX(self.table_choice, -1, self.on_table_selected)
        wx.EVT_COMBOBOX(self.x_choice, -1, self.on_column_selected)
        wx.EVT_BUTTON(self.update_chart_btn, -1, self.update_figpanel)   
        
        self.SetSizer(sizer)
        self.Show(1)

    def on_select_multiple(self, evt):
        tablename = self.table_choice.GetString(self.table_choice.GetSelection())
        column_names = self.get_numeric_columns_from_table(tablename)
        dlg = wx.MultiChoiceDialog(self, 
                                   'Select the columns you would like to plot',
                                   'Select Columns', column_names)
        dlg.SetSelections([column_names.index(v) for v in self.x_columns])
        if (dlg.ShowModal() == wx.ID_OK):
            self.x_choice.SetValue(SELECT_MULTIPLE)
            self.x_columns = [column_names[i] for i in dlg.GetSelections()]
            self.group_choice.Disable()
            self.group_choice.SetStringSelection(NO_GROUP)
        
    def on_table_selected(self, evt):
        table = self.table_choice.Value
        if table == ui.TableComboBox.OTHER_TABLE:
            t = ui.get_other_table_from_user(self)
            if t is not None:
                self.table_choice.Items = self.table_choice.Items[:-1] + [t] + self.table_choice.Items[-1:]
                self.table_choice.Select(self.table_choice.Items.index(t))
            else:
                self.table_choice.Select(0)
                return
        self.group_choice.Enable()
        self.x_columns = []
        self.update_column_fields()
        
    def on_column_selected(self, evt):
        self.group_choice.Enable()        
    
    def update_column_fields(self):
        tablename = self.table_choice.GetString(self.table_choice.GetSelection())
        fieldnames = self.get_numeric_columns_from_table(tablename)
        self.x_choice.Clear()
        self.x_choice.AppendItems(fieldnames)
        self.x_choice.SetSelection(0)

    def get_numeric_columns_from_table(self, table):
        ''' Fetches names of numeric columns for the given table. '''
        measurements = db.GetColumnNames(table)
        types = db.GetColumnTypes(table)
        return [m for m,t in zip(measurements, types) if t in [float, int, long]]
        
    def update_figpanel(self, evt=None):
        table = self.table_choice.Value
        fltr = self.filter_choice.get_filter_or_none()
        grouping = self.group_choice.Value
        if self.x_choice.Value == SELECT_MULTIPLE:
            points_dict = {}
            for col in self.x_columns:
                pts = self.loadpoints(table, col, fltr, NO_GROUP)
                for k in pts.keys(): assert k not in points_dict.keys()
                points_dict.update(pts)
        else:
            col = self.x_choice.Value
            points_dict = self.loadpoints(table, col, fltr, grouping)
        
        # Check if the user is creating a plethora of plots by accident
        if 100 >= len(points_dict) > 25:
            res = wx.MessageDialog(self, 'Are you sure you want to show %s box '
                                   'plots on one axis?'%(len(points_dict)), 
                                   'Warning', style=wx.YES_NO|wx.NO_DEFAULT
                                   ).ShowModal()
            if res != wx.ID_YES:
                return
        elif len(points_dict) > 100:
            wx.MessageBox('Sorry, boxplot can not show more than 100 plots on\n'
                          'a single axis. Your current settings would plot %d.\n'
                          'Try using a filter to narrow your query.'
                          %(len(points_dict)), 'Too many groups to plot')
            return

        self.figpanel.setpoints(points_dict)
        if self.group_choice.Value != NO_GROUP:
            self.figpanel.set_x_axis_label(grouping)
            self.figpanel.set_y_axis_label(self.x_choice.Value)
        self.figpanel.draw()
        
    def loadpoints(self, tablename, col, fltr=None, grouping=NO_GROUP):
        '''
        Returns a dict mapping x label values to lists of values from col
        '''
        q = sql.QueryBuilder()
        select = [sql.Column(tablename, col)]
        if grouping != NO_GROUP:
            dm = datamodel.DataModel.getInstance()
            group_cols = dm.GetGroupColumnNames(grouping, include_table_name=True)
            select += [sql.Column(*col.split('.')) for col in group_cols]
        q.set_select_clause(select)
        if fltr is not None:
            q.add_filter(fltr)

        res = db.execute(str(q))
        res = np.array(res, dtype=object)
        # replaces Nones with NaNs
        for row in res:
            if row[0] is None:
                row[0] = np.nan
        
        points_dict = {}
        if self.group_choice.Value != NO_GROUP:
            for row in res:
                groupkey = tuple(row[1:])
                points_dict[groupkey] = points_dict.get(groupkey, []) + [row[0]]
        else:
            points_dict = {col : [r[0] for r in res]}
        return points_dict

    def save_settings(self):
        '''
        Called when saving a workspace to file.
        returns a dictionary mapping setting names to values encoded as strings
        '''
        if self.x_choice.Value == SELECT_MULTIPLE:
            cols = self.x_columns
        else:
            cols = [self.x_choice.GetString(self.x_choice.GetSelection())]
        return {'table'  : self.table_choice.Value,
                'x-axis' : ','.join(cols),
                'filter' : self.filter_choice.Value,
                'x-lim'  : self.figpanel.subplot.get_xlim(),
                'y-lim'  : self.figpanel.subplot.get_ylim(),
##                'grouping': self.group_choice.Value,
##                'version': '1',
                }
    
    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 'version' not in settings:
##            settings['grouping'] = NO_GROUP
##            settings['version'] = '1'
        if 'table' in settings:
            self.table_choice.SetStringSelection(settings['table'])
            self.update_column_fields()
        if 'x-axis' in settings:
            cols = map(str.strip, settings['x-axis'].split(','))
            if len(cols) == 1:
                self.x_choice.SetStringSelection(cols[0])
            else:
                self.x_choice.SetValue(SELECT_MULTIPLE)
                self.x_columns = cols
        if 'filter' in settings:
            self.filter_choice.SetStringSelection(settings['filter'])
        self.update_figpanel()
        if 'x-lim' in settings:
            self.figpanel.subplot.set_xlim(eval(settings['x-lim']))
        if 'y-lim' in settings:
            self.figpanel.subplot.set_ylim(eval(settings['y-lim']))
        self.figpanel.draw()