source: orange-bioinformatics/orangecontrib/bio/widgets/OWGeneInfo.py @ 2023:5e045c5ba71a

Revision 2023:5e045c5ba71a, 25.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 6 weeks ago (diff)

Fixed an UnboundLocalError introduced in 61d9516b43bd

Line 
1"""
2<name>Gene Info</name>
3<description>Displays gene information from NCBI and other sources.</description>
4<priority>2010</priority>
5<contact>Ales Erjavec (ales.erjavec(@at@)fri.uni-lj.si)</contact>
6<icon>icons/GeneInfo.svg</icon>
7"""
8
9from __future__ import absolute_import, with_statement
10
11import sys
12from collections import defaultdict
13from functools import partial
14
15from PyQt4.QtCore import pyqtSlot as Slot
16
17import Orange
18
19from Orange.utils import serverfiles
20from Orange.utils import lru_cache
21
22from Orange.orng.orngDataCaching import data_hints
23from Orange.OrangeWidgets import OWGUI
24from Orange.OrangeWidgets.OWGUI import LinkStyledItemDelegate, LinkRole
25
26from Orange.OrangeWidgets.OWWidget import *
27
28from Orange.OrangeWidgets.OWConcurrent import \
29    ThreadExecutor, Task, methodinvoke
30
31
32from .. import gene, taxonomy
33from .utils import download
34
35
36NAME = "Gene Info"
37DESCRIPTION = "Displays gene information from NCBI and other sources."
38ICON = "icons/GeneInfo.svg"
39PRIORITY = 2010
40
41INPUTS = [("Examples", Orange.data.Table, "setData")]
42OUTPUTS = [("Selected Examples", Orange.data.Table)]
43
44REPLACES = ["_bioinformatics.widgets.OWGeneInfo.OWGeneInfo"]
45
46
47class TreeModel(QAbstractItemModel):
48
49    def __init__(self, data, header, parent):
50        QAbstractItemModel.__init__(self, parent)
51        self._data = [[QVariant(s) for s in row] for row in data]
52        self._dataDict = {}
53        self._header = header
54        self._roleData = {Qt.DisplayRole: self._data}
55        self._roleData = partial(
56            defaultdict,
57            partial(defaultdict,
58                    partial(defaultdict, QVariant)))(self._roleData)
59
60    def setColumnLinks(self, column, links):
61        font = QFont()
62        font.setUnderline(True)
63        font = QVariant(font)
64        for i, link in enumerate(links):
65            self._roleData[LinkRole][i][column] = QVariant(link)
66            self._roleData[Qt.FontRole][i][column] = font
67            self._roleData[Qt.ForegroundRole][i][column] = \
68                QVariant(QColor(Qt.blue))
69
70    def setRoleData(self, role, row, col, data):
71        self._roleData[role][row][col] = data
72
73    def data(self, index, role=Qt.DisplayRole):
74        row, col = index.row(), index.column()
75        return self._roleData[role][row][col]
76
77    def index(self, row, col, parent=QModelIndex()):
78        return self.createIndex(row, col, 0)
79
80    def parent(self, index):
81        return QModelIndex()
82
83    def rowCount(self, index=QModelIndex()):
84        if index.isValid():
85            return 0
86        else:
87            return len(self._data)
88
89    def columnCount(self, index=QModelIndex()):
90        return len(self._header)
91
92    def headerData(self, section, orientation, role=Qt.DisplayRole):
93        if role == Qt.DisplayRole:
94            return QVariant(self._header[section])
95        return QVariant()
96
97
98class LinkFmt(object):
99
100    def __init__(self, link_fmt, name):
101        self.link_fmt = link_fmt
102        self.name = name
103
104    def format(self, *args, **kwargs):
105        return Link(self.link_fmt.format(*args, **kwargs), **kwargs)
106
107    def __repr__(self):
108        return "<LinkFmt " + repr(self.name) + " >"
109
110    def __str__(self):
111        return self.name
112
113
114class Link(object):
115
116    def __init__(self, link, text=None, **kwargs):
117        self.link = link
118        self.text = text if text is not None else "link"
119        self.__dict__.update(kwargs)
120
121
122@lru_cache(maxsize=2)
123def get_ncbi_info(taxid):
124    return gene.NCBIGeneInfo(taxid)
125
126
127def ncbi_info(taxid, genes, advance=None):
128    taxid = gene.NCBIGeneInfo.TAX_MAP.get(taxid, taxid)
129    download.ensure_downloaded(
130        "NCBI_geneinfo",
131        "gene_info.%s.db" % taxid,
132        advance
133    )
134    info = get_ncbi_info(taxid)
135
136    schema_link = LinkFmt(
137        "http://www.ncbi.nlm.nih.gov/sites/entrez?Db=gene&Cmd=ShowDetailView&TermToSearch={gene_id}",
138        name="NCBI ID")
139
140    schema = [schema_link, "Symbol", "Locus Tag", "Chromosome",
141              "Description", "Synonyms", "Nomenclature"]
142    ret = []
143    for gene_name in genes:
144        gi = info.get_info(gene_name)
145        if gi:
146            ret.append([schema_link.format(gene_id=gi.gene_id, text=gi.gene_id),
147                        gi.symbol + " (%s)" % gene_name if gene_name != gi.symbol else gi.symbol,
148                        gi.locus_tag or "",
149                        gi.chromosome or "",
150                        gi.description or "",
151                        ", ".join(gi.synonyms),
152                        gi.symbol_from_nomenclature_authority or ""
153                        ])
154        else:
155            ret.append(None)
156    return schema, ret
157
158
159def dicty_info(taxid, genes, advance=None):
160    from .. import dicty
161    download.ensure_downloaded(
162        dicty.DictyBase.domain,
163        dicty.DictyBase.filename,
164        advance
165    )
166    info = dicty.DictyBase()
167    name_matcher = gene.GMDicty()
168    name_matcher.set_targets(info.info.keys())
169    schema_link = LinkFmt(
170        "http://dictybase.org/db/cgi-bin/gene_page.pl?dictybaseid={gene_id}",
171        name="Dicty Base Id")
172    schema = [schema_link, "Name", "Synonyms", "Gene Products"]
173
174    ret = []
175    for gene_name in genes:
176        gene_name = name_matcher.umatch(gene_name)
177        gi = info.info.get(gene_name, None)
178        if gi:
179            ret.append([schema_link.format(gene_id=gene_name, text=gene_name),
180                        gi[0] + " (%s)" % gene_name if gene_name != gi[0] else gi[0],  # Gene Name
181                        ", ".join(gi[1]),  # Synonyms
182                        gi[2] or "",  # Gene Products
183                        ])
184
185        else:
186            ret.append(None)
187
188    return schema, ret
189
190
191INFO_SOURCES = {
192    "default": [("NCBI Info", ncbi_info)],
193    "352472": [("NCBI Info", ncbi_info),
194               ("Dicty Base", dicty_info)]
195}
196
197
198class OWGeneInfo(OWWidget):
199    settingsList = ["organismIndex", "geneAttr", "useAttr", "autoCommit",
200                    "taxid"]
201    contextHandlers = {
202        "": DomainContextHandler(
203            "", ["organismIndex", "geneAttr", "useAttr", "useAltSource",
204                 "taxid"]
205        )
206    }
207
208    def __init__(self, parent=None, signalManager=None, name="Gene Info"):
209        OWWidget.__init__(self, parent, signalManager, name)
210
211        self.inputs = [("Examples", Orange.data.Table, self.setData)]
212        self.outputs = [("Selected Examples", Orange.data.Table)]
213
214        self.organismIndex = 0
215        self.taxid = None
216        self.geneAttr = 0
217        self.useAttr = False
218        self.autoCommit = False
219        self.searchString = ""
220        self.selectionChangedFlag = False
221        self.useAltSource = 0
222        self.loadSettings()
223
224        self.__initialized = False
225        self.initfuture = None
226        self.itemsfuture = None
227
228        self.infoLabel = OWGUI.widgetLabel(
229            OWGUI.widgetBox(self.controlArea, "Info", addSpace=True),
230            "Initializing\n"
231        )
232
233        self.organisms = None
234        self.organismBox = OWGUI.widgetBox(
235            self.controlArea, "Organism", addSpace=True)
236
237        self.organismComboBox = OWGUI.comboBox(
238            self.organismBox, self, "organismIndex",
239            callback=self._onSelectedOrganismChanged,
240            debuggingEnabled=0)
241
242        # For now only support one alt source, with a checkbox
243        # In the future this can be extended to multiple selections
244        self.altSourceCheck = OWGUI.checkBox(self.organismBox, self,
245                            "useAltSource", "Show information from dictyBase",
246                            callback=self.onAltSourceChange,
247#                            debuggingEnabled=0,
248                            )
249        self.altSourceCheck.hide()
250
251        box = OWGUI.widgetBox(self.controlArea, "Gene names", addSpace=True)
252        self.geneAttrComboBox = OWGUI.comboBox(
253            box, self, "geneAttr",
254            "Gene atttibute", callback=self.updateInfoItems
255        )
256        OWGUI.checkBox(box, self, "useAttr", "Use attribute names",
257                       callback=self.updateInfoItems,
258                       disables=[(-1, self.geneAttrComboBox)])
259
260        self.geneAttrComboBox.setDisabled(bool(self.useAttr))
261
262        box = OWGUI.widgetBox(self.controlArea, "Commit", addSpace=True)
263        b = OWGUI.button(box, self, "Commit", callback=self.commit)
264        c = OWGUI.checkBox(box, self, "autoCommit", "Commit on change")
265        OWGUI.setStopper(self, b, c, "selectionChangedFlag",
266                         callback=self.commit)
267
268        # A label for dictyExpress link
269        self.dictyExpressBox = OWGUI.widgetBox(
270            self.controlArea, "Dicty Express")
271        self.linkLabel = OWGUI.widgetLabel(self.dictyExpressBox, "")
272        self.linkLabel.setOpenExternalLinks(False)
273        self.connect(self.linkLabel, SIGNAL("linkActivated(QString)"),
274                     self.onDictyExpressLink)
275        self.dictyExpressBox.hide()
276
277        OWGUI.rubber(self.controlArea)
278
279        OWGUI.lineEdit(self.mainArea, self, "searchString", "Filter",
280                       callbackOnType=True, callback=self.searchUpdate)
281
282        self.treeWidget = QTreeView(self.mainArea)
283        self.treeWidget.setRootIsDecorated(False)
284        self.treeWidget.setSelectionMode(
285            QAbstractItemView.ExtendedSelection)
286        self.treeWidget.setItemDelegate(
287            LinkStyledItemDelegate(self.treeWidget))
288        self.treeWidget.setUniformRowHeights(True)
289        self.treeWidget.viewport().setMouseTracking(True)
290        self.treeWidget.setSortingEnabled(True)
291        self.mainArea.layout().addWidget(self.treeWidget)
292
293        box = OWGUI.widgetBox(self.mainArea, "",
294                              orientation="horizontal")
295        OWGUI.button(box, self, "Select Filtered",
296                     callback=self.selectFiltered)
297        OWGUI.button(box, self, "Clear Selection",
298                     callback=self.treeWidget.clearSelection)
299
300        self.resize(1000, 700)
301
302        self.geneinfo = []
303        self.cells = []
304        self.row2geneinfo = {}
305        self.data = None
306
307        # : (# input genes, # matches genes)
308        self.matchedInfo = 0, 0
309        self.selectionUpdateInProgress = False
310
311        self.setBlocking(True)
312        self.executor = ThreadExecutor(self)
313
314        self.progressBarInit()
315
316        task = Task(
317            function=partial(
318                taxonomy.ensure_downloaded,
319                callback=methodinvoke(self, "advance", ())
320            )
321        )
322
323        task.resultReady.connect(self.initialize)
324        task.exceptionReady.connect(self._onInitializeError)
325
326        self.initfuture = self.executor.submit(task)
327
328    @Slot()
329    def advance(self):
330        assert self.thread() is QThread.currentThread()
331
332        self.progressBarSet(self.progressBarValue + 1,
333                            processEventsFlags=None)
334
335    def initialize(self):
336        if self.__initialized:
337            # Already initialized
338            return
339
340        self.progressBarFinished()
341
342        self.organisms = sorted(
343            set([name.split(".")[-2] for name in
344                 serverfiles.listfiles("NCBI_geneinfo")] +
345                gene.NCBIGeneInfo.essential_taxids())
346        )
347
348        self.organismComboBox.addItems(
349            [taxonomy.name(tax_id) for tax_id in self.organisms]
350        )
351        if self.taxid in self.organisms:
352            self.organismIndex = self.organisms.index(self.taxid)
353
354        self.infoLabel.setText("No data on input\n")
355        self.__initialized = True
356        self.initfuture = None
357
358        self.setBlocking(False)
359
360    def _onInitializeError(self, exc):
361        sys.excepthook(type(exc), exc.args, None)
362        self.error(0, "Could not download the necessary files.")
363
364    def _onSelectedOrganismChanged(self):
365        self.taxid = self.organisms[self.organismIndex]
366        if self.data is not None:
367            self.updateInfoItems()
368
369    def setData(self, data=None):
370        if not self.__initialized:
371            self.initfuture.result()
372            self.initialize()
373
374        if self.itemsfuture is not None:
375            raise Exception("Already processing")
376
377        self.closeContext()
378        self.data = data
379
380        if data:
381            self.geneAttrComboBox.clear()
382            self.attributes = \
383                [attr for attr in (data.domain.variables +
384                                   data.domain.getmetas().values())
385                 if isinstance(attr, (Orange.feature.String,
386                                      Orange.feature.Discrete))]
387
388            self.geneAttrComboBox.addItems(
389                [attr.name for attr in self.attributes]
390            )
391
392            self.taxid = data_hints.get_hint(self.data, "taxid", self.taxid)
393            self.useAttr = data_hints.get_hint(
394                self.data, "genesinrows", self.useAttr)
395
396            self.openContext("", data)
397            self.geneAttr = min(self.geneAttr, len(self.attributes) - 1)
398
399            if self.taxid in self.organisms:
400                self.organismIndex = self.organisms.index(self.taxid)
401
402            self.updateInfoItems()
403        else:
404            self.clear()
405
406    def infoSource(self):
407        """ Return the current selected info source getter function from
408        INFO_SOURCES
409        """
410        org = self.organisms[min(self.organismIndex, len(self.organisms) - 1)]
411        if org not in INFO_SOURCES:
412            org = "default"
413        sources = INFO_SOURCES[org]
414        name, func = sources[min(self.useAltSource, len(sources) - 1)]
415        return name, func
416
417    def inputGenes(self):
418        if self.useAttr:
419            genes = [attr.name for attr in self.data.domain.attributes]
420        elif self.attributes:
421            attr = self.attributes[self.geneAttr]
422            genes = [str(ex[attr]) for ex in self.data
423                     if not ex[attr].isSpecial()]
424        else:
425            genes = []
426        return genes
427
428    def updateInfoItems(self):
429        self.warning(0)
430        if not self.data:
431            return
432
433        genes = self.inputGenes()
434        if self.useAttr:
435            genes = [attr.name for attr in self.data.domain.attributes]
436        elif self.attributes:
437            attr = self.attributes[self.geneAttr]
438            genes = [str(ex[attr]) for ex in self.data
439                     if not ex[attr].isSpecial()]
440        else:
441            genes = []
442        if not genes:
443            self.warning(0, "Could not extract genes from input dataset.")
444
445        self.warning(1)
446        org = self.organisms[min(self.organismIndex, len(self.organisms) - 1)]
447        source_name, info_getter = self.infoSource()
448
449        self.error(0)
450
451        self.updateDictyExpressLink(genes, show=org == "352472")
452        self.altSourceCheck.setVisible(org == "352472")
453
454        self.progressBarInit()
455        self.setBlocking(True)
456        self.setEnabled(False)
457        self.infoLabel.setText("Retrieving info records.\n")
458
459        self.genes = genes
460
461        task = Task(
462            function=partial(
463                info_getter, org, genes,
464                advance=methodinvoke(self, "advance", ()))
465        )
466        self.itemsfuture = self.executor.submit(task)
467        task.finished.connect(self._onItemsCompleted)
468
469    def _onItemsCompleted(self):
470        self.setBlocking(False)
471        self.progressBarFinished()
472        self.setEnabled(True)
473
474        try:
475            schema, geneinfo = self.itemsfuture.result()
476        finally:
477            self.itemsfuture = None
478
479        self.geneinfo = geneinfo = list(zip(self.genes, geneinfo))
480        self.cells = cells = []
481        self.row2geneinfo = {}
482        links = []
483        for i, (_, gi) in enumerate(geneinfo):
484            if gi:
485                row = []
486                for _, item in zip(schema, gi):
487                    if isinstance(item, Link):
488                        # TODO: This should be handled by delegates
489                        row.append(item.text)
490                        links.append(item.link)
491                    else:
492                        row.append(item)
493                cells.append(row)
494                self.row2geneinfo[len(cells) - 1] = i
495
496        model = TreeModel(cells, [str(col) for col in schema], None)
497
498        model.setColumnLinks(0, links)
499        proxyModel = QSortFilterProxyModel(self)
500        proxyModel.setSourceModel(model)
501        self.treeWidget.setModel(proxyModel)
502        self.connect(self.treeWidget.selectionModel(),
503                     SIGNAL("selectionChanged(QItemSelection , QItemSelection )"),
504                     self.commitIf)
505
506        for i in range(7):
507            self.treeWidget.resizeColumnToContents(i)
508            self.treeWidget.setColumnWidth(
509                i, min(self.treeWidget.columnWidth(i), 200)
510            )
511
512        self.infoLabel.setText("%i genes\n%i matched NCBI's IDs" %
513                               (len(self.genes), len(cells)))
514        self.matchedInfo = len(self.genes), len(cells)
515
516    def clear(self):
517        self.infoLabel.setText("No data on input\n")
518        self.treeWidget.setModel(
519            TreeModel([], ["NCBI ID", "Symbol", "Locus Tag",
520                           "Chromosome", "Description", "Synonyms",
521                           "Nomenclature"], self.treeWidget))
522
523        self.geneAttrComboBox.clear()
524        self.send("Selected Examples", None)
525
526    def commitIf(self, *args):
527        if self.autoCommit and not self.selectionUpdateInProgress:
528            self.commit()
529        else:
530            self.selectionChangedFlag = True
531
532    def commit(self):
533        if not self.data:
534            return
535        model = self.treeWidget.model()
536        mapToSource = model.mapToSource
537        selectedRows = self.treeWidget.selectedIndexes()
538        selectedRows = [mapToSource(index).row() for index in selectedRows]
539        model = model.sourceModel()
540
541        selectedGeneids = [self.row2geneinfo[row] for row in selectedRows]
542        selectedIds = [self.geneinfo[i][0] for i in selectedGeneids]
543        selectedIds = set(selectedIds)
544        gene2row = dict((self.geneinfo[self.row2geneinfo[row]][0], row)
545                        for row in selectedRows)
546
547        if self.useAttr:
548            def is_selected(attr):
549                return attr.name in selectedIds
550            attrs = [attr for attr in self.data.domain.attributes
551                     if is_selected(attr)]
552            domain = Orange.data.Domain(attrs, self.data.domain.classVar)
553            domain.addmetas(self.data.domain.getmetas())
554            newdata = Orange.data.Table(domain, self.data)
555            self.send("Selected Examples", newdata)
556        elif self.attributes:
557            attr = self.attributes[self.geneAttr]
558            examples = [ex for ex in self.data if str(ex[attr]) in selectedIds]
559            # Add gene info
560            domain = Orange.data.Domain(
561                self.data.domain, self.data.domain.classVar)
562            domain.addmetas(self.data.domain.getmetas())
563            n_columns = model.columnCount()
564
565            headers = [str(model.headerData(i, Qt.Horizontal, Qt.DisplayRole)
566                           .toString())
567                       for i in range(n_columns)]
568            new_meta_attrs = [(Orange.feature.Descriptor.new_meta_id(),
569                               Orange.feature.String(name))
570                              for name in headers]
571            domain.addmetas(dict(new_meta_attrs))
572            examples = [Orange.data.Instance(domain, ex) for ex in examples]
573            for ex in examples:
574                for i, (_, meta) in enumerate(new_meta_attrs):
575                    index = model.index(gene2row[str(ex[attr])], i)
576                    ex[meta] = str(
577                        model.data(index, Qt.DisplayRole).toString()
578                    )
579
580            if examples:
581                newdata = Orange.data.Table(examples)
582            else:
583                newdata = None
584            self.send("Selected Examples", newdata)
585        else:
586            self.send("Selected Examples", None)
587
588    def rowFiltered(self, row):
589        searchStrings = self.searchString.lower().split()
590        row = unicode(" ".join(self.cells[row]).lower(), errors="ignore")
591        return not all([s in row for s in searchStrings])
592
593    def searchUpdate(self):
594        if not self.data:
595            return
596        searchStrings = self.searchString.lower().split()
597        index = self.treeWidget.model().sourceModel().index
598        mapFromSource = self.treeWidget.model().mapFromSource
599        for i, row in enumerate(self.cells):
600            row = unicode(" ".join(row).lower(), errors="ignore")
601            self.treeWidget.setRowHidden(
602                mapFromSource(index(i, 0)).row(),
603                QModelIndex(),
604                not all([s in row for s in searchStrings]))
605
606    def selectFiltered(self):
607        if not self.data:
608            return
609        itemSelection = QItemSelection()
610
611        index = self.treeWidget.model().sourceModel().index
612        mapFromSource = self.treeWidget.model().mapFromSource
613        for i, row in enumerate(self.cells):
614            if not self.rowFiltered(i):
615                itemSelection.select(mapFromSource(index(i, 0)),
616                                     mapFromSource(index(i, 0)))
617        self.treeWidget.selectionModel().select(
618            itemSelection,
619            QItemSelectionModel.Select | QItemSelectionModel.Rows)
620
621    def sendReport(self):
622        from Orange.OrangeWidgets import OWReport
623        genes, matched = self.matchedInfo
624
625        if self.organisms:
626            org = self.organisms[min(self.organismIndex,
627                                     len(self.organisms) - 1)]
628            org_name = taxonomy.name(org)
629        else:
630            org = None
631            org_name = None
632        if self.data is not None:
633            self.reportRaw(
634                "<p>Input: %i genes of which %i (%.1f%%) matched NCBI synonyms"
635                "<br>"
636                "Organism: %s"
637                "<br>"
638                "Filter: %s"
639                "</p>" % (genes, matched, 100.0 * matched / genes, org_name,
640                          self.searchString)
641            )
642            self.reportSubsection("Gene list")
643            self.reportRaw(reportItemView(self.treeWidget))
644        else:
645            self.reportRaw("<p>No input</p>")
646
647    def updateDictyExpressLink(self, genes, show=False):
648        def fix(ddb):
649            if ddb.startswith("DDB"):
650                if not ddb.startswith("DDB_G"):
651                    ddb = ddb.replace("DDB", "DDB_G")
652                return ddb
653            return None
654        if show:
655            genes = [fix(gene) for gene in genes if fix(gene)]
656            link1 = '<a href="http://dictyexpress.biolab.si/run/index.php?gene=%s">Microarray profile</a>'
657            link2 = '<a href="http://dictyexpress.biolab.si/run/index.php?gene=%s&db=rnaseq">RNA-Seq profile</a>'
658            self.linkLabel.setText(link1 + "<br/>" + link2)
659
660            show = any(genes)
661
662        if show:
663            self.dictyExpressBox.show()
664        else:
665            self.dictyExpressBox.hide()
666
667    def onDictyExpressLink(self, link):
668        if not self.data:
669            return
670
671        selectedIndexes = self.treeWidget.selectedIndexes()
672        if not len(selectedIndexes):
673            QMessageBox.information(
674                self, "No gene ids selected",
675                "Please select some genes and try again."
676            )
677            return
678        model = self.treeWidget.model()
679        mapToSource = model.mapToSource
680        selectedRows = self.treeWidget.selectedIndexes()
681        selectedRows = [mapToSource(index).row() for index in selectedRows]
682        model = model.sourceModel()
683
684        selectedGeneids = [self.row2geneinfo[row] for row in selectedRows]
685        selectedIds = [self.geneinfo[i][0] for i in selectedGeneids]
686        selectedIds = set(selectedIds)
687
688        def fix(ddb):
689            if ddb.startswith("DDB"):
690                if not ddb.startswith("DDB_G"):
691                    ddb = ddb.replace("DDB", "DDB_G")
692                return ddb
693            return None
694
695        genes = [fix(gene) for gene in selectedIds if fix(gene)]
696        url = str(link) % " ".join(genes)
697        QDesktopServices.openUrl(QUrl(url))
698
699    def onAltSourceChange(self):
700        self.updateInfoItems()
701
702    def onDeleteWidget(self):
703        OWWidget.onDeleteWidget(self)
704
705        # try to cancel pending tasks
706        if self.initfuture:
707            self.initfuture.cancel()
708        if self.itemsfuture:
709            self.itemsfuture.cancel()
710
711        self.executor.shutdown()
712
713
714def reportItemView(view):
715    model = view.model()
716    return reportItemModel(view, model)
717
718
719def reportItemModel(view, model, index=QModelIndex()):
720    if not index.isValid() or model.hasChildren(index):
721        columnCount, rowCount = model.columnCount(index), model.rowCount(index)
722        if not index.isValid():
723            text = ('<table>\n<tr>' +
724                    ''.join('<th>%s</th>' %
725                            model.headerData(i, Qt.Horizontal, Qt.DisplayRole)
726                            .toString()
727                            for i in range(columnCount)) +
728                    '</tr>\n')
729        else:
730            pass
731        text += ''.join('<tr>' +
732                        ''.join('<td>' + reportItemModel(view, model, model.index(row, column, index)) +
733                                '</td>' for column in range(columnCount)) +
734                        '</tr>\n'
735                        for row in range(rowCount)
736                        if not view.isRowHidden(row, index))
737        text += '</table>'
738        return text
739    else:
740        variant = model.data(index, Qt.DisplayRole)
741        return str(variant.toString()) if variant.isValid() else ""
742
743
744if __name__ == "__main__":
745    app = QApplication(sys.argv)
746    data = Orange.data.Table("brown-selected.tab")
747    w = OWGeneInfo()
748    w.show()
749
750    w.setData(data)
751    app.exec_()
752    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.