source: sasview/batch/src/sans/grid.py @ e72db38

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since e72db38 was e72db38, checked in by Gervaise Alina <gervyh@…>, 13 years ago

changing batch folder

  • Property mode set to 100644
File size: 13.3 KB
Line 
1
2
3import  wx
4import  wx.grid as  Grid
5import  images
6
7
8class MegaTable(Grid.PyGridTableBase):
9    """
10    A custom wx.Grid Table using user supplied data
11    """
12    def __init__(self, data, colnames, plugins):
13        """data is a list of the form
14        [(rowname, dictionary),
15        dictionary.get(colname, None) returns the data for column
16        colname
17        """
18        # The base class must be initialized *first*
19        Grid.PyGridTableBase.__init__(self)
20        self.data = data
21        self.colnames = colnames
22        self.plugins = plugins or {}
23        # XXX
24        # we need to store the row length and column length to
25        # see if the table has changed size
26        self._rows = self.GetNumberRows()
27        self._cols = self.GetNumberCols()
28
29    def GetNumberCols(self):
30        return len(self.colnames)
31
32    def GetNumberRows(self):
33        return len(self.data)
34
35    def GetColLabelValue(self, col):
36        return self.colnames[col]
37
38    def GetRowLabelValue(self, row):
39        return "row %03d" % int(self.data[row][0])
40
41    def GetValue(self, row, col):
42        return str(self.data[row][1].get(self.GetColLabelValue(col), ""))
43
44    def GetRawValue(self, row, col):
45        return self.data[row][1].get(self.GetColLabelValue(col), "")
46
47    def SetValue(self, row, col, value):
48        self.data[row][1][self.GetColLabelValue(col)] = value
49
50    def ResetView(self, grid):
51        """
52        (Grid) -> Reset the grid view.   Call this to
53        update the grid if rows and columns have been added or deleted
54        """
55        grid.BeginBatch()
56
57        for current, new, delmsg, addmsg in [
58            (self._rows, self.GetNumberRows(), Grid.GRIDTABLE_NOTIFY_ROWS_DELETED, Grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
59            (self._cols, self.GetNumberCols(), Grid.GRIDTABLE_NOTIFY_COLS_DELETED, Grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
60        ]:
61
62            if new < current:
63                msg = Grid.GridTableMessage(self,delmsg,new,current-new)
64                grid.ProcessTableMessage(msg)
65            elif new > current:
66                msg = Grid.GridTableMessage(self,addmsg,new-current)
67                grid.ProcessTableMessage(msg)
68                self.UpdateValues(grid)
69
70        grid.EndBatch()
71
72        self._rows = self.GetNumberRows()
73        self._cols = self.GetNumberCols()
74        # update the column rendering plugins
75        self._updateColAttrs(grid)
76
77        # update the scrollbars and the displayed part of the grid
78        grid.AdjustScrollbars()
79        grid.ForceRefresh()
80
81
82    def UpdateValues(self, grid):
83        """Update all displayed values"""
84        # This sends an event to the grid table to update all of the values
85        msg = Grid.GridTableMessage(self, Grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
86        grid.ProcessTableMessage(msg)
87
88    def _updateColAttrs(self, grid):
89        """
90        wx.Grid -> update the column attributes to add the
91        appropriate renderer given the column name.  (renderers
92        are stored in the self.plugins dictionary)
93
94        Otherwise default to the default renderer.
95        """
96        col = 0
97
98        for colname in self.colnames:
99            attr = Grid.GridCellAttr()
100            if colname in self.plugins:
101                renderer = self.plugins[colname](self)
102
103                if renderer.colSize:
104                    grid.SetColSize(col, renderer.colSize)
105
106                if renderer.rowSize:
107                    grid.SetDefaultRowSize(renderer.rowSize)
108
109                attr.SetReadOnly(True)
110                attr.SetRenderer(renderer)
111
112            grid.SetColAttr(col, attr)
113            col += 1
114
115    # ------------------------------------------------------
116    # begin the added code to manipulate the table (non wx related)
117    def AppendRow(self, row):
118        #print 'append'
119        entry = {}
120
121        for name in self.colnames:
122            entry[name] = "Appended_%i"%row
123
124        # XXX Hack
125        # entry["A"] can only be between 1..4
126        entry["A"] = random.choice(range(4))
127        self.data.insert(row, ["Append_%i"%row, entry])
128
129    def DeleteCols(self, cols):
130        """
131        cols -> delete the columns from the dataset
132        cols hold the column indices
133        """
134        # we'll cheat here and just remove the name from the
135        # list of column names.  The data will remain but
136        # it won't be shown
137        deleteCount = 0
138        cols = cols[:]
139        cols.sort()
140
141        for i in cols:
142            self.colnames.pop(i-deleteCount)
143            # we need to advance the delete count
144            # to make sure we delete the right columns
145            deleteCount += 1
146
147        if not len(self.colnames):
148            self.data = []
149
150    def DeleteRows(self, rows):
151        """
152        rows -> delete the rows from the dataset
153        rows hold the row indices
154        """
155        deleteCount = 0
156        rows = rows[:]
157        rows.sort()
158
159        for i in rows:
160            self.data.pop(i-deleteCount)
161            # we need to advance the delete count
162            # to make sure we delete the right rows
163            deleteCount += 1
164
165    def SortColumn(self, col):
166        """
167        col -> sort the data based on the column indexed by col
168        """
169        name = self.colnames[col]
170        _data = []
171
172        for row in self.data:
173            rowname, entry = row
174            _data.append((entry.get(name, None), row))
175
176        _data.sort()
177        self.data = []
178
179        for sortvalue, row in _data:
180            self.data.append(row)
181
182    # end table manipulation code
183    # ----------------------------------------------------------
184
185
186# --------------------------------------------------------------------
187# Sample wx.Grid renderers
188
189class MegaImageRenderer(Grid.PyGridCellRenderer):
190    def __init__(self, table):
191        """
192        Image Renderer Test.  This just places an image in a cell
193        based on the row index.  There are N choices and the
194        choice is made by  choice[row%N]
195        """
196        Grid.PyGridCellRenderer.__init__(self)
197        self.table = table
198        self._choices = [images.Smiles.GetBitmap,
199                         images.Mondrian.GetBitmap,
200                         images.WXPdemo.GetBitmap,
201                         ]
202
203        self.colSize = None
204        self.rowSize = None
205
206    def Draw(self, grid, attr, dc, rect, row, col, isSelected):
207        choice = self.table.GetRawValue(row, col)
208        bmp = self._choices[ choice % len(self._choices)]()
209        image = wx.MemoryDC()
210        image.SelectObject(bmp)
211
212        # clear the background
213        dc.SetBackgroundMode(wx.SOLID)
214
215        if isSelected:
216            dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
217            dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
218        else:
219            dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
220            dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))
221        dc.DrawRectangleRect(rect)
222
223
224        # copy the image but only to the size of the grid cell
225        width, height = bmp.GetWidth(), bmp.GetHeight()
226
227        if width > rect.width-2:
228            width = rect.width-2
229
230        if height > rect.height-2:
231            height = rect.height-2
232
233        dc.Blit(rect.x+1, rect.y+1, width, height,
234                image,
235                0, 0, wx.COPY, True)
236
237
238class MegaFontRenderer(Grid.PyGridCellRenderer):
239    def __init__(self, table, color="blue", font="ARIAL", fontsize=8):
240        """Render data in the specified color and font and fontsize"""
241        Grid.PyGridCellRenderer.__init__(self)
242        self.table = table
243        self.color = color
244        self.font = wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, font)
245        self.selectedBrush = wx.Brush("blue", wx.SOLID)
246        self.normalBrush = wx.Brush(wx.WHITE, wx.SOLID)
247        self.colSize = None
248        self.rowSize = 50
249
250    def Draw(self, grid, attr, dc, rect, row, col, isSelected):
251        # Here we draw text in a grid cell using various fonts
252        # and colors.  We have to set the clipping region on
253        # the grid's DC, otherwise the text will spill over
254        # to the next cell
255        dc.SetClippingRect(rect)
256
257        # clear the background
258        dc.SetBackgroundMode(wx.SOLID)
259       
260        if isSelected:
261            dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
262            dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
263        else:
264            dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
265            dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))
266        dc.DrawRectangleRect(rect)
267
268        text = self.table.GetValue(row, col)
269        dc.SetBackgroundMode(wx.SOLID)
270
271        # change the text background based on whether the grid is selected
272        # or not
273        if isSelected:
274            dc.SetBrush(self.selectedBrush)
275            dc.SetTextBackground("blue")
276        else:
277            dc.SetBrush(self.normalBrush)
278            dc.SetTextBackground("white")
279
280        dc.SetTextForeground(self.color)
281        dc.SetFont(self.font)
282        dc.DrawText(text, rect.x+1, rect.y+1)
283
284        # Okay, now for the advanced class :)
285        # Let's add three dots "..."
286        # to indicate that that there is more text to be read
287        # when the text is larger than the grid cell
288
289        width, height = dc.GetTextExtent(text)
290       
291        if width > rect.width-2:
292            width, height = dc.GetTextExtent("...")
293            x = rect.x+1 + rect.width-2 - width
294            dc.DrawRectangle(x, rect.y+1, width+1, height)
295            dc.DrawText("...", x, rect.y+1)
296
297        dc.DestroyClippingRegion()
298
299
300# --------------------------------------------------------------------
301# Sample Grid using a specialized table and renderers that can
302# be plugged in based on column names
303
304class MegaGrid(Grid.Grid):
305    def __init__(self, parent, data, colnames, plugins=None):
306        """parent, data, colnames, plugins=None
307        Initialize a grid using the data defined in data and colnames
308        (see MegaTable for a description of the data format)
309        plugins is a dictionary of columnName -> column renderers.
310        """
311
312        # The base class must be initialized *first*
313        Grid.Grid.__init__(self, parent, -1)
314        self._table = MegaTable(data, colnames, plugins)
315        self.SetTable(self._table)
316        self._plugins = plugins
317
318        self.Bind(Grid.EVT_GRID_LABEL_RIGHT_CLICK, self.OnLabelRightClicked)
319
320    def Reset(self):
321        """reset the view based on the data in the table.  Call
322        this when rows are added or destroyed"""
323        self._table.ResetView(self)
324
325    def OnLabelRightClicked(self, evt):
326        # Did we click on a row or a column?
327        row, col = evt.GetRow(), evt.GetCol()
328        if row == -1: self.colPopup(col, evt)
329        elif col == -1: self.rowPopup(row, evt)
330
331    def rowPopup(self, row, evt):
332        """(row, evt) -> display a popup menu when a row label is right clicked"""
333        appendID = wx.NewId()
334        deleteID = wx.NewId()
335        x = self.GetRowSize(row)/2
336
337        if not self.GetSelectedRows():
338            self.SelectRow(row)
339
340        menu = wx.Menu()
341        xo, yo = evt.GetPosition()
342        menu.Append(appendID, "Append Row")
343        menu.Append(deleteID, "Delete Row(s)")
344
345        def append(event, self=self, row=row):
346            self._table.AppendRow(row)
347            self.Reset()
348
349        def delete(event, self=self, row=row):
350            rows = self.GetSelectedRows()
351            self._table.DeleteRows(rows)
352            self.Reset()
353
354        self.Bind(wx.EVT_MENU, append, id=appendID)
355        self.Bind(wx.EVT_MENU, delete, id=deleteID)
356        self.PopupMenu(menu)
357        menu.Destroy()
358        return
359
360
361    def colPopup(self, col, evt):
362        """(col, evt) -> display a popup menu when a column label is
363        right clicked"""
364        x = self.GetColSize(col)/2
365        menu = wx.Menu()
366        id1 = wx.NewId()
367        sortID = wx.NewId()
368
369        xo, yo = evt.GetPosition()
370        self.SelectCol(col)
371        cols = self.GetSelectedCols()
372        self.Refresh()
373        menu.Append(id1, "Delete Col(s)")
374        menu.Append(sortID, "Sort Column")
375
376        def delete(event, self=self, col=col):
377            cols = self.GetSelectedCols()
378            self._table.DeleteCols(cols)
379            self.Reset()
380
381        def sort(event, self=self, col=col):
382            self._table.SortColumn(col)
383            self.Reset()
384
385        self.Bind(wx.EVT_MENU, delete, id=id1)
386
387        if len(cols) == 1:
388            self.Bind(wx.EVT_MENU, sort, id=sortID)
389
390        self.PopupMenu(menu)
391        menu.Destroy()
392        return
393
394# -----------------------------------------------------------------
395# Test data
396# data is in the form
397# [rowname, dictionary]
398# where dictionary.get(colname, None) -> returns the value for the cell
399#
400# the colname must also be supplied
401import random
402colnames = ["Row", "This", "Is", "A", "Test"]
403
404data = []
405
406for row in range(1000):
407    d = {}
408    for name in ["This", "Test", "Is"]:
409        d[name] = random.random()
410
411    d["Row"] = len(data)
412    # XXX
413    # the "A" column can only be between one and 4
414    d["A"] = random.choice(range(4))
415    data.append((str(row), d))
416
417class MegaFontRendererFactory:
418    def __init__(self, color, font, fontsize):
419        """
420        (color, font, fontsize) -> set of a factory to generate
421        renderers when called.
422        func = MegaFontRenderFactory(color, font, fontsize)
423        renderer = func(table)
424        """
425        self.color = color
426        self.font = font
427        self.fontsize = fontsize
428
429    def __call__(self, table):
430        return MegaFontRenderer(table, self.color, self.font, self.fontsize)
431
432
433
434
435
436
437
438
439
440
441
442
Note: See TracBrowser for help on using the repository browser.