Changeset 1913:5e94e30690e0 in orange-bioinformatics


Ignore:
Timestamp:
11/21/13 11:41:01 (5 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Added gene name/description to the completion popup view.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • orangecontrib/bio/widgets/OWSelectGenes.py

    r1912 r1913  
    33import operator 
    44from collections import defaultdict, namedtuple 
     5from operator import itemgetter 
    56from xml.sax.saxutils import escape 
    67 
     
    910from PyQt4.QtGui import ( 
    1011    QFrame, QHBoxLayout, QPlainTextEdit, QSyntaxHighlighter, QTextCharFormat, 
    11     QTextCursor, QCompleter, QStringListModel, QStandardItemModel, 
    12     QStandardItem, QListView, QStyle, QStyledItemDelegate, 
     12    QTextCursor, QCompleter, QStandardItemModel, QSortFilterProxyModel, 
     13    QStandardItem, QListView, QTreeView, QStyle, QStyledItemDelegate, 
    1314    QStyleOptionViewItemV4, QPalette, QColor, QApplication, QAction, 
    1415    QToolButton, QItemSelectionModel, QPlainTextDocumentLayout, QTextDocument, 
     
    1617) 
    1718 
    18 from PyQt4.QtCore import Qt, QEvent, QVariant, pyqtSignal as Signal 
     19from PyQt4.QtCore import Qt, QEvent, QVariant, QThread, pyqtSignal as Signal 
    1920 
    2021import Orange 
    2122 
    22 from Orange.OrangeWidgets.OWWidget import \ 
     23from Orange.OrangeWidgets.OWWidget import ( 
    2324    OWWidget, DomainContextHandler, Default 
     25) 
     26 
     27from Orange.OrangeWidgets.OWConcurrent import ( 
     28    ThreadExecutor, Task, methodinvoke 
     29) 
    2430 
    2531from Orange.OrangeWidgets.OWItemModels import VariableListModel 
    2632from Orange.OrangeWidgets import OWGUI 
    2733from Orange.orng.orngDataCaching import data_hints 
     34from Orange.bio import obiGene as geneinfo 
    2835 
    2936 
     
    129136 
    130137    SelectInput, SelectCustom = 0, 1 
     138    CompletionRole = Qt.UserRole + 1 
    131139 
    132140    def __init__(self, parent=None, signalManager=None, title=NAME): 
     
    162170        # Selected subset variable index 
    163171        self.subsetGeneIndex = -1 
     172        self.geneinfo = (None, None) 
     173        self._executor = ThreadExecutor() 
     174        self._infotask = None 
    164175 
    165176        box = OWGUI.widgetBox(self.controlArea, "Gene Attribute") 
     
    231242        completer = ListCompleter() 
    232243        completer.setCompletionMode(QCompleter.PopupCompletion) 
     244#         completer.setCompletionRole(OWSelectGenes.CompletionRole) 
    233245        completer.setCaseSensitivity(Qt.CaseInsensitive) 
    234246        completer.setMaxVisibleItems(10) 
    235         completer.popup().setAlternatingRowColors(True) 
    236         completer.setModel(QStringListModel([], self)) 
     247 
     248        popup = QTreeView() 
     249        popup.setSelectionMode(QTreeView.ExtendedSelection) 
     250        popup.setEditTriggers(QTreeView.NoEditTriggers) 
     251        popup.setRootIsDecorated(False) 
     252        popup.setAlternatingRowColors(True) 
     253        popup.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) 
     254        popup.setMaximumWidth(500) 
     255        popup.header().setStretchLastSection(False) 
     256        popup.header().hide() 
     257 
     258        completer.setPopup(popup) 
     259        completer.setModel(SetFilterProxyModel(self)) 
    237260 
    238261        self.entryField.setCompleter(completer) 
     
    351374 
    352375        self._changedFlag = True 
    353         self._updateCompletionModel() 
     376 
    354377        if data is not None: 
    355378            self.taxid = data_hints.get_hint(data, "taxid", None) 
     
    358381 
    359382        self.openContext("", data) 
     383 
     384        if self.taxid is None: 
     385            self.geneinfo = (None, None) 
     386 
     387        if self.taxid != self.geneinfo[0]:  # and self.taxid in available_organisms 
     388            self.geneinfo = (self.taxid, None) 
     389            # TODO: cache the least recently used info objects. 
     390            # Or better yet, optimize the implementation of NCBIGeneInfo 
     391            task = Task(function=lambda taxid=self.taxid: 
     392                                    (taxid, geneinfo.NCBIGeneInfo(taxid))) 
     393            task.resultReady.connect(self._on_geneInfoReady) 
     394            self._infotask = task 
     395            self._executor.submit(task) 
     396 
     397        self._updateCompletionModel() 
    360398 
    361399        self.commit() 
     
    468506            names = [] 
    469507 
     508        infodict = {} 
     509 
     510        if self.geneinfo[1] is not None: 
     511            info = [(name, self.geneinfo[1].get_info(name, None)) 
     512                    for name in names] 
     513            info = filter(itemgetter(1), info) 
     514            infodict = dict(info) 
     515 
     516        names = sorted(set(names)) 
     517 
     518        model = QStandardItemModel() 
     519 
     520        def make_row(name): 
     521            if name in infodict: 
     522                info = infodict[name] 
     523                col1 = QStandardItem(name) 
     524                col1.setData(info.symbol, OWSelectGenes.CompletionRole) 
     525                return [col1, 
     526                        QStandardItem(info.symbol), 
     527                        QStandardItem(info.description)] 
     528            else: 
     529                return [QStandardItem(name)] 
     530 
     531        for name in names: 
     532            model.appendRow(make_row(name)) 
     533 
    470534        self.geneNames = names 
    471         self.entryField.completer().model().setStringList(sorted(set(names))) 
     535        self.entryField.completer().model().setSourceModel(model) 
    472536        self.entryField.document().highlighter.setNames(names) 
    473537 
     
    489553            self.invalidateOutput() 
    490554            to_complete = sorted(set(self.geneNames) - set(names)) 
    491             self.entryField.completer().model().setStringList(to_complete) 
     555            self.entryField.completer().model().setFilterFixedSet(to_complete) 
    492556 
    493557        item = self._selectedSaveSlot() 
    494558        if item: 
    495559            item.modified = item.savedata != names 
     560 
     561    def _on_geneInfoReady(self, geneinfo): 
     562        assert QThread.currentThread() is self.thread() 
     563        # Check if the gene info is for the correct (current requested) 
     564        # organism (we might receive a late response from a previous 
     565        # request) 
     566        if self.geneinfo[0] == geneinfo[0]: 
     567            self.geneinfo = geneinfo 
     568            self._updateCompletionModel() 
    496569 
    497570    def _selectedSaveSlot(self): 
     
    664737        ) 
    665738 
     739    def onDeleteWidget(self): 
     740        if self._infotask: 
     741            self._infotask.future().cancel() 
     742 
     743        self._executor.shutdown() 
     744        OWWidget.onDeleteWidget(self) 
     745 
    666746 
    667747def is_string(feature): 
     
    784864        pos = cursor.position() 
    785865 
    786         if pos == len(text) or not(text[pos].strip()): 
     866        if pos == len(text) or not text[pos].strip(): 
    787867            # cursor is at end of text or whitespace 
    788868            # find the beginning of the current word 
     
    801881                    rect.setWidth(popup.width()) 
    802882                else: 
    803                     rect.setWidth(popup.sizeHintForColumn(0) + 
    804                                   popup.verticalScrollBar().sizeHint().width()) 
     883                    view_adjust_size_to_contents(popup) 
     884                    rect.setWidth(popup.width()) 
    805885 
    806886                # Popup the completer list 
     
    913993            text = text[:pos] + text[end:] 
    914994        return [item for item in text.split() if item.strip()] 
     995 
     996 
     997def view_adjust_column_sizes(view, maxWidth=None): 
     998    """ 
     999    Adjust view's column sizes to to contents. 
     1000    """ 
     1001    if maxWidth is None: 
     1002        maxWidth = sys.maxint 
     1003 
     1004    for col in range(view.model().columnCount()): 
     1005        width = min(view.sizeHintForColumn(col), maxWidth) 
     1006        view.setColumnWidth(col, width) 
     1007 
     1008 
     1009def view_adjust_size_to_contents(view): 
     1010    """ 
     1011    Adjust the view to a reasonable size based in it's contents. 
     1012    """ 
     1013    view_adjust_column_sizes(view) 
     1014    w = sum([view.columnWidth(col) 
     1015             for col in range(view.model().columnCount())]) 
     1016    w += view.verticalScrollBar().sizeHint().width() 
     1017 
     1018    h = view.sizeHintForRow(0) * 7 
     1019    h += view.horizontalScrollBar().sizeHint().height() 
     1020    view.resize(w, h) 
    9151021 
    9161022 
     
    10001106 
    10011107    def _complete(self): 
    1002         selection = self.popup().selectionModel().selection() 
    1003         indexes = selection.indexes() 
    1004  
    1005         items = [toString(index.data(self.completionRole())) 
    1006                 for index in indexes] 
     1108        column = self.completionColumn() 
     1109        role = self.completionRole() 
     1110        indexes = self.popup().selectionModel().selectedRows(column) 
     1111 
     1112        items = [toString(index.data(role)) for index in indexes] 
    10071113 
    10081114        if self.popup().isVisible(): 
     
    10111117        if items: 
    10121118            self.activated.emit(items) 
     1119 
     1120 
     1121class SetFilterProxyModel(QSortFilterProxyModel): 
     1122    def __init__(self, *args, **kwargs): 
     1123        super(SetFilterProxyModel, self).__init__(*args, **kwargs) 
     1124        self._filterFixedSet = None 
     1125 
     1126    def setFilterFixedSet(self, items): 
     1127        if items is None: 
     1128            self._filterFixedSet = None 
     1129        else: 
     1130            self._filterFixedSet = set(items) 
     1131        self.invalidateFilter() 
     1132 
     1133    def filterAcceptsRow(self, row, parent): 
     1134        if self._filterFixedSet is None: 
     1135            return True 
     1136 
     1137        model = self.sourceModel() 
     1138        col = self.filterKeyColumn() 
     1139        var = model.data(model.index(row, col, parent), 
     1140                         self.filterRole()) 
     1141        var = toString(var) 
     1142        return var in self._filterFixedSet 
    10131143 
    10141144 
     
    10281158 
    10291159from PyQt4.QtGui import ( 
    1030     QVBoxLayout, QComboBox, QLineEdit, QTreeView, QDialogButtonBox, 
    1031     QSortFilterProxyModel, QProgressBar, QStackedWidget, QSizePolicy 
     1160    QVBoxLayout, QComboBox, QLineEdit, QDialogButtonBox, 
     1161    QProgressBar, QStackedWidget, QSizePolicy 
    10321162) 
    10331163 
     
    10361166from Orange.bio import obiTaxonomy as taxonomy 
    10371167from Orange.utils import serverfiles 
    1038  
    1039 from Orange.OrangeWidgets.OWConcurrent import ( 
    1040     ThreadExecutor, Task, methodinvoke 
    1041 ) 
    10421168 
    10431169 
     
    11691295 
    11701296    def _on_loadFinished(self): 
     1297        assert QThread.currentThread() is self.thread() 
    11711298        self._stack.setCurrentWidget(self.orgcombo) 
    11721299 
Note: See TracChangeset for help on using the changeset viewer.