source: orange/Orange/OrangeWidgets/Data/OWDataTable.py @ 11564:eca0d7ea5719

Revision 11564:eca0d7ea5719, 29.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 11 months ago (diff)

Optimization of 'ExampleTableModel.data' method.

Line 
1"""
2<name>Data Table</name>
3<description>Shows data in a spreadsheet.</description>
4<icon>icons/DataTable.svg</icon>
5<priority>100</priority>
6<contact>Peter Juvan (peter.juvan@fri.uni-lj.si)</contact>
7"""
8
9import Orange
10
11from OWWidget import *
12import OWGUI
13import math
14from orngDataCaching import *
15import OWColorPalette
16
17
18NAME = "Data Table"
19
20DESCRIPTION = "Shows data in a spreadsheet."
21
22LONG_DESCRIPTION = """Data Table widget takes one or more data sets
23on its input and presents them in a spreadsheet format.
24
25"""
26
27ICON = "icons/DataTable.svg"
28
29PRIORITY = 100
30
31AUTHOR = "Peter Juvan"
32
33AUTHOR_EMAIL = "peter.juvan(@at@)fri.uni-lj.si"
34
35INPUTS = [("Data", ExampleTable, "dataset", Multiple + Default)]
36
37OUTPUTS = [("Selected Data", ExampleTable, Default),
38           ("Other Data", ExampleTable)]
39
40##############################################################################
41
42def safe_call(func):
43    from functools import wraps
44    @wraps(func)
45    def wrapper(*args, **kwargs):
46        try:
47            return func(*args, **kwargs)
48        except Exception, ex:
49            print >> sys.stderr, func.__name__, "call error", ex
50            return QVariant()
51    return wrapper
52   
53
54class ExampleTableModel(QAbstractItemModel):
55    Attribute, ClassVar, ClassVars, Meta = range(4)
56
57    def __init__(self, examples, dist, *args):
58        QAbstractItemModel.__init__(self, *args)
59        self.examples = examples
60        self.domain = examples.domain
61        self.dist = dist
62        self.attributes = list(examples.domain.attributes)
63        self.class_var = self.examples.domain.class_var
64        self.class_vars = list(self.examples.domain.class_vars)
65        self.metas = self.examples.domain.getmetas().values()
66        # Attributes/features for all table columns
67        self.all_attrs = (self.attributes +
68                          ([self.class_var] if self.class_var else []) +
69                          self.class_vars + self.metas)
70        # Table roles for all table columns
71        self.table_roles = \
72            (([ExampleTableModel.Attribute] * len(self.attributes)) +
73             ([ExampleTableModel.ClassVar] if self.class_var else []) +
74             ([ExampleTableModel.ClassVars] * len(self.class_vars)) +
75             ([ExampleTableModel.Meta] * len(self.metas)))
76
77        # True if an feature at column i is continuous
78        self._continuous_mask = [isinstance(attr, Orange.feature.Continuous)
79                                 for attr in self.all_attrs]
80
81        self._meta_mask = [role == ExampleTableModel.Meta
82                           for role in self.table_roles]
83
84        self.cls_color = QColor(160, 160, 160)
85        self.meta_color = QColor(220, 220, 200)
86
87        role_to_color = {ExampleTableModel.Attribute: None,
88                         ExampleTableModel.ClassVar: self.cls_color,
89                         ExampleTableModel.ClassVars: self.cls_color,
90                         ExampleTableModel.Meta: self.meta_color}
91
92        self.background_colors = map(role_to_color.get, self.table_roles)
93
94        self.sorted_map = range(len(self.examples))
95
96        # all attribute labels (annotation) keys
97        self.attr_labels = sorted(
98            reduce(set.union,
99                   [attr.attributes for attr in self.all_attrs],
100                   set())
101        )
102
103        self._show_attr_labels = False
104        self._other_data = {}
105
106    def get_show_attr_labels(self):
107        return self._show_attr_labels
108
109    def set_show_attr_labels(self, val):
110        self.emit(SIGNAL("layoutAboutToBeChanged()"))
111        self._show_attr_labels = val
112        self.emit(SIGNAL("headerDataChanged(Qt::Orientation, int, int)"),
113                  Qt.Horizontal,
114                  0,
115                  len(self.all_attrs) - 1
116                  )
117        self.emit(SIGNAL("layoutChanged()"))
118        self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
119                  self.index(0, 0),
120                  self.index(len(self.examples) - 1, len(self.all_attrs) - 1)
121                  )
122
123    show_attr_labels = pyqtProperty("bool",
124                                    fget=get_show_attr_labels,
125                                    fset=set_show_attr_labels)
126
127    def data(self, index, role,
128             # For optimizing out LOAD_GLOBAL byte code instructions in
129             # the item role tests (goes from 14 us to 9 us average).
130             _str=str,
131             _Qt_DisplayRole=Qt.DisplayRole,
132             _Qt_BackgroundRole=Qt.BackgroundRole,
133             _OWGUI_TableBarItem_BarRole=OWGUI.TableBarItem.BarRole,
134             _OWGUI_TableValueRole=OWGUI.TableValueRole,
135             _OWGUI_TableClassValueRole=OWGUI.TableClassValueRole,
136             _OWGUI_TableVariable=OWGUI.TableVariable,
137             # Some cached local precomputed values.
138             # All of the above roles we respond to
139             _recognizedRoles=set([Qt.DisplayRole,
140                                   Qt.BackgroundRole,
141                                   OWGUI.TableBarItem.BarRole,
142                                   OWGUI.TableValueRole,
143                                   OWGUI.TableClassValueRole,
144                                   OWGUI.TableVariable]),
145             ):
146        """
147        Return the data for `role` for an value at `index`.
148        """
149        if role not in _recognizedRoles:
150            return self._other_data.get((index.row(), index.column(), role),
151                                        None)
152
153        row, col = self.sorted_map[index.row()], index.column()
154        example, attr = self.examples[row], self.all_attrs[col]
155
156        val = example[attr]
157
158        if role == _Qt_DisplayRole:
159            return _str(val)
160        elif role == _Qt_BackgroundRole:
161            return self.background_colors[col]
162        elif role == _OWGUI_TableBarItem_BarRole and \
163                self._continuous_mask[col] and not val.isSpecial() and \
164                col < len(self.dist):
165            dist = self.dist[col]
166            return (float(val) - dist.min) / (dist.max - dist.min or 1)
167        elif role == _OWGUI_TableValueRole:
168            # The actual value instance (should it be EditRole?)
169            return val
170        elif role == _OWGUI_TableClassValueRole and self.class_var is not None:
171            # The class value for the row's example
172            return example.get_class()
173        elif role == _OWGUI_TableVariable:
174            # The variable descriptor for column
175            return attr
176
177        return None
178
179    def setData(self, index, variant, role):
180        self._other_data[index.row(), index.column(), role] = variant
181        self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
182       
183    def index(self, row, col, parent=QModelIndex()):
184        return self.createIndex(row, col, 0)
185   
186    def parent(self, index):
187        return QModelIndex()
188   
189    def rowCount(self, parent=QModelIndex()):
190        if parent.isValid():
191            return 0
192        else:
193            return max([len(self.examples)] + [row for row, _, _ in self._other_data.keys()])
194       
195    def columnCount(self, index=QModelIndex()):
196        return max([len(self.all_attrs)] + [col for _, col, _ in self._other_data.keys()])
197   
198    @safe_call
199    def headerData(self, section, orientation, role):
200        if orientation == Qt.Horizontal:
201            attr = self.all_attrs[section]
202            if role ==Qt.DisplayRole:
203                values = [attr.name] + ([str(attr.attributes.get(label, "")) for label in self.attr_labels] if self.show_attr_labels else [])
204                return QVariant("\n".join(values))
205            if role == Qt.ToolTipRole:
206                pairs = [(key, str(attr.attributes[key])) for key in self.attr_labels if key in attr.attributes]
207                tip = "<b>%s</b>" % attr.name
208                tip = "<br>".join([tip] + ["%s = %s" % pair for pair in pairs])
209                return QVariant(tip) 
210        else:
211            if role == Qt.DisplayRole:
212                return QVariant(section + 1)
213        return QVariant()
214   
215    def sort(self, column, order=Qt.AscendingOrder):
216        self.emit(SIGNAL("layoutAboutToBeChanged()"))
217        attr = self.all_attrs[column] 
218        values = [(ex[attr], i) for i, ex in enumerate(self.examples)]
219        values = sorted(values, key=lambda t: t[0] if not t[0].isSpecial() else sys.maxint, reverse=(order!=Qt.AscendingOrder))
220        self.sorted_map = [v[1] for v in values]
221        self.emit(SIGNAL("layoutChanged()"))
222        self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
223                  self.index(0,0),
224                  self.index(len(self.examples) - 1, len(self.all_attrs) - 1)
225                  )
226
227
228class TableViewWithCopy(QTableView):
229    def keyPressEvent(self, event):
230        if event == QKeySequence.Copy:
231            sel_model = self.selectionModel()
232            try:
233                self.copy_selection_to_clipboard(sel_model)
234            except Exception:
235                import traceback
236                traceback.print_exc(file=sys.stderr)
237        else:
238            return QTableView.keyPressEvent(self, event)
239           
240    def copy_selection_to_clipboard(self, selection_model):
241        """Copy table selection to the clipboard.
242        """
243        # TODO: html/rtf table
244        import csv
245        from StringIO import StringIO
246        rows = selection_model.selectedRows(0)
247        lines = []
248        csv_str = StringIO()
249        csv_writer = csv.writer(csv_str, dialect="excel")
250        tsv_str = StringIO()
251        tsv_writer = csv.writer(tsv_str, dialect="excel-tab")
252        for row in rows:
253            line = []
254            for i in range(self.model().columnCount()):
255                index = self.model().index(row.row(), i)
256                val = index.data(Qt.DisplayRole)
257                line.append(unicode(val.toString()))
258
259            csv_writer.writerow(line)
260            tsv_writer.writerow(line)
261
262        csv_lines = csv_str.getvalue()
263        tsv_lines = tsv_str.getvalue()
264
265        mime = QMimeData()
266        mime.setData("text/csv", QByteArray(csv_lines))
267        mime.setData("text/tab-separated-values", QByteArray(tsv_lines))
268        mime.setData("text/plain", QByteArray(tsv_lines))
269        QApplication.clipboard().setMimeData(mime, QClipboard.Clipboard)
270
271
272class OWDataTable(OWWidget):
273    settingsList = ["showDistributions", "showMeta", "distColorRgb", "showAttributeLabels", "autoCommit", "selectedSchemaIndex", "colorByClass"]
274
275    def __init__(self, parent=None, signalManager = None):
276        OWWidget.__init__(self, parent, signalManager, "Data Table")
277
278        self.inputs = [("Data", ExampleTable, self.dataset, Multiple + Default)]
279        self.outputs = [("Selected Data", ExampleTable, Default), ("Other Data", ExampleTable)]
280
281        self.data = {}          # key: id, value: ExampleTable
282        self.showMetas = {}     # key: id, value: (True/False, columnList)
283        self.showMeta = 1
284        self.showAttributeLabels = 1
285        self.showDistributions = 1
286        self.distColorRgb = (220,220,220, 255)
287        self.distColor = QColor(*self.distColorRgb)
288        self.locale = QLocale()
289        self.autoCommit = False
290        self.colorSettings = None
291        self.selectedSchemaIndex = 0
292        self.colorByClass = True
293       
294        self.loadSettings()
295
296        # info box
297        infoBox = OWGUI.widgetBox(self.controlArea, "Info")
298        self.infoEx = OWGUI.widgetLabel(infoBox, 'No data on input.')
299        self.infoMiss = OWGUI.widgetLabel(infoBox, ' ')
300        OWGUI.widgetLabel(infoBox, ' ')
301        self.infoAttr = OWGUI.widgetLabel(infoBox, ' ')
302        self.infoMeta = OWGUI.widgetLabel(infoBox, ' ')
303        OWGUI.widgetLabel(infoBox, ' ')
304        self.infoClass = OWGUI.widgetLabel(infoBox, ' ')
305        infoBox.setMinimumWidth(200)
306        OWGUI.separator(self.controlArea)
307
308        # settings box
309        boxSettings = OWGUI.widgetBox(self.controlArea, "Settings", addSpace=True)
310        self.cbShowMeta = OWGUI.checkBox(boxSettings, self, "showMeta", 'Show meta attributes', callback = self.cbShowMetaClicked)
311        self.cbShowMeta.setEnabled(False)
312        self.cbShowAttLbls = OWGUI.checkBox(boxSettings, self, "showAttributeLabels", 'Show attribute labels (if any)', callback = self.cbShowAttLabelsClicked)
313        self.cbShowAttLbls.setEnabled(True)
314
315        box = OWGUI.widgetBox(self.controlArea, "Colors")
316        OWGUI.checkBox(box, self, "showDistributions", 'Visualize continuous values', callback = self.cbShowDistributions)
317        OWGUI.checkBox(box, self, "colorByClass", 'Color by class value', callback = self.cbShowDistributions)
318        OWGUI.button(box, self, "Set colors", self.setColors, tooltip = "Set the canvas background color and color palette for coloring continuous variables", debuggingEnabled = 0)
319
320        resizeColsBox = OWGUI.widgetBox(boxSettings, 0, "horizontal", 0)
321        OWGUI.label(resizeColsBox, self, "Resize columns: ")
322        OWGUI.toolButton(resizeColsBox, self, "+", self.increaseColWidth, tooltip = "Increase the width of the columns", width=20, height=20)
323        OWGUI.toolButton(resizeColsBox, self, "-", self.decreaseColWidth, tooltip = "Decrease the width of the columns", width=20, height=20)
324        OWGUI.rubber(resizeColsBox)
325
326        self.btnResetSort = OWGUI.button(boxSettings, self, "Restore Order of Examples", callback = self.btnResetSortClicked, tooltip = "Show examples in the same order as they appear in the file")
327       
328        OWGUI.separator(self.controlArea)
329        selectionBox = OWGUI.widgetBox(self.controlArea, "Selection")
330        self.sendButton = OWGUI.button(selectionBox, self, "Send selections", self.commit, default=True)
331        cb = OWGUI.checkBox(selectionBox, self, "autoCommit", "Commit on any change", callback=self.commitIf)
332        OWGUI.setStopper(self, self.sendButton, cb, "selectionChangedFlag", self.commit)
333
334        OWGUI.rubber(self.controlArea)
335
336        dlg = self.createColorDialog()
337        self.discPalette = dlg.getDiscretePalette("discPalette")
338
339        # GUI with tabs
340        self.tabs = OWGUI.tabWidget(self.mainArea)
341        self.id2table = {}  # key: widget id, value: table
342        self.table2id = {}  # key: table, value: widget id
343        self.connect(self.tabs, SIGNAL("currentChanged(QWidget*)"), self.tabClicked)
344       
345        self.selectionChangedFlag = False
346       
347
348    def createColorDialog(self):
349        c = OWColorPalette.ColorPaletteDlg(self, "Color Palette")
350        c.createDiscretePalette("discPalette", "Discrete Palette")
351        box = c.createBox("otherColors", "Other Colors")
352        c.createColorButton(box, "Default", "Default color", QColor(Qt.white))
353        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex)
354        return c
355
356    def setColors(self):
357        dlg = self.createColorDialog()
358        if dlg.exec_():
359            self.colorSettings = dlg.getColorSchemas()
360            self.selectedSchemaIndex = dlg.selectedSchemaIndex
361            self.discPalette = dlg.getDiscretePalette("discPalette")
362            self.distColorRgb = dlg.getColor("Default")
363
364    def increaseColWidth(self):
365        table = self.tabs.currentWidget()
366        if table:
367            for col in range(table.model().columnCount(QModelIndex())):
368                w = table.columnWidth(col)
369                table.setColumnWidth(col, w + 10)
370
371    def decreaseColWidth(self):
372        table = self.tabs.currentWidget()
373        if table:
374            for col in range(table.model().columnCount(QModelIndex())):
375                w = table.columnWidth(col)
376                minW = table.sizeHintForColumn(col)
377                table.setColumnWidth(col, max(w - 10, minW))
378
379
380    def dataset(self, data, id=None):
381        """Generates a new table and adds it to a new tab when new data arrives;
382        or hides the table and removes a tab when data==None;
383        or replaces the table when new data arrives together with already existing id."""
384        if data != None:  # can be an empty table!
385            if self.data.has_key(id):
386                # remove existing table
387                self.data.pop(id)
388                self.showMetas.pop(id)
389                self.id2table[id].hide()
390                self.tabs.removeTab(self.tabs.indexOf(self.id2table[id]))
391                self.table2id.pop(self.id2table.pop(id))
392            self.data[id] = data
393            self.showMetas[id] = (True, [])
394
395            table = TableViewWithCopy() #QTableView()
396            table.setSelectionBehavior(QAbstractItemView.SelectRows)
397            table.setSortingEnabled(True)
398            table.setHorizontalScrollMode(QTableWidget.ScrollPerPixel)
399            table.horizontalHeader().setMovable(True)
400            table.horizontalHeader().setClickable(True)
401            table.horizontalHeader().setSortIndicatorShown(False)
402           
403            option = table.viewOptions()
404            size = table.style().sizeFromContents(QStyle.CT_ItemViewItem, option, QSize(20, 20), table) #QSize(20, QFontMetrics(option.font).lineSpacing()), table)
405           
406            table.verticalHeader().setDefaultSectionSize(size.height() + 2) #int(size.height() * 1.25) + 2)
407
408            self.id2table[id] = table
409            self.table2id[table] = id
410            if data.name:
411                tabName = "%s " % data.name
412            else:
413                tabName = ""
414            tabName += "(" + str(id[1]) + ")"
415            if id[2] != None:
416                tabName += " [" + str(id[2]) + "]"
417            self.tabs.addTab(table, tabName)
418
419            self.progressBarInit()
420            self.setTable(table, data)
421            self.progressBarFinished()
422            self.tabs.setCurrentIndex(self.tabs.indexOf(table))
423            self.setInfo(data)
424            self.sendButton.setEnabled(not self.autoCommit)
425
426        elif self.data.has_key(id):
427            table = self.id2table[id]
428            self.data.pop(id)
429            self.showMetas.pop(id)
430            table.hide()
431            self.tabs.removeTab(self.tabs.indexOf(table))
432            self.table2id.pop(self.id2table.pop(id))
433            self.setInfo(self.data.get(self.table2id.get(self.tabs.currentWidget(),None),None))
434
435        if len(self.data) == 0:
436            self.sendButton.setEnabled(False)
437
438        self.setCbShowMeta()
439
440    def setCbShowMeta(self):
441        for ti in range(self.tabs.count()):
442            if len(self.tabs.widget(ti).model().metas)>0:
443                self.cbShowMeta.setEnabled(True)
444                break
445        else:
446            self.cbShowMeta.setEnabled(False)
447           
448    def sendReport(self):
449        qTableInstance = self.tabs.currentWidget()
450        id = self.table2id.get(qTableInstance, None)
451        data = self.data.get(id, None)
452        self.reportData(data)
453        table = self.id2table[id]
454        import OWReport
455        self.reportRaw(OWReport.reportTable(table))
456       
457       
458    # Writes data into table, adjusts the column width.
459    def setTable(self, table, data):
460        if data==None:
461            return
462        qApp.setOverrideCursor(Qt.WaitCursor)
463        vars = data.domain.variables
464        m = data.domain.getmetas(False)
465        ml = [(k, m[k]) for k in m]
466        ml.sort(lambda x,y: cmp(y[0], x[0]))
467        metas = [x[1] for x in ml]
468        metaKeys = [x[0] for x in ml]
469
470        mo = data.domain.getmetas(True).items()
471        if mo:
472            mo.sort(lambda x,y: cmp(x[1].name.lower(),y[1].name.lower()))
473            metas.append(None)
474            metaKeys.append(None)
475
476        varsMetas = vars + metas
477
478        numVars = len(data.domain.variables)
479        numMetas = len(metas)
480        numVarsMetas = numVars + numMetas
481        numEx = len(data)
482        numSpaces = int(math.log(max(numEx,1), 10))+1
483
484#        table.clear()
485        table.oldSortingIndex = -1
486        table.oldSortingOrder = 1
487#        table.setColumnCount(numVarsMetas)
488#        table.setRowCount(numEx)
489
490        dist = getCached(data, orange.DomainBasicAttrStat, (data,))
491       
492        datamodel = ExampleTableModel(data, dist, self)
493       
494#        proxy = QSortFilterProxyModel(self)
495#        proxy.setSourceModel(datamodel)
496       
497        color_schema = self.discPalette if self.colorByClass else None
498        table.setItemDelegate(OWGUI.TableBarItem(self, color=self.distColor, color_schema=color_schema) \
499                              if self.showDistributions else QStyledItemDelegate(self)) #TableItemDelegate(self, table))
500       
501        table.setModel(datamodel)
502        def p():
503            try:
504                table.updateGeometries()
505                table.viewport().update()
506            except RuntimeError:
507                pass
508       
509        size = table.verticalHeader().sectionSizeHint(0)
510        table.verticalHeader().setDefaultSectionSize(size)
511       
512        self.connect(datamodel, SIGNAL("layoutChanged()"), lambda *args: QTimer.singleShot(50, p))
513       
514        id = self.table2id.get(table, None)
515
516        # set the header (attribute names)
517
518        self.drawAttributeLabels(table)
519
520        self.showMetas[id][1].extend([i for i, attr in enumerate(table.model().all_attrs) if attr in table.model().metas])
521        self.connect(table.horizontalHeader(), SIGNAL("sectionClicked(int)"), self.sortByColumn)
522        self.connect(table.selectionModel(), SIGNAL("selectionChanged(QItemSelection, QItemSelection)"), self.updateSelection)
523        #table.verticalHeader().setMovable(False)
524
525        qApp.restoreOverrideCursor() 
526
527    def setCornerText(self, table, text):
528        """
529        Set table corner text. As this is an ugly hack, do everything in
530        try - except blocks, as it may stop working in newer Qt.
531        """
532
533        if not hasattr(table, "btn") and not hasattr(table, "btnfailed"):
534            try:
535                btn = table.findChild(QAbstractButton)
536
537                class efc(QObject):
538                    def eventFilter(self, o, e):
539                        if (e.type() == QEvent.Paint):
540                            if isinstance(o, QAbstractButton):
541                                btn = o
542                                #paint by hand (borrowed from QTableCornerButton)
543                                opt = QStyleOptionHeader()
544                                opt.init(btn)
545                                state = QStyle.State_None;
546                                if (btn.isEnabled()):
547                                    state |= QStyle.State_Enabled;
548                                if (btn.isActiveWindow()):
549                                    state |= QStyle.State_Active;
550                                if (btn.isDown()):
551                                    state |= QStyle.State_Sunken;
552                                opt.state = state;
553                                opt.rect = btn.rect();
554                                opt.text = btn.text();
555                                opt.position = QStyleOptionHeader.OnlyOneSection;
556                                painter = QStylePainter(btn);
557                                painter.drawControl(QStyle.CE_Header, opt);
558                                return True # eat evebt
559                        return False
560               
561                table.efc = efc()
562                btn.installEventFilter(table.efc)
563                table.btn = btn
564            except:
565                table.btnfailed = True
566
567        if hasattr(table, "btn"):
568            try:
569                btn = table.btn
570                btn.setText(text)
571                opt = QStyleOptionHeader()
572                opt.text = btn.text()
573                s = btn.style().sizeFromContents(QStyle.CT_HeaderSection, opt, QSize(), btn).expandedTo(QApplication.globalStrut())
574                if s.isValid():
575                    table.verticalHeader().setMinimumWidth(s.width())
576                   
577            except:
578                pass
579
580    def sortByColumn(self, index):
581        table = self.tabs.currentWidget()
582        table.horizontalHeader().setSortIndicatorShown(1)
583        header = table.horizontalHeader()
584        if index == table.oldSortingIndex:
585            order = table.oldSortingOrder == Qt.AscendingOrder and Qt.DescendingOrder or Qt.AscendingOrder
586        else:
587            order = Qt.AscendingOrder
588        table.sortByColumn(index, order)
589        table.oldSortingIndex = index
590        table.oldSortingOrder = order
591        #header.setSortIndicator(index, order)
592
593    def tabClicked(self, qTableInstance):
594        """Updates the info box and showMetas checkbox when a tab is clicked.
595        """
596        id = self.table2id.get(qTableInstance,None)
597        self.setInfo(self.data.get(id,None))
598        show_col = self.showMetas.get(id,None)
599        if show_col:
600            self.cbShowMeta.setChecked(show_col[0])
601            self.cbShowMeta.setEnabled(len(show_col[1])>0)
602        self.updateSelection()
603
604    def cbShowMetaClicked(self):
605        table = self.tabs.currentWidget()
606        id = self.table2id.get(table, None)
607        if self.showMetas.has_key(id):
608            show,col = self.showMetas[id]
609            self.showMetas[id] = (not show,col)
610        if show:
611            for c in col:
612                table.hideColumn(c)
613        else:
614            for c in col:
615                table.showColumn(c)
616                table.resizeColumnToContents(c)
617
618    def drawAttributeLabels(self, table):
619#        table.setHorizontalHeaderLabels(table.variableNames)
620        table.model().show_attr_labels = bool(self.showAttributeLabels)
621        if self.showAttributeLabels:
622            labelnames = set()
623            for a in table.model().examples.domain:
624                labelnames.update(a.attributes.keys())
625            labelnames = sorted(list(labelnames))
626#            if len(labelnames):
627#                table.setHorizontalHeaderLabels([table.variableNames[i] + "\n" + "\n".join(["%s" % a.attributes.get(lab, "") for lab in labelnames]) for (i, a) in enumerate(table.data.domain.attributes)])
628            self.setCornerText(table, "\n".join([""] + labelnames))
629        else:
630            self.setCornerText(table, "")
631        table.repaint()
632
633    def cbShowAttLabelsClicked(self):
634        for table in self.table2id.keys():
635            self.drawAttributeLabels(table)
636
637    def cbShowDistributions(self):
638        for ti in range(self.tabs.count()):
639            color_schema = self.discPalette if self.colorByClass else None
640            delegate = OWGUI.TableBarItem(self, color=self.distColor,
641                                          color_schema=color_schema) \
642                       if self.showDistributions else QStyledItemDelegate(self)
643            self.tabs.widget(ti).setItemDelegate(delegate)
644        tab = self.tabs.currentWidget()
645        if tab:
646            tab.reset()
647
648    # show data in the default order
649    def btnResetSortClicked(self):
650        table = self.tabs.currentWidget()
651        if table:
652            id = self.table2id[table]
653            data = self.data[id]
654            table.horizontalHeader().setSortIndicatorShown(False)
655            self.progressBarInit()
656            self.setTable(table, data)
657            self.progressBarFinished()
658
659    def setInfo(self, data):
660        """Updates data info.
661        """
662        def sp(l, capitalize=False):
663            n = len(l)
664            if n == 0:
665                if capitalize:
666                    return "No", "s"
667                else:
668                    return "no", "s"
669            elif n == 1:
670                return str(n), ''
671            else:
672                return str(n), 's'
673
674        if data == None:
675            self.infoEx.setText('No data on input.')
676            self.infoMiss.setText('')
677            self.infoAttr.setText('')
678            self.infoMeta.setText('')
679            self.infoClass.setText('')
680        else:
681            self.infoEx.setText("%s example%s," % sp(data))
682            missData = orange.Preprocessor_takeMissing(data)
683            self.infoMiss.setText('%s (%.1f%s) with missing values.' % (len(missData), len(data) and 100.*len(missData)/len(data), "%"))
684            self.infoAttr.setText("%s attribute%s," % sp(data.domain.attributes,True))
685            self.infoMeta.setText("%s meta attribute%s." % sp(data.domain.getmetas()))
686            if data.domain.classVar:
687                if data.domain.classVar.varType == orange.VarTypes.Discrete:
688                    self.infoClass.setText('Discrete class with %s value%s.' % sp(data.domain.classVar.values))
689                elif data.domain.classVar.varType == orange.VarTypes.Continuous:
690                    self.infoClass.setText('Continuous class.')
691                else:
692                    self.infoClass.setText("Class is neither discrete nor continuous.")
693            else:
694                self.infoClass.setText('Classless domain.')
695
696    def updateSelection(self, *args):
697        self.sendButton.setEnabled(bool(self.getCurrentSelection()) and not self.autoCommit)
698        self.commitIf()
699           
700    def getCurrentSelection(self):
701        table = self.tabs.currentWidget()
702        if table and table.model():
703            model = table.model()
704            new = table.selectionModel().selectedIndexes()
705            return sorted(set([model.sorted_map[ind.row()] for ind in new]))
706       
707    def commitIf(self):
708        if self.autoCommit:
709            self.commit()
710        else:
711            self.selectionChangedFlag = True
712           
713    def commit(self):
714        table = self.tabs.currentWidget()
715        if table and table.model():
716            model = table.model()
717            selected = self.getCurrentSelection()
718            selection = [1 if i in selected else 0 for i in range(len(model.examples))]
719            data = model.examples.select(selection)
720            self.send("Selected Data", data if len(data) > 0 else None)
721            data = model.examples.select(selection, 0)
722            self.send("Other Data", data if len(data) > 0 else None)
723        else:
724            self.send("Selected Data", None)
725            self.send("Other Data", None)
726           
727        self.selectionChangedFlag = False
728
729
730def test():
731    a = QApplication(sys.argv)
732    ow = OWDataTable()
733
734    d1 = orange.ExampleTable("auto-mpg")
735    d2 = orange.ExampleTable("sponge.tab")
736    d3 = orange.ExampleTable("wpbc.csv")
737    d4 = orange.ExampleTable("adult_sample.tab")
738    d5 = orange.ExampleTable("wine.tab")
739
740    ow.show()
741    ow.dataset(d1, "auto-mpg")
742    ow.dataset(d2, "sponge")
743    ow.dataset(d3, "wpbc")
744    ow.dataset(d4, "adult_sample")
745    ow.dataset(d5, "wine")
746    a.exec_()
747    ow.saveSettings()
748
749
750if __name__ == "__main__":
751    test()
Note: See TracBrowser for help on using the repository browser.