source: orange/Orange/OrangeWidgets/Data/OWDataTable.py @ 11719:d26fa6fe99b2

Revision 11719:d26fa6fe99b2, 31.0 KB checked in by blaz <blaz.zupan@…>, 7 months ago (diff)

Updated DESCRIPTION fields.

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