Example #1
0
class VariableComboBox(QWidget):

    def __init__(self, id, text="Input data", default=[], model=None):
        QWidget.__init__(self)
        self.setToolTip("<p>Select input dataset</p>")
        self.id = id
        if model is None:
            self.model = TreeModel()
        else:
            self.model = model
        self.comboBox = QComboBox()
        self.treeView = QListView(self.comboBox)
        self.connect(self.comboBox, SIGNAL("currentIndexChanged(int)"), 
            self.changeSelectedText)
        self.proxyModel = SortFilterProxyModel()
        self.proxyModel.setDynamicSortFilter(True)
        self.proxyModel.setFilterKeyColumn(1)
        self.proxyModel.setSourceModel(self.model)
        regexp = QRegExp("|".join([r"%s" % i for i in default]))
        self.proxyModel.setFilterRegExp(regexp)
#        self.treeView.header().hide()
        self.currentText = QString()
        self.treeView.setModel(self.proxyModel)

        self.comboBox.setModel(self.proxyModel)
        self.comboBox.setView(self.treeView)
#        self.treeView.hideColumn(1)
#        self.treeView.hideColumn(2)
#        self.treeView.hideColumn(3)
        self.treeView.viewport().installEventFilter(self.comboBox)
        label = QLabel(text)
        hbox = HBoxLayout()
        hbox.addWidget(label)
        hbox.addWidget(self.comboBox)
        self.setLayout(hbox)
        self.changeSelectedText(None)

    def changeSelectedText(self, index):
        item = self.treeView.currentIndex()
        if not item.isValid():
            item = self.proxyModel.index(0,0, QModelIndex())
        tree = self.treeView.model().parentTree(item)
        self.currentText = tree

    def parameterValues(self):
        return {self.id:self.currentText}
Example #2
0
class QuickAccessWidget(QFrame):
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.Box | QFrame.Sunken)
        self.setStyleSheet("QListView {background: transparent; }")
        self.setLayout(QHBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.listView = QListView(self)
        self.layout().addWidget(self.listView)

        self.listView.setModel(self.window().quickAccessModel)
        self.listView.setMovement(QListView.Snap)
        self.listView.setFlow(QListView.LeftToRight)
        self.listView.setResizeMode(QListView.Adjust)
        gridSize = self.logicalDpiX() / 96 * 60
        self.listView.setGridSize(QSize(gridSize, gridSize))
        self.listView.setViewMode(QListView.IconMode)

        self.listView.activated.connect(self.listView.model().runShortcut)
Example #3
0
class QuickAccessWidget(QFrame):
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.Box | QFrame.Sunken)
        self.setStyleSheet("QListView {background: transparent; }")
        self.setLayout(QHBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.listView = QListView(self)
        self.layout().addWidget(self.listView)

        self.listView.setModel(self.window().quickAccessModel)
        self.listView.setMovement(QListView.Snap)
        self.listView.setFlow(QListView.LeftToRight)
        self.listView.setResizeMode(QListView.Adjust)
        gridSize = self.logicalDpiX() / 96 * 60
        self.listView.setGridSize(QSize(gridSize, gridSize))
        self.listView.setViewMode(QListView.IconMode)

        self.listView.activated.connect(self.listView.model().runShortcut)
Example #4
0
class DataListToListWidget(DataPusherWidget):
    '''
    DataListToListWidget widget for settings dialogs
    '''
    def __init__(self, parent=None):
        super(DataListToListWidget, self).__init__(parent)
        self.rightListView = None
        self.leftDataTypeListView = None
        self.setupLeftPane()
        self.setupRightPane()
        self.connectSlots()

    def setupLeftPane(self):
        self.leftDataTypeListView = QListView()
        self.leftScrollArea.setWidgetResizable(True)
        self.leftVerticalLayout.addWidget(self.leftDataTypeListView)
        self.leftDataTypeListView.setSelectionMode(
            QAbstractItemView.ExtendedSelection)

    def setupRightPane(self):
        self.rightListView = QListView()
        self.rightScrollArea.setWidgetResizable(True)
        self.rightVerticalLayout.addWidget(self.rightListView)
        self.rightListView.setSelectionMode(QAbstractItemView.SingleSelection)

    def connectSlots(self):
        self.rightArrowPushButton.clicked.connect(self.rightArrowClicked)
        self.upPushButton.clicked.connect(self.upClicked)
        self.downPushButton.clicked.connect(self.downClicked)
        self.deletePushButton.clicked.connect(self.deleteClicked)

    def setLeftModel(self, model):
        logger.debug(">>setLeftModel()")
        self.leftDataTypeListView.setModel(model)

    def populateRightListView(self, rightDataList):
        assert all(isinstance(item, str) for item in rightDataList)
        model = self.rightListView.model()
        if model is None:
            model = QStandardItemModel(self.rightListView)
        for data in rightDataList:
            item = QStandardItem(data)
            # disable checkbox as is confusing
            item.setCheckable(False)
            model.appendRow(item)
        self.rightListView.setModel(model)

    def rightArrowClicked(self):
        selectionModel = self.leftDataTypeListView.selectionModel()
        rightDataList = []
        if selectionModel is not None:
            indexes = selectionModel.selectedIndexes()
            for index in indexes:
                dataText = self.leftDataTypeListView.model().itemFromIndex(
                    index).text()
                rightDataList.append(dataText)
            self.populateRightListView(rightDataList)

    #see https://wiki.python.org/moin/PyQt/Reading%20selections%20from%20a%20selection%20model
    def upClicked(self):
        logger.debug("upClicked")

        promoteList = []
        indexes = self.rightListView.selectionModel().selectedIndexes()
        model = self.rightListView.model()
        parent = QModelIndex()
        #selectedItems = self.rightListView.selectionModel().selectedItems()
        for index in indexes:
            #item = model.index(index.row(), index.column(), parent)
            item = self.getComboBoxItemFromIndex(model, index)
            #promoteList.append(item)
            promoteList.append(str(item.data()))
            logger.debug("data: {0}".format(item.data()))
        promotedList = self.resortRightList(promoteList)
        self.populateRightListView(promotedList)

    def getComboBoxItemFromIndex(self, model, index):
        parent = QModelIndex()
        item = model.index(index.row(), index.column(), parent)
        return item

    def downClicked(self):
        logger.debug("downClicked")

    def deleteClicked(self):
        logger.debug("delete clicked")
        model = self.rightListView.model()
        trackList = []
        for row in range(model.rowCount()):
            item = model.item(row)
            #keep unchecked items
            if item.checkState() == Qt.Unchecked:
                trackList.append(item)
        self.populateRightListView(trackList)
        logger.debug("deleteClicked")

    def resortRightList(self, promoteList):
        trackList = self.getRightListViewStrings()
        assert all(isinstance(item, str) for item in trackList)
        for item in promoteList:
            index = trackList.index(item)
            if index > 0:
                trackList.insert(index - 1, trackList.pop(index))
        return trackList

    def getLeftListViewStrings(self):
        model = self.leftDataTypeListView.model()
        itemList = []
        if model is not None:
            for row in range(model.rowCount()):
                item = model.item(row)
                itemList.append(item.text())
        return itemList

    def getRightListViewStrings(self):
        model = self.rightListView.model()
        itemList = []
        if model is not None:
            for row in range(model.rowCount()):
                item = model.item(row)
                itemList.append(item.text())
        return itemList
Example #5
0
class SortedListWidget(QWidget):
    sortingOrderChanged = Signal()

    class _MyItemDelegate(QStyledItemDelegate):

        def __init__(self, sortingModel, parent):
            QStyledItemDelegate.__init__(self, parent)
            self.sortingModel = sortingModel

        def sizeHint(self, option, index):
            size = QStyledItemDelegate.sizeHint(self, option, index)
            return QSize(size.width(), size.height() + 4)

        def createEditor(self, parent, option, index):
            cb = QComboBox(parent)
            cb.setModel(self.sortingModel)
            cb.showPopup()
            return cb

        def setEditorData(self, editor, index):
            pass  # TODO: sensible default

        def setModelData(self, editor, model, index):
            text = editor.currentText()
            model.setData(index, text)

    def __init__(self, *args):
        QWidget.__init__(self, *args)
        self.setContentsMargins(0, 0, 0, 0)
        gridLayout = QGridLayout()
        gridLayout.setContentsMargins(0, 0, 0, 0)
        gridLayout.setSpacing(1)

        model = QStandardItemModel(self)
        model.rowsInserted.connect(self.__changed)
        model.rowsRemoved.connect(self.__changed)
        model.dataChanged.connect(self.__changed)

        self._listView = QListView(self)
        self._listView.setModel(model)
#        self._listView.setDragEnabled(True)
        self._listView.setDropIndicatorShown(True)
        self._listView.setDragDropMode(QListView.InternalMove)
        self._listView.viewport().setAcceptDrops(True)
        self._listView.setMinimumHeight(100)

        gridLayout.addWidget(self._listView, 0, 0, 2, 2)

        vButtonLayout = QVBoxLayout()

        self._upAction = QAction(
            "\u2191", self, toolTip="Move up")

        self._upButton = QToolButton(self)
        self._upButton.setDefaultAction(self._upAction)
        self._upButton.setSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)

        self._downAction = QAction(
            "\u2193", self, toolTip="Move down")

        self._downButton = QToolButton(self)
        self._downButton.setDefaultAction(self._downAction)
        self._downButton.setSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)

        vButtonLayout.addWidget(self._upButton)
        vButtonLayout.addWidget(self._downButton)

        gridLayout.addLayout(vButtonLayout, 0, 2, 2, 1)

        hButtonLayout = QHBoxLayout()

        self._addAction = QAction("+", self)
        self._addButton = QToolButton(self)
        self._addButton.setDefaultAction(self._addAction)

        self._removeAction = QAction("-", self)
        self._removeButton = QToolButton(self)
        self._removeButton.setDefaultAction(self._removeAction)
        hButtonLayout.addWidget(self._addButton)
        hButtonLayout.addWidget(self._removeButton)
        hButtonLayout.addStretch(10)
        gridLayout.addLayout(hButtonLayout, 2, 0, 1, 2)

        self.setLayout(gridLayout)
        self._addAction.triggered.connect(self._onAddAction)
        self._removeAction.triggered.connect(self._onRemoveAction)
        self._upAction.triggered.connect(self._onUpAction)
        self._downAction.triggered.connect(self._onDownAction)

    def sizeHint(self):
        size = QWidget.sizeHint(self)
        return QSize(size.width(), 100)

    def _onAddAction(self):
        item = QStandardItem("")
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listView.model().appendRow(item)
        self._listView.setCurrentIndex(item.index())
        self._listView.edit(item.index())

    def _onRemoveAction(self):
        current = self._listView.currentIndex()
        self._listView.model().takeRow(current.row())

    def _onUpAction(self):
        row = self._listView.currentIndex().row()
        model = self._listView.model()
        if row > 0:
            items = model.takeRow(row)
            model.insertRow(row - 1, items)
            self._listView.setCurrentIndex(model.index(row - 1, 0))

    def _onDownAction(self):
        row = self._listView.currentIndex().row()
        model = self._listView.model()
        if row < model.rowCount() and row >= 0:
            items = model.takeRow(row)
            if row == model.rowCount():
                model.appendRow(items)
            else:
                model.insertRow(row + 1, items)
            self._listView.setCurrentIndex(model.index(row + 1, 0))

    def setModel(self, model):
        """ Set a model to select items from
        """
        self._model = model
        self._listView.setItemDelegate(self._MyItemDelegate(self._model, self))

    def addItem(self, *args):
        """ Add a new entry in the list
        """
        item = QStandardItem(*args)
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listView.model().appendRow(item)

    def setItems(self, items):
        self._listView.model().clear()
        for item in items:
            self.addItem(item)

    def items(self):
        order = []
        for row in range(self._listView.model().rowCount()):
            order.append(str(self._listView.model().item(row, 0).text()))
        return order

    def __changed(self):
        self.sortingOrderChanged.emit()

    sortingOrder = property(items, setItems)
Example #6
0
class OrderSelectorView(QObject):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parentWidget, label_text_NotSelected,
                 label_text_Selected, model):
        """Constructor"""

        QObject.__init__(self)

        self.model = model

        self.gridLayout = QGridLayout(parentWidget)

        self.verticalLayout_left_list = QVBoxLayout()
        self.label_NotSelected = QLabel(parentWidget)
        self.label_NotSelected.setText(label_text_NotSelected)
        self.verticalLayout_left_list.addWidget(self.label_NotSelected)
        self.listView_NotSelected = QListView(parentWidget)
        self.verticalLayout_left_list.addWidget(self.listView_NotSelected)
        self.gridLayout.addLayout(self.verticalLayout_left_list, 0, 0, 1, 2)

        self.verticalLayout_right_left = QVBoxLayout()
        spacerItem = QSpacerItem(20, 178, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.verticalLayout_right_left.addItem(spacerItem)
        self.pushButton_right_arrow = QPushButton(parentWidget)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalPolicy(0)
        sizePolicy.setHeightForWidth(
            self.pushButton_right_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_right_arrow.setSizePolicy(sizePolicy)
        self.pushButton_right_arrow.setMinimumSize(QSize(50, 50))
        self.pushButton_right_arrow.setMaximumSize(QSize(50, 50))
        self.pushButton_right_arrow.setIcon(QIcon(':/right_arrow.png'))
        self.pushButton_right_arrow.setIconSize(QSize(50, 50))
        self.pushButton_right_arrow.setText('')
        self.verticalLayout_right_left.addWidget(self.pushButton_right_arrow)
        self.pushButton_left_arrow = QPushButton(parentWidget)
        sizePolicy.setHeightForWidth(
            self.pushButton_left_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_left_arrow.setSizePolicy(sizePolicy)
        self.pushButton_left_arrow.setMinimumSize(QSize(50, 50))
        self.pushButton_left_arrow.setMaximumSize(QSize(50, 50))
        self.pushButton_left_arrow.setIcon(QIcon(':/left_arrow.png'))
        self.pushButton_left_arrow.setIconSize(QSize(50, 50))
        self.pushButton_left_arrow.setText('')
        self.verticalLayout_right_left.addWidget(self.pushButton_left_arrow)
        spacerItem = QSpacerItem(20, 178, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.verticalLayout_right_left.addItem(spacerItem)
        self.gridLayout.addLayout(self.verticalLayout_right_left, 0, 2, 1, 1)

        self.verticalLayout_right_list = QVBoxLayout()
        self.label_Selected = QLabel(parentWidget)
        self.label_Selected.setText(label_text_Selected)
        self.verticalLayout_right_list.addWidget(self.label_Selected)
        self.listView_Selected = QListView(parentWidget)
        self.verticalLayout_right_list.addWidget(self.listView_Selected)
        self.gridLayout.addLayout(self.verticalLayout_right_list, 0, 3, 1, 2)

        self.verticalLayout_up_down = QVBoxLayout()
        spacerItem = QSpacerItem(20, 178, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.verticalLayout_up_down.addItem(spacerItem)
        self.pushButton_up_arrow = QPushButton(parentWidget)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalPolicy(0)
        sizePolicy.setHeightForWidth(
            self.pushButton_up_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_up_arrow.setSizePolicy(sizePolicy)
        self.pushButton_up_arrow.setMinimumSize(QSize(50, 50))
        self.pushButton_up_arrow.setMaximumSize(QSize(50, 50))
        self.pushButton_up_arrow.setIcon(QIcon(':/up_arrow.png'))
        self.pushButton_up_arrow.setIconSize(QSize(50, 50))
        self.pushButton_up_arrow.setText('')
        self.verticalLayout_up_down.addWidget(self.pushButton_up_arrow)
        self.pushButton_down_arrow = QPushButton(parentWidget)
        sizePolicy.setHeightForWidth(
            self.pushButton_down_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_down_arrow.setSizePolicy(sizePolicy)
        self.pushButton_down_arrow.setMinimumSize(QSize(50, 50))
        self.pushButton_down_arrow.setMaximumSize(QSize(50, 50))
        self.pushButton_down_arrow.setIcon(QIcon(':/down_arrow.png'))
        self.pushButton_down_arrow.setIconSize(QSize(50, 50))
        self.pushButton_down_arrow.setText('')
        self.verticalLayout_up_down.addWidget(self.pushButton_down_arrow)
        spacerItem = QSpacerItem(20, 178, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.verticalLayout_up_down.addItem(spacerItem)
        self.gridLayout.addLayout(self.verticalLayout_up_down, 0, 5, 1, 1)

        self.listView_NotSelected.setSelectionMode(
            QAbstractItemView.ExtendedSelection)
        self.listView_Selected.setSelectionMode(
            QAbstractItemView.ExtendedSelection)

        self.listView_NotSelected.setModel(self.model.model_NotSelected)
        self.listView_Selected.setModel(self.model.model_Selected)

    #----------------------------------------------------------------------
    def on_right_arrow_press(self):
        """"""

        NSMod = self.listView_NotSelected.model()
        NSSelMod = self.listView_NotSelected.selectionModel()

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = NSSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            SSelMod.clearSelection()
        while mod_ind_list != []:
            mod_ind = mod_ind_list[0]

            item = NSMod.itemFromIndex(mod_ind)
            new_item = item.clone()
            SMod.appendRow(new_item)
            SSelMod.select(SMod.indexFromItem(new_item),
                           QItemSelectionModel.Select)
            NSMod.removeRow(mod_ind.row())

            mod_ind_list = NSSelMod.selectedRows(0)

    #----------------------------------------------------------------------
    def on_left_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        NSMod = self.listView_NotSelected.model()
        NSSelMod = self.listView_NotSelected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            NSSelMod.clearSelection()
        while mod_ind_list != []:
            mod_ind = mod_ind_list[0]

            item = SMod.itemFromIndex(mod_ind)
            text = item.text()
            if text in self.model.permanently_selected_string_list:
                print '"' + text + '"' + ' cannot be unselected.'
                return
            else:
                new_item = item.clone()
                NSMod.appendRow(new_item)
                NSSelMod.select(NSMod.indexFromItem(new_item),
                                QItemSelectionModel.Select)
                SMod.removeRow(mod_ind.row())

            mod_ind_list = SSelMod.selectedRows(0)

    #----------------------------------------------------------------------
    def on_up_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            sorted_row_ind_list = sorted(
                [mod_ind.row() for mod_ind in mod_ind_list])

        for row_ind in sorted_row_ind_list:
            try:
                upper_item = SMod.item(row_ind - 1).clone()
                lower_item = SMod.item(row_ind).clone()
                SMod.removeRow(row_ind - 1)
                SMod.insertRow(row_ind - 1, lower_item)
                SMod.removeRow(row_ind)
                SMod.insertRow(row_ind, upper_item)
                SSelMod.select(SMod.indexFromItem(lower_item),
                               QItemSelectionModel.Select)
            except:
                break

    #----------------------------------------------------------------------
    def on_down_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            sorted_row_ind_list = sorted(
                [mod_ind.row() for mod_ind in mod_ind_list], reverse=True)

        for row_ind in sorted_row_ind_list:
            try:
                upper_item = SMod.item(row_ind).clone()
                lower_item = SMod.item(row_ind + 1).clone()
                SMod.removeRow(row_ind)
                SMod.insertRow(row_ind, lower_item)
                SMod.removeRow(row_ind + 1)
                SMod.insertRow(row_ind + 1, upper_item)
                SSelMod.select(SMod.indexFromItem(upper_item),
                               QItemSelectionModel.Select)
            except:
                break
Example #7
0
class RunDialog(QDialog):
    def __init__(self, run_model, parent):
        QDialog.__init__(self, parent)
        self.setWindowFlags(self.windowFlags()
                            & ~Qt.WindowContextHelpButtonHint)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.setModal(True)
        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle("Simulations")

        assert isinstance(run_model, BaseRunModel)
        self._run_model = run_model

        layout = QVBoxLayout()
        layout.setSizeConstraint(QLayout.SetFixedSize)

        self.simulations_tracker = SimulationsTracker()
        states = self.simulations_tracker.getStates()

        self.total_progress = SimpleProgress()
        layout.addWidget(self.total_progress)

        status_layout = QHBoxLayout()
        status_layout.addStretch()
        self.__status_label = QLabel()
        status_layout.addWidget(self.__status_label)
        status_layout.addStretch()
        layout.addLayout(status_layout)

        self.progress = Progress()
        self.progress.setIndeterminateColor(self.total_progress.color)
        for state in states:
            self.progress.addState(state.state, QColor(*state.color),
                                   100.0 * state.count / state.total_count)

        layout.addWidget(self.progress)

        self.detailed_progress = None

        legend_layout = QHBoxLayout()
        self.legends = {}
        for state in states:
            self.legends[state] = Legend("%s (%d/%d)", QColor(*state.color))
            self.legends[state].updateLegend(state.name, 0, 0)
            legend_layout.addWidget(self.legends[state])

        layout.addLayout(legend_layout)

        self.running_time = QLabel("")

        ert = None
        if isinstance(run_model, BaseRunModel):
            ert = run_model.ert()

        self.plot_tool = PlotTool()
        self.plot_tool.setParent(None)
        self.plot_button = QPushButton(self.plot_tool.getName())
        self.plot_button.clicked.connect(self.plot_tool.trigger)
        self.plot_button.setEnabled(ert is not None)

        self.kill_button = QPushButton("Kill simulations")
        self.done_button = QPushButton("Done")
        self.done_button.setHidden(True)
        self.restart_button = QPushButton("Restart")
        self.restart_button.setHidden(True)
        self.show_details_button = QPushButton("Details")

        self.realizations_view = QListView()
        self.realizations_view.setModel(
            QStandardItemModel(self.realizations_view))
        self.realizations_view.setVisible(False)

        button_layout = QHBoxLayout()

        size = 20
        spin_movie = resourceMovie("ide/loading.gif")
        spin_movie.setSpeed(60)
        spin_movie.setScaledSize(QSize(size, size))
        spin_movie.start()

        self.processing_animation = QLabel()
        self.processing_animation.setMaximumSize(QSize(size, size))
        self.processing_animation.setMinimumSize(QSize(size, size))
        self.processing_animation.setMovie(spin_movie)

        button_layout.addWidget(self.processing_animation)
        button_layout.addWidget(self.running_time)
        button_layout.addStretch()
        button_layout.addWidget(self.show_details_button)
        button_layout.addWidget(self.plot_button)
        button_layout.addWidget(self.kill_button)
        button_layout.addWidget(self.done_button)
        button_layout.addWidget(self.restart_button)

        layout.addStretch()
        layout.addLayout(button_layout)

        layout.addWidget(self.realizations_view)

        self.setLayout(layout)

        self.kill_button.clicked.connect(self.killJobs)
        self.done_button.clicked.connect(self.accept)
        self.restart_button.clicked.connect(self.restart_failed_realizations)
        self.show_details_button.clicked.connect(self.show_detailed_progress)

        self.__updating = False
        self.__update_queued = False
        self.__simulation_started = False

        self.__update_timer = QTimer(self)
        self.__update_timer.setInterval(500)
        self.__update_timer.timeout.connect(self.updateRunStatus)
        self._simulations_argments = {}

    def startSimulation(self, arguments):

        self._simulations_argments = arguments

        if not 'prev_successful_realizations' in self._simulations_argments:
            self._simulations_argments['prev_successful_realizations'] = 0
        self._run_model.reset()

        def run():
            self._run_model.startSimulations(self._simulations_argments)

        simulation_thread = Thread(name="ert_gui_simulation_thread")
        simulation_thread.setDaemon(True)
        simulation_thread.run = run
        simulation_thread.start()

        self.__update_timer.start()

    def checkIfRunFinished(self):
        if self._run_model.isFinished():

            self.update_realizations_view()
            self.hideKillAndShowDone()

            if self._run_model.hasRunFailed():
                error = self._run_model.getFailMessage()
                QMessageBox.critical(
                    self, "Simulations failed!",
                    "The simulation failed with the following error:\n\n%s" %
                    error)
                self.reject()

    def updateRunStatus(self):
        self.checkIfRunFinished()

        self.total_progress.setProgress(self._run_model.getProgress())

        self.__status_label.setText(self._run_model.getPhaseName())

        states = self.simulations_tracker.getStates()

        if self._run_model.isIndeterminate():
            self.progress.setIndeterminate(True)

            for state in states:
                self.legends[state].updateLegend(state.name, 0, 0)

        else:
            if self.detailed_progress:
                self.detailed_progress.set_progress(
                    *self._run_model.getDetailedProgress())

            self.progress.setIndeterminate(False)
            total_count = self._run_model.getQueueSize()
            queue_status = self._run_model.getQueueStatus()

            for state in states:
                state.count = 0
                state.total_count = total_count

            for state in states:
                for queue_state in queue_status:
                    if queue_state in state.state:
                        state.count += queue_status[queue_state]

                self.progress.updateState(
                    state.state, 100.0 * state.count / state.total_count)
                self.legends[state].updateLegend(state.name, state.count,
                                                 state.total_count)

        self.setRunningTime()

    def setRunningTime(self):
        days = 0
        hours = 0
        minutes = 0
        seconds = self._run_model.getRunningTime()

        if seconds >= 60:
            minutes, seconds = divmod(seconds, 60)

        if minutes >= 60:
            hours, minutes = divmod(minutes, 60)

        if hours >= 24:
            days, hours = divmod(hours, 24)

        if days > 0:
            self.running_time.setText(
                "Running time: %d days %d hours %d minutes %d seconds" %
                (days, hours, minutes, seconds))
        elif hours > 0:
            self.running_time.setText(
                "Running time: %d hours %d minutes %d seconds" %
                (hours, minutes, seconds))
        elif minutes > 0:
            self.running_time.setText("Running time: %d minutes %d seconds" %
                                      (minutes, seconds))
        else:
            self.running_time.setText("Running time: %d seconds" % seconds)

    def killJobs(self):
        kill_job = QMessageBox.question(
            self, "Kill simulations?",
            "Are you sure you want to kill the currently running simulations?",
            QMessageBox.Yes | QMessageBox.No)

        if kill_job == QMessageBox.Yes:
            if self._run_model.killAllSimulations():
                self.reject()

    def hideKillAndShowDone(self):
        self.__update_timer.stop()
        self.processing_animation.hide()
        self.kill_button.setHidden(True)
        self.done_button.setHidden(False)
        self.realizations_view.setVisible(True)
        self.restart_button.setVisible(self.has_failed_realizations())
        self.restart_button.setEnabled(self._run_model.support_restart)

    def has_failed_realizations(self):
        completed = self._run_model.completed_realizations_mask
        initial = self._run_model.initial_realizations_mask
        for (index, successful) in enumerate(completed):
            if initial[index] and not successful:
                return True
        return False

    def count_successful_realizations(self):
        """
        Counts the realizations completed in the prevoius ensemble run
        :return:
        """
        completed = self._run_model.completed_realizations_mask
        return completed.count(True)

    def create_mask_from_failed_realizations(self):
        """
        Creates a BoolVector mask representing the failed realizations
        :return: Type BoolVector
        """
        completed = self._run_model.completed_realizations_mask
        initial = self._run_model.initial_realizations_mask
        inverted_mask = BoolVector(default_value=False)
        for (index, successful) in enumerate(completed):
            inverted_mask[index] = initial[index] and not successful
        return inverted_mask

    def restart_failed_realizations(self):

        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Information)
        msg.setText(
            "Note that workflows will only be executed on the restarted realizations and that this might have unexpected consequences."
        )
        msg.setWindowTitle("Restart Failed Realizations")
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        result = msg.exec_()

        if result == QMessageBox.Ok:
            active_realizations = self.create_mask_from_failed_realizations()
            self._simulations_argments[
                'active_realizations'] = active_realizations
            self._simulations_argments[
                'prev_successful_realizations'] += self.count_successful_realizations(
                )
            self.startSimulation(self._simulations_argments)

    def update_realizations_view(self):
        completed = self._run_model.completed_realizations_mask
        for (index, successful) in enumerate(completed):
            items = self.realizations_view.model().findItems(str(index))
            if not items:
                item = QStandardItem(str(index))
                self.realizations_view.model().appendRow(item)
            else:
                item = items[0]
            item.setCheckState(successful or item.checkState())

    def show_detailed_progress(self):
        if not self.detailed_progress:
            self.detailed_progress = DetailedProgressDialog(
                self, self.simulations_tracker.getStates())
        self.detailed_progress.set_progress(
            *self._run_model.getDetailedProgress())
        self.detailed_progress.show()
class OWGenotypeDistances(widget.OWWidget):
    name = "Expression Profile Distances"
    description = ("Compute distances between expression profiles of "
                   "different experimental factors.")
    icon = "../widgets/icons/GenotypeDistances.svg"
    priority = 1050

    inputs = [("Data", Orange.data.Table, "set_data")]
    outputs = [("Distances", Orange.misc.DistMatrix),
               ("Sorted Data", Orange.data.Table)]

    settingsHandler = SetContextHandler()

    separate_keys = settings.ContextSetting({})
    relevant_keys = settings.ContextSetting({})

    distance_measure = settings.Setting(0)
    auto_commit = settings.Setting(False)

    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation", dist_pcorr),
                          ("Euclidean distance", dist_eucl),
                          ("Distance from Spearman correlation", dist_spearman)
                          ]

    def __init__(self, parent=None):
        super().__init__(self, parent)

        self.data = None
        self.partitions = []
        self.matrix = None
        self.split_groups = []
        self._disable_updates = False

        ########
        # GUI
        ########

        box = gui.widgetBox(self.controlArea, "Input")

        self.info_box = gui.widgetLabel(box, "No data on input\n")

        box = gui.widgetBox(self.controlArea, "Separate By", addSpace=True)

        self.separate_view = QListView(selectionMode=QListView.MultiSelection)
        box.layout().addWidget(self.separate_view)

        box = gui.widgetBox(self.controlArea, "Sort By", addSpace=True)
        self.relevant_view = QListView(selectionMode=QListView.MultiSelection)

        box.layout().addWidget(self.relevant_view)

        self.distance_view = gui.comboBox(
            self.controlArea,
            self,
            "distance_measure",
            box="Distance Measure",
            items=[name for name, _ in self.DISTANCE_FUNCTIONS])

        gui.rubber(self.controlArea)

        gui.auto_commit(self.controlArea, self, "auto_commit", "Commit")
        self.groups_box = gui.widgetBox(self.mainArea, "Groups")
        self.groups_scroll_area = QScrollArea()
        self.groups_box.layout().addWidget(self.groups_scroll_area)

    def sizeHint(self):
        return QSize(800, 600)

    def clear(self):
        self.data = None
        self.partitions = []
        self.split_groups = []
        self.matrix = None

    def get_suitable_keys(self, data):
        """Return suitable attr label keys from the data where the key has at least
        two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def set_data(self, data=None):
        """Set the input data table.
        """
        self.closeContext()
        self.clear()
        self.error(0)
        self.warning(0)
        if data and not self.get_suitable_keys(data):
            self.error(0, "Data has no suitable column labels.")
            data = None

        self.data = data

        if data:
            self.info_box.setText("{0} genes\n{1} experiments".format(
                len(data), len(data.domain)))
            self.update_control()
            self.split_data()
        else:
            self.separate_view.setModel(itemmodels.PyListModel([]))
            self.relevant_view.setModel(itemmodels.PyListModel([]))
            self.groups_scroll_area.setWidget(QWidget())
            self.info_box.setText("No data on input.\n")
        self.commit()

    def update_control(self):
        """Update the control area of the widget. Populate the list
        views with keys from attribute labels.
        """
        keys = self.get_suitable_keys(self.data)

        model = itemmodels.PyListModel(keys)
        self.separate_view.setModel(model)
        self.separate_view.selectionModel().selectionChanged.connect(
            self.on_separate_key_changed)

        model = itemmodels.PyListModel(keys)
        self.relevant_view.setModel(model)
        self.relevant_view.selectionModel().selectionChanged.connect(
            self.on_relevant_key_changed)

        self.openContext(keys)

        # Get the selected keys from the open context
        separate_keys = self.separate_keys
        relevant_keys = self.relevant_keys

        def select(model, selection_model, selected_items):
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            selection = QItemSelection()
            for ind in indices:
                index = model.index(ind)
                selection.select(index, index)

            selection_model.select(selection, QItemSelectionModel.Select)

        self._disable_updates = True
        try:
            select(self.relevant_view.model(),
                   self.relevant_view.selectionModel(), relevant_keys)

            select(self.separate_view.model(),
                   self.separate_view.selectionModel(), separate_keys)
        finally:
            self._disable_updates = False

    def on_separate_key_changed(self, *args):
        if not self._disable_updates:
            self.separate_keys = self.selected_separeate_by_keys()
            self.split_data()

    def on_relevant_key_changed(self, *args):
        if not self._disable_updates:
            self.relevant_keys = self.selected_relevant_keys()
            self.split_data()

    def selected_separeate_by_keys(self):
        """Return the currently selected separate by keys
        """
        rows = self.separate_view.selectionModel().selectedRows()
        rows = sorted([idx.row() for idx in rows])
        keys = [self.separate_view.model()[row] for row in rows]
        return keys

    def selected_relevant_keys(self):
        """Return the currently selected relevant keys
        """
        rows = self.relevant_view.selectionModel().selectedRows()
        rows = sorted([idx.row() for idx in rows])
        keys = [self.relevant_view.model()[row] for row in rows]
        return keys

    def split_data(self):
        """Split the data and update the Groups widget
        """
        separate_keys = self.selected_separeate_by_keys()
        relevant_keys = self.selected_relevant_keys()

        self.warning(0)
        if not separate_keys:
            self.warning(0, "No separate by column selected.")

        partitions, uniquepos = separate_by(self.data,
                                            separate_keys,
                                            consider=relevant_keys)
        partitions = partitions.items()

        all_values = defaultdict(set)
        for a in [at.attributes for at in self.data.domain.attributes]:
            for k, v in a.items():
                all_values[k].add(v)

        # sort groups
        pkeys = [key for key, _ in partitions]
        types = [
            data_type([a[i] for a in pkeys]) for i in range(len(pkeys[0]))
        ]

        partitions = sorted(partitions,
                            key=lambda x: tuple(types[i](v)
                                                for i, v in enumerate(x[0])))

        split_groups = []

        # Collect relevant key value pairs for all columns
        relevant_items = None

        for keys, indices in partitions:
            if relevant_items == None:
                relevant_items = [defaultdict(set) for _ in indices]
            for i, ind in enumerate(indices):
                if ind is not None:
                    attr = self.data.domain[ind]
                    for key in relevant_keys:
                        relevant_items[i][key].add(attr.attributes[key])

        #those with different values between rows are not relevant
        for d in relevant_items:
            for k, s in list(d.items()):
                if len(s) > 1:
                    del d[k]
                else:
                    d[k] = s.pop()

        def get_attr(attr_index, i):
            if attr_index is None:
                attr = Orange.data.ContinuousVariable(
                    next(missing_name_gen), compute_value=lambda x: None)
                attr.attributes.update(relevant_items[i])
                return attr
            else:
                return self.data.domain[attr_index]

        for keys, indices in partitions:
            attrs = [
                get_attr(attr_index, i) for i, attr_index in enumerate(indices)
            ]
            for attr in attrs:
                attr.attributes.update(zip(separate_keys, keys))
            domain = Orange.data.Domain(attrs, [], self.data.domain.metas)
            split_groups.append((keys, domain))

        self.set_groups(separate_keys, split_groups, relevant_keys,
                        relevant_items, all_values, uniquepos)

        self.partitions = partitions
        self.split_groups = split_groups

        self.commit()

    def set_groups(self, keys, groups, relevant_keys, relevant_items,
                   all_values, uniquepos):
        """Set the current data groups and update the Group widget
        """
        layout = QVBoxLayout()
        header_widths = []
        header_views = []
        palette = self.palette()
        all_values = all_values.keys()

        def for_print(rd):
            attrs = []
            for d in rd:
                attr = Orange.data.ContinuousVariable(next(inactive_name_gen))
                attr.attributes.update(d)
                attrs.append(attr)
            return Orange.data.Domain(attrs, None)

        for separatev, domain in [(None, for_print(relevant_items))] + groups:
            label = None
            if separatev is not None:
                ann_vals = " <b>|</b> ".join(["<b>{0}</ b> = {1}".format(key,val) \
                     for key, val in zip(keys, separatev)])
                label = QLabel(ann_vals)

            model = QStandardItemModel()
            for i, attr in enumerate(domain.attributes):
                item = QStandardItem()
                if separatev is not None:
                    isunique = uniquepos[separatev][i]
                else:
                    isunique = all(a[i] for a in uniquepos.values())

                if str(attr.name).startswith(
                        "!!missing "
                ):  # TODO: Change this to not depend on name
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key not in relevant_items[i]]
                    header_text = "\n".join(
                        header_text) if header_text else "Empty"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setFlags(Qt.NoItemFlags)
                    item.setData(QColor(Qt.red), Qt.ForegroundRole)
                    item.setData(
                        palette.color(QPalette.Disabled, QPalette.Window),
                        Qt.BackgroundRole)
                    item.setData("Missing feature.", Qt.ToolTipRole)
                elif str(attr.name).startswith("!!inactive "):
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key in relevant_items[i]]
                    header_text = "\n".join(
                        header_text) if header_text else "No descriptor"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setData(
                        palette.color(QPalette.Disabled, QPalette.Window),
                        Qt.BackgroundRole)
                else:
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key not in relevant_items[i]]
                    header_text = "\n".join(
                        header_text) if header_text else "Empty"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setData(attr.name, Qt.ToolTipRole)

                if not isunique:
                    item.setData(QColor(Qt.red), Qt.ForegroundRole)

                model.setHorizontalHeaderItem(i, item)
            attr_count = len(domain.attributes)
            view = MyHeaderView(Qt.Horizontal)
            view.setResizeMode(QHeaderView.Fixed)
            view.setModel(model)
            hint = view.sizeHint()
            view.setMaximumHeight(hint.height())

            widths = [view.sectionSizeHint(i) for i in range(attr_count)]
            header_widths.append(widths)
            header_views.append(view)

            if label:
                layout.addWidget(label)
            layout.addWidget(view)
            layout.addSpacing(8)

        # Make all header sections the same width
        width_sum = 0
        max_header_count = max([h.count() for h in header_views])
        for i in range(max_header_count):
            max_width = max([w[i] for w in header_widths if i < len(w)] or [0])
            for view in header_views:
                if i < view.count():
                    view.resizeSection(i, max_width)
            width_sum += max_width + 2

        for h in header_views:
            h.setMinimumWidth(h.length() + 4)

        widget = QWidget()
        widget.setLayout(layout)
        widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
        layout.activate()

        max_width = max(h.length() for h in header_views) + 20

        left, _, right, _ = self.getContentsMargins()
        widget.setMinimumWidth(width_sum)
        widget.setMinimumWidth(max_width + left + right)
        self.groups_scroll_area.setWidget(widget)

    def compute_distances(self, separate_keys, partitions, data):
        """Compute the distances between genotypes.
        """
        if separate_keys and partitions:
            self.progressBarInit()
            #             matrix = Orange.misc.DistMatrix(len(partitions))
            matrix = numpy.zeros((len(partitions), len(partitions)))

            profiles = [linearize(data, indices) for _, indices in partitions]
            dist_func = self.DISTANCE_FUNCTIONS[self.distance_measure][1]
            #             from Orange.utils import progress_bar_milestones
            count = (len(profiles) * len(profiles) - 1) / 2
            #             milestones = progress_bar_milestones(count)
            iter_count = 0
            for i in range(len(profiles)):
                for j in range(i + 1, len(profiles)):
                    matrix[i, j] = dist_func(profiles[i], profiles[j])
                    matrix[j, i] = matrix[i, j]
                    iter_count += 1
                    #                     if iter_count in milestones:
                    self.progressBarSet(100.0 * iter_count / count)
            self.progressBarFinished()

            items = [[
                "{0}={1}".format(key, value)
                for key, value in zip(separate_keys, values)
            ] for values, _ in partitions]
            items = [" | ".join(item) for item in items]
            #             matrix.setattr("items", items)
            matrix = Orange.misc.DistMatrix(matrix)
        else:
            matrix = None

        self.matrix = matrix

    def commit(self):
        separate_keys = self.selected_separeate_by_keys()
        self.compute_distances(separate_keys, self.partitions, self.data)
        if self.split_groups:
            all_attrs = []
            for group, domain in self.split_groups:
                attrs = []
                group_name = " | ".join("{0}={1}".format(*item)
                                        for item in zip(separate_keys, group))
                for attr in domain.attributes:
                    newattr = clone_attr(attr)
                    newattr.attributes[
                        "<GENOTYPE GROUP>"] = group_name  # Need a better way to pass the groups to downstream widgets.
                    attrs.append(newattr)

                all_attrs.extend(attrs)

            domain = Orange.data.Domain(all_attrs, self.data.domain.class_vars,
                                        self.data.domain.metas)
            data = Orange.data.Table(domain, self.data)
        else:
            data = None
        self.send("Sorted Data", data)
        self.send("Distances", self.matrix)
Example #9
0
class OWQualityControl(widget.OWWidget):
    name = "Quality Control"
    description = "Experiment quality control"
    icon = "../widgets/icons/QualityControl.svg"
    priority = 5000

    inputs = [("Experiment Data", Orange.data.Table, "set_data")]
    outputs = []

    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation",
                           dist_pcorr),
                          ("Euclidean distance",
                           dist_eucl),
                          ("Distance from Spearman correlation",
                           dist_spearman)]

    settingsHandler = SetContextHandler()

    split_by_labels = settings.ContextSetting({})
    sort_by_labels = settings.ContextSetting({})

    selected_distance_index = settings.Setting(0)

    def __init__(self, parent=None):
        super().__init__(parent)

        ## Attributes
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None
        self.base_group_index = 0

        ## GUI
        box = gui.widgetBox(self.controlArea, "Info")
        self.info_box = gui.widgetLabel(box, "\n")

        ## Separate By box
        box = gui.widgetBox(self.controlArea, "Separate By")
        self.split_by_model = itemmodels.PyListModel(parent=self)
        self.split_by_view = QListView()
        self.split_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.split_by_view.setModel(self.split_by_model)
        box.layout().addWidget(self.split_by_view)

        self.split_by_view.selectionModel().selectionChanged.connect(
            self.on_split_key_changed)

        ## Sort By box
        box = gui.widgetBox(self.controlArea, "Sort By")
        self.sort_by_model = itemmodels.PyListModel(parent=self)
        self.sort_by_view = QListView()
        self.sort_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.sort_by_view.setModel(self.sort_by_model)
        box.layout().addWidget(self.sort_by_view)

        self.sort_by_view.selectionModel().selectionChanged.connect(
            self.on_sort_key_changed)

        ## Distance box
        box = gui.widgetBox(self.controlArea, "Distance Measure")
        gui.comboBox(box, self, "selected_distance_index",
                     items=[name for name, _ in self.DISTANCE_FUNCTIONS],
                     callback=self.on_distance_measure_changed)

        self.scene = QGraphicsScene()
        self.scene_view = QGraphicsView(self.scene)
        self.scene_view.setRenderHints(QPainter.Antialiasing)
        self.scene_view.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.mainArea.layout().addWidget(self.scene_view)

        self.scene_view.installEventFilter(self)

        self._disable_updates = False
        self._cached_distances = {}
        self._base_index_hints = {}
        self.main_widget = None

        self.resize(800, 600)

    def clear(self):
        """Clear the widget state."""
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None

        with disable_updates(self):
            self.split_by_model[:] = []
            self.sort_by_model[:] = []

        self.main_widget = None
        self.scene.clear()
        self.info_box.setText("\n")
        self._cached_distances = {}

    def set_data(self, data=None):
        """Set input experiment data."""
        self.closeContext()
        self.clear()

        self.error(0)
        self.warning(0)

        if data is not None:
            keys = self.get_suitable_keys(data)
            if not keys:
                self.error(0, "Data has no suitable feature labels.")
                data = None

        self.data = data
        if data is not None:
            self.on_new_data()

    def update_label_candidates(self):
        """Update the label candidates selection GUI 
        (Group/Sort By views).

        """
        keys = self.get_suitable_keys(self.data)
        with disable_updates(self):
            self.split_by_model[:] = keys
            self.sort_by_model[:] = keys

    def get_suitable_keys(self, data):
        """ Return suitable attr label keys from the data where
        the key has at least two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def selected_split_by_labels(self):
        """Return the current selected split labels.
        """
        sel_m = self.split_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_sort_by_labels(self):
        """Return the current selected sort labels
        """
        sel_m = self.sort_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_distance(self):
        """Return the selected distance function.
        """
        return self.DISTANCE_FUNCTIONS[self.selected_distance_index][1]

    def selected_base_group_index(self):
        """Return the selected base group index
        """
        return self.base_group_index

    def selected_base_indices(self, base_group_index=None):
        indices = []
        for g, ind in self.groups:
            if base_group_index is None:
                label = group_label(self.selected_split_by_labels(), g)
                ind = [i for i in ind if i is not None]
                i = self._base_index_hints.get(label, ind[0] if ind else None)
            else:
                i = ind[base_group_index]
            indices.append(i)
        return indices

    def on_new_data(self):
        """We have new data and need to recompute all.
        """
        self.closeContext()

        self.update_label_candidates()
        self.info_box.setText(
            "%s genes \n%s experiments" %
            (len(self.data),  len(self.data.domain.attributes))
        )

        self.base_group_index = 0

        keys = self.get_suitable_keys(self.data)
        self.openContext(keys)

        ## Restore saved context settings (split/sort selection)
        split_by_labels = self.split_by_labels
        sort_by_labels = self.sort_by_labels

        def select(model, selection_model, selected_items):
            """Select items in a Qt item model view
            """
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            for ind in indices:
                selection_model.select(model.index(ind),
                                       QItemSelectionModel.Select)

        with disable_updates(self):
            select(self.split_by_view.model(),
                   self.split_by_view.selectionModel(),
                   split_by_labels)

            select(self.sort_by_view.model(),
                   self.sort_by_view.selectionModel(),
                   sort_by_labels)

        with widget_disable(self):
            self.split_and_update()

    def on_split_key_changed(self, *args):
        """Split key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.split_by_labels = self.selected_split_by_labels()
                self.split_and_update()

    def on_sort_key_changed(self, *args):
        """Sort key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.sort_by_labels = self.selected_sort_by_labels()
                self.split_and_update()

    def on_distance_measure_changed(self):
        """Distance measure has changed
        """
        if self.data is not None:
            with widget_disable(self):
                self.update_distances()
                self.replot_experiments()

    def on_view_resize(self, size):
        """The view with the quality plot has changed
        """
        if self.main_widget:
            current = self.main_widget.size()
            self.main_widget.resize(size.width() - 6,
                                    current.height())

            self.scene.setSceneRect(self.scene.itemsBoundingRect())

    def on_rug_item_clicked(self, item):
        """An ``item`` in the quality plot has been clicked.
        """
        update = False
        sort_by_labels = self.selected_sort_by_labels()
        if sort_by_labels and item.in_group:
            ## The item is part of the group
            if item.group_index != self.base_group_index:
                self.base_group_index = item.group_index
                update = True

        else:
            if sort_by_labels:
                # If the user clicked on an background item it
                # invalidates the sorted labels selection
                with disable_updates(self):
                    self.sort_by_view.selectionModel().clear()
                    update = True

            index = item.index
            group = item.group
            label = group_label(self.selected_split_by_labels(), group)

            if self._base_index_hints.get(label, 0) != index:
                self._base_index_hints[label] = index
                update = True

        if update:
            with widget_disable(self):
                self.split_and_update()

    def eventFilter(self, obj, event):
        if obj is self.scene_view and event.type() == QEvent.Resize:
            self.on_view_resize(event.size())
        return super().eventFilter(obj, event)

    def split_and_update(self):
        """
        Split the data based on the selected sort/split labels
        and update the quality plot.

        """
        split_labels = self.selected_split_by_labels()
        sort_labels = self.selected_sort_by_labels()

        self.warning(0)
        if not split_labels:
            self.warning(0, "No separate by label selected.")

        self.groups, self.unique_pos = \
                exp.separate_by(self.data, split_labels,
                                consider=sort_labels,
                                add_empty=True)

        self.groups = sorted(self.groups.items(),
                             key=lambda t: list(map(float_if_posible, t[0])))
        self.unique_pos = sorted(self.unique_pos.items(),
                                 key=lambda t: list(map(float_if_posible, t[0])))

        if self.groups:
            if sort_labels:
                group_base = self.selected_base_group_index()
                base_indices = self.selected_base_indices(group_base)
            else:
                base_indices = self.selected_base_indices()
            self.update_distances(base_indices)
            self.replot_experiments()

    def get_cached_distances(self, measure):
        if measure not in self._cached_distances:
            attrs = self.data.domain.attributes
            mat = numpy.zeros((len(attrs), len(attrs)))

            self._cached_distances[measure] = \
                (mat, set(zip(range(len(attrs)), range(len(attrs)))))

        return self._cached_distances[measure]

    def get_cached_distance(self, measure, i, j):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        if key in computed:
            return matrix[i, j]
        else:
            return None

    def get_distance(self, measure, i, j):
        d = self.get_cached_distance(measure, i, j)
        if d is None:
            vec_i = take_columns(self.data, [i])
            vec_j = take_columns(self.data, [j])
            d = measure(vec_i, vec_j)

            mat, computed = self.get_cached_distances(measure)
            mat[i, j] = d
            key = key = (i, j) if i < j else (j, i)
            computed.add(key)
        return d

    def store_distance(self, measure, i, j, dist):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        matrix[j, i] = matrix[i, j] = dist
        computed.add(key)

    def update_distances(self, base_indices=()):
        """Recompute the experiment distances.
        """
        distance = self.selected_distance()
        if base_indices == ():
            base_group_index = self.selected_base_group_index()
            base_indices = [ind[base_group_index] \
                            for _, ind in self.groups]

        assert(len(base_indices) == len(self.groups))

        base_distances = []
        attributes = self.data.domain.attributes
        pb = gui.ProgressBar(self, len(self.groups) * len(attributes))

        for (group, indices), base_index in zip(self.groups, base_indices):
            # Base column of the group
            if base_index is not None:
                base_vec = take_columns(self.data, [base_index])
                distances = []
                # Compute the distances between base column
                # and all the rest data columns.
                for i in range(len(attributes)):
                    if i == base_index:
                        distances.append(0.0)
                    elif self.get_cached_distance(distance, i, base_index) is not None:
                        distances.append(self.get_cached_distance(distance, i, base_index))
                    else:
                        vec_i = take_columns(self.data, [i])
                        dist = distance(base_vec, vec_i)
                        self.store_distance(distance, i, base_index, dist)
                        distances.append(dist)
                    pb.advance()

                base_distances.append(distances)
            else:
                base_distances.append(None)

        pb.finish()
        self.distances = base_distances

    def replot_experiments(self):
        """Replot the whole quality plot.
        """
        self.scene.clear()
        labels = []

        max_dist = numpy.nanmax(list(filter(None, self.distances)))
        rug_widgets = []

        group_pen = QPen(Qt.black)
        group_pen.setWidth(2)
        group_pen.setCapStyle(Qt.RoundCap)
        background_pen = QPen(QColor(0, 0, 250, 150))
        background_pen.setWidth(1)
        background_pen.setCapStyle(Qt.RoundCap)

        main_widget = QGraphicsWidget()
        layout = QGraphicsGridLayout()
        attributes = self.data.domain.attributes
        if self.data is not None:
            for (group, indices), dist_vec in zip(self.groups, self.distances):
                indices_set = set(indices)
                rug_items = []
                if dist_vec is not None:
                    for i, attr in enumerate(attributes):
                        # Is this a within group distance or background
                        in_group = i in indices_set
                        if in_group:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           1.0, self.on_rug_item_clicked)
                            rug_item.setPen(group_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)
                            rug_item.group_index = indices.index(i)
                            rug_item.setZValue(rug_item.zValue() + 1)
                        else:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           0.85, self.on_rug_item_clicked)
                            rug_item.setPen(background_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)

                        rug_item.group = group
                        rug_item.index = i
                        rug_item.in_group = in_group

                        rug_items.append(rug_item)

                rug_widget = RugGraphicsWidget(parent=main_widget)
                rug_widget.set_rug(rug_items)

                rug_widgets.append(rug_widget)

                label = group_label(self.selected_split_by_labels(), group)
                label_item = QGraphicsSimpleTextItem(label, main_widget)
                label_item = GraphicsSimpleTextLayoutItem(label_item, parent=layout)
                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                labels.append(label_item)

        for i, (label, rug_w) in enumerate(zip(labels, rug_widgets)):
            layout.addItem(label, i, 0, Qt.AlignVCenter)
            layout.addItem(rug_w, i, 1)
            layout.setRowMaximumHeight(i, 30)

        main_widget.setLayout(layout)
        self.scene.addItem(main_widget)
        self.main_widget = main_widget
        self.rug_widgets = rug_widgets
        self.labels = labels
        self.on_view_resize(self.scene_view.size())
Example #10
0
class OWGenotypeDistances(widget.OWWidget):
    name = "Expression Profile Distances"
    description = ("Compute distances between expression profiles of "
                   "different experimental factors.")
    icon = "../widgets/icons/GenotypeDistances.svg"
    priority = 1050

    inputs = [("Data", Orange.data.Table, "set_data")]
    outputs = [("Distances", Orange.misc.DistMatrix),
               ("Sorted Data", Orange.data.Table)]

    settingsHandler = SetContextHandler()

    separate_keys = settings.ContextSetting({})
    relevant_keys = settings.ContextSetting({})

    distance_measure = settings.Setting(0)
    auto_commit = settings.Setting(False)

    DISTANCE_FUNCTIONS = [
        ("Distance from Pearson correlation", dist_pcorr),
        ("Euclidean distance", dist_eucl),
        ("Distance from Spearman correlation", dist_spearman)
    ]

    def __init__(self, parent=None):
        super().__init__(self, parent)

        self.data = None
        self.partitions = []
        self.matrix = None
        self.split_groups = []
        self._disable_updates = False

        ########
        # GUI
        ########

        box = gui.widgetBox(self.controlArea, "Input")

        self.info_box = gui.widgetLabel(box, "No data on input\n")

        box = gui.widgetBox(self.controlArea, "Separate By",
                            addSpace=True)

        self.separate_view = QListView(
            selectionMode=QListView.MultiSelection
        )
        box.layout().addWidget(self.separate_view)

        box = gui.widgetBox(self.controlArea, "Sort By",
                            addSpace=True)
        self.relevant_view = QListView(
            selectionMode=QListView.MultiSelection)

        box.layout().addWidget(self.relevant_view)

        self.distance_view = gui.comboBox(
            self.controlArea, self, "distance_measure",
            box="Distance Measure",
            items=[name for name, _ in self.DISTANCE_FUNCTIONS])

        gui.rubber(self.controlArea)

        gui.auto_commit(self.controlArea, self, "auto_commit", "Commit")
        self.groups_box = gui.widgetBox(self.mainArea, "Groups")
        self.groups_scroll_area = QScrollArea()
        self.groups_box.layout().addWidget(self.groups_scroll_area)

    def sizeHint(self):
        return QSize(800, 600)

    def clear(self):
        self.data = None
        self.partitions = []
        self.split_groups = []
        self.matrix = None

    def get_suitable_keys(self, data):
        """Return suitable attr label keys from the data where the key has at least
        two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def set_data(self, data=None):
        """Set the input data table.
        """
        self.closeContext()
        self.clear()
        self.error(0)
        self.warning(0)
        if data and not self.get_suitable_keys(data):
            self.error(0, "Data has no suitable attribute labels.")
            data = None

        self.data = data

        if data:
            self.info_box.setText("{0} genes\n{1} experiments"
                                  .format(len(data), len(data.domain)))
            self.update_control()
            self.split_data()
        else:
            self.separate_view.setModel(itemmodels.PyListModel([]))
            self.relevant_view.setModel(itemmodels.PyListModel([]))
            self.groups_scroll_area.setWidget(QWidget())
            self.info_box.setText("No data on input.\n")
        self.commit()

    def update_control(self):
        """Update the control area of the widget. Populate the list
        views with keys from attribute labels.
        """
        keys = self.get_suitable_keys(self.data)

        model = itemmodels.PyListModel(keys)
        self.separate_view.setModel(model)
        self.separate_view.selectionModel().selectionChanged.connect(
            self.on_separate_key_changed)

        model = itemmodels.PyListModel(keys)
        self.relevant_view.setModel(model)
        self.relevant_view.selectionModel().selectionChanged.connect(
            self.on_relevant_key_changed)

        self.openContext(keys)

        # Get the selected keys from the open context
        separate_keys = self.separate_keys
        relevant_keys = self.relevant_keys

        def select(model, selection_model, selected_items):
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            selection = QItemSelection()
            for ind in indices:
                index = model.index(ind)
                selection.select(index, index)

            selection_model.select(selection, QItemSelectionModel.Select)

        self._disable_updates = True
        try:
            select(self.relevant_view.model(),
                   self.relevant_view.selectionModel(),
                   relevant_keys)

            select(self.separate_view.model(),
                   self.separate_view.selectionModel(),
                   separate_keys)
        finally:
            self._disable_updates = False

    def on_separate_key_changed(self, *args):
        if not self._disable_updates:
            self.separate_keys = self.selected_separeate_by_keys()
            self.split_data()

    def on_relevant_key_changed(self, *args):
        if not self._disable_updates:
            self.relevant_keys = self.selected_relevant_keys()
            self.split_data()

    def selected_separeate_by_keys(self):
        """Return the currently selected separate by keys
        """
        rows = self.separate_view.selectionModel().selectedRows()
        rows = sorted([idx.row() for idx in rows])
        keys = [self.separate_view.model()[row] for row in rows]
        return keys

    def selected_relevant_keys(self):
        """Return the currently selected relevant keys
        """
        rows = self.relevant_view.selectionModel().selectedRows()
        rows = sorted([idx.row() for idx in rows])
        keys = [self.relevant_view.model()[row] for row in rows]
        return keys

    def split_data(self):
        """Split the data and update the Groups widget
        """
        separate_keys = self.selected_separeate_by_keys()
        relevant_keys = self.selected_relevant_keys()

        self.warning(0)
        if not separate_keys:
            self.warning(0, "No separate by attribute selected.")

        partitions, uniquepos = separate_by(
            self.data, separate_keys, consider=relevant_keys)
        partitions = partitions.items()

        all_values = defaultdict(set)
        for a in [at.attributes for at in self.data.domain.attributes]:
            for k, v in a.items():
                all_values[k].add(v)

        # sort groups
        pkeys = [key for key, _ in partitions]
        types = [data_type([a[i] for a in pkeys])
                 for i in range(len(pkeys[0]))]

        partitions = sorted(partitions, key=lambda x:
                    tuple(types[i](v) for i,v in enumerate(x[0])))

        split_groups = []

        # Collect relevant key value pairs for all columns
        relevant_items = None

        for keys, indices in partitions:
            if relevant_items == None:
                relevant_items = [defaultdict(set) for _ in indices]
            for i, ind in enumerate(indices):
                if ind is not None:
                    attr = self.data.domain[ind]
                    for key in relevant_keys:
                        relevant_items[i][key].add(attr.attributes[key])

        #those with different values between rows are not relevant
        for d in relevant_items:
            for k, s in list(d.items()):
                if len(s) > 1:
                    del d[k]
                else:
                    d[k] = s.pop()

        def get_attr(attr_index, i):
            if attr_index is None:
                attr = Orange.data.ContinuousVariable(next(missing_name_gen), 
                    compute_value=lambda x: None)
                attr.attributes.update(relevant_items[i])
                return attr
            else:
                return self.data.domain[attr_index]

        for keys, indices in partitions:
            attrs = [get_attr(attr_index, i)
                     for i, attr_index in enumerate(indices)]
            for attr in attrs:
                attr.attributes.update(zip(separate_keys, keys))
            domain = Orange.data.Domain(attrs, [], self.data.domain.metas)
            split_groups.append((keys, domain))

        self.set_groups(separate_keys, split_groups, relevant_keys,
                        relevant_items, all_values, uniquepos)

        self.partitions = partitions
        self.split_groups = split_groups

        self.commit()

    def set_groups(self, keys, groups, relevant_keys, relevant_items,
                   all_values, uniquepos):
        """Set the current data groups and update the Group widget
        """
        layout = QVBoxLayout()
        header_widths = []
        header_views = []
        palette = self.palette()
        all_values = all_values.keys()

        def for_print(rd):
            attrs = []
            for d in rd:
                attr = Orange.data.ContinuousVariable(next(inactive_name_gen))
                attr.attributes.update(d)
                attrs.append(attr)
            return Orange.data.Domain(attrs, None)

        for separatev, domain in [(None, for_print(relevant_items))] + groups:
            label = None
            if separatev is not None:
                ann_vals = " <b>|</b> ".join(["<b>{0}</ b> = {1}".format(key,val) \
                     for key, val in zip(keys, separatev)])
                label = QLabel(ann_vals)

            model = QStandardItemModel()
            for i, attr in enumerate(domain.attributes):
                item = QStandardItem()
                if separatev is not None:
                    isunique = uniquepos[separatev][i]
                else:
                    isunique = all(a[i] for a in uniquepos.values())

                if str(attr.name).startswith("!!missing "):  # TODO: Change this to not depend on name
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key not in relevant_items[i]]
                    header_text = "\n".join(header_text) if header_text else "Empty"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setFlags(Qt.NoItemFlags)
                    item.setData(QColor(Qt.red), Qt.ForegroundRole)
                    item.setData(palette.color(QPalette.Disabled, QPalette.Window), Qt.BackgroundRole)
                    item.setData("Missing feature.", Qt.ToolTipRole)
                elif str(attr.name).startswith("!!inactive "):
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key in relevant_items[i]]
                    header_text = "\n".join(header_text) if header_text else "No descriptor"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setData(palette.color(QPalette.Disabled, QPalette.Window), Qt.BackgroundRole)
                else:
                    header_text = ["{0}={1}".format(key, attr.attributes.get(key, "?")) \
                                   for key in all_values if key not in relevant_items[i]]
                    header_text = "\n".join(header_text) if header_text else "Empty"
                    item.setData(header_text, Qt.DisplayRole)
                    item.setData(attr.name, Qt.ToolTipRole)

                if not isunique:
                    item.setData(QColor(Qt.red), Qt.ForegroundRole)

                model.setHorizontalHeaderItem(i, item)
            attr_count = len(domain.attributes)
            view = MyHeaderView(Qt.Horizontal)
            view.setResizeMode(QHeaderView.Fixed)
            view.setModel(model)
            hint = view.sizeHint()
            view.setMaximumHeight(hint.height())

            widths = [view.sectionSizeHint(i) for i in range(attr_count)]
            header_widths.append(widths)
            header_views.append(view)

            if label:
                layout.addWidget(label)
            layout.addWidget(view)
            layout.addSpacing(8)

        # Make all header sections the same width
        width_sum = 0
        max_header_count = max([h.count() for h in header_views])
        for i in range(max_header_count):
            max_width = max([w[i] for w in header_widths if i < len(w)] or [0])
            for view in header_views:
                if i < view.count():
                    view.resizeSection(i, max_width)
            width_sum += max_width + 2

        for h in header_views:
            h.setMinimumWidth(h.length() + 4)

        widget = QWidget()
        widget.setLayout(layout)
        widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
        layout.activate()

        max_width = max(h.length() for h in header_views) + 20

        left, _, right, _ = self.getContentsMargins()
        widget.setMinimumWidth(width_sum)
        widget.setMinimumWidth(max_width + left + right)
        self.groups_scroll_area.setWidget(widget)

    def compute_distances(self, separate_keys, partitions, data):
        """Compute the distances between genotypes.
        """
        if separate_keys and partitions:
            self.progressBarInit()
#             matrix = Orange.misc.DistMatrix(len(partitions))
            matrix = numpy.zeros((len(partitions), len(partitions)))

            profiles = [linearize(data, indices) for _, indices in partitions]
            dist_func = self.DISTANCE_FUNCTIONS[self.distance_measure][1]
#             from Orange.utils import progress_bar_milestones
            count = (len(profiles) * len(profiles) - 1) / 2
#             milestones = progress_bar_milestones(count)
            iter_count = 0
            for i in range(len(profiles)):
                for j in range(i + 1, len(profiles)):
                    matrix[i, j] = dist_func(profiles[i], profiles[j])
                    matrix[j, i] = matrix[i, j]
                    iter_count += 1
#                     if iter_count in milestones:
                    self.progressBarSet(100.0 * iter_count / count)
            self.progressBarFinished()

            items = [["{0}={1}".format(key, value)
                      for key, value in zip(separate_keys, values)]
                     for values, _ in partitions]
            items = [" | ".join(item) for item in items]
#             matrix.setattr("items", items)
            matrix = Orange.misc.DistMatrix(matrix)
        else:
            matrix = None

        self.matrix = matrix

    def commit(self):
        separate_keys = self.selected_separeate_by_keys()
        self.compute_distances(separate_keys,
                               self.partitions,
                               self.data)
        if self.split_groups:
            all_attrs = []
            for group, domain in self.split_groups:
                attrs = []
                group_name = " | ".join("{0}={1}".format(*item) for item in
                                        zip(separate_keys, group))
                for attr in domain.attributes:
                    newattr = clone_attr(attr)
                    newattr.attributes["<GENOTYPE GROUP>"] = group_name # Need a better way to pass the groups to downstream widgets.
                    attrs.append(newattr)

                all_attrs.extend(attrs)

            domain = Orange.data.Domain(all_attrs, self.data.domain.class_vars,
                                        self.data.domain.metas)
            data = Orange.data.Table(domain, self.data)
        else:
            data = None
        self.send("Sorted Data", data)
        self.send("Distances", self.matrix)
Example #11
0
class SortedListWidget(QWidget):
    sortingOrderChanged = Signal()

    class _MyItemDelegate(QStyledItemDelegate):
        def __init__(self, sortingModel, parent):
            QStyledItemDelegate.__init__(self, parent)
            self.sortingModel = sortingModel

        def sizeHint(self, option, index):
            size = QStyledItemDelegate.sizeHint(self, option, index)
            return QSize(size.width(), size.height() + 4)

        def createEditor(self, parent, option, index):
            cb = QComboBox(parent)
            cb.setModel(self.sortingModel)
            cb.showPopup()
            return cb

        def setEditorData(self, editor, index):
            pass  # TODO: sensible default

        def setModelData(self, editor, model, index):
            text = editor.currentText()
            model.setData(index, text)

    def __init__(self, *args):
        QWidget.__init__(self, *args)
        self.setContentsMargins(0, 0, 0, 0)
        gridLayout = QGridLayout()
        gridLayout.setContentsMargins(0, 0, 0, 0)
        gridLayout.setSpacing(1)

        model = QStandardItemModel(self)
        model.rowsInserted.connect(self.__changed)
        model.rowsRemoved.connect(self.__changed)
        model.dataChanged.connect(self.__changed)

        self._listView = QListView(self)
        self._listView.setModel(model)
        #        self._listView.setDragEnabled(True)
        self._listView.setDropIndicatorShown(True)
        self._listView.setDragDropMode(QListView.InternalMove)
        self._listView.viewport().setAcceptDrops(True)
        self._listView.setMinimumHeight(100)

        gridLayout.addWidget(self._listView, 0, 0, 2, 2)

        vButtonLayout = QVBoxLayout()

        self._upAction = QAction("\u2191", self, toolTip="Move up")

        self._upButton = QToolButton(self)
        self._upButton.setDefaultAction(self._upAction)
        self._upButton.setSizePolicy(QSizePolicy.Fixed,
                                     QSizePolicy.MinimumExpanding)

        self._downAction = QAction("\u2193", self, toolTip="Move down")

        self._downButton = QToolButton(self)
        self._downButton.setDefaultAction(self._downAction)
        self._downButton.setSizePolicy(QSizePolicy.Fixed,
                                       QSizePolicy.MinimumExpanding)

        vButtonLayout.addWidget(self._upButton)
        vButtonLayout.addWidget(self._downButton)

        gridLayout.addLayout(vButtonLayout, 0, 2, 2, 1)

        hButtonLayout = QHBoxLayout()

        self._addAction = QAction("+", self)
        self._addButton = QToolButton(self)
        self._addButton.setDefaultAction(self._addAction)

        self._removeAction = QAction("-", self)
        self._removeButton = QToolButton(self)
        self._removeButton.setDefaultAction(self._removeAction)
        hButtonLayout.addWidget(self._addButton)
        hButtonLayout.addWidget(self._removeButton)
        hButtonLayout.addStretch(10)
        gridLayout.addLayout(hButtonLayout, 2, 0, 1, 2)

        self.setLayout(gridLayout)
        self._addAction.triggered.connect(self._onAddAction)
        self._removeAction.triggered.connect(self._onRemoveAction)
        self._upAction.triggered.connect(self._onUpAction)
        self._downAction.triggered.connect(self._onDownAction)

    def sizeHint(self):
        size = QWidget.sizeHint(self)
        return QSize(size.width(), 100)

    def _onAddAction(self):
        item = QStandardItem("")
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listView.model().appendRow(item)
        self._listView.setCurrentIndex(item.index())
        self._listView.edit(item.index())

    def _onRemoveAction(self):
        current = self._listView.currentIndex()
        self._listView.model().takeRow(current.row())

    def _onUpAction(self):
        row = self._listView.currentIndex().row()
        model = self._listView.model()
        if row > 0:
            items = model.takeRow(row)
            model.insertRow(row - 1, items)
            self._listView.setCurrentIndex(model.index(row - 1, 0))

    def _onDownAction(self):
        row = self._listView.currentIndex().row()
        model = self._listView.model()
        if row < model.rowCount() and row >= 0:
            items = model.takeRow(row)
            if row == model.rowCount():
                model.appendRow(items)
            else:
                model.insertRow(row + 1, items)
            self._listView.setCurrentIndex(model.index(row + 1, 0))

    def setModel(self, model):
        """ Set a model to select items from
        """
        self._model = model
        self._listView.setItemDelegate(self._MyItemDelegate(self._model, self))

    def addItem(self, *args):
        """ Add a new entry in the list
        """
        item = QStandardItem(*args)
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listView.model().appendRow(item)

    def setItems(self, items):
        self._listView.model().clear()
        for item in items:
            self.addItem(item)

    def items(self):
        order = []
        for row in range(self._listView.model().rowCount()):
            order.append(str(self._listView.model().item(row, 0).text()))
        return order

    def __changed(self):
        self.sortingOrderChanged.emit()

    sortingOrder = property(items, setItems)
class CompleteLineEdit(QLineEdit):
    def __init__(self, words):
        super(CompleteLineEdit, self).__init__(None)

        self.words = words                # QStringList  整个完成列表的单词
        self.listView = QListView(self)
        self.model = QStringListModel(self)
        self.listView.setWindowFlags(Qt.ToolTip)

        self.connect(self, SIGNAL("textChanged(const QString &)"),
                     self, SLOT("setCompleter(const QString &)"))

        self.connect(self.listView, SIGNAL("clicked(const QModelIndex &)"),
                     self, SLOT("completeText(const QModelIndex &)"))

    def focusOutEvent(self, focus_event):
        # self.listView.hide()
        pass

    @pyqtSlot("QKeyEvent")
    def keyPressEvent(self, e):
        if not self.listView.isHidden():
            key = e.key()
            count = self.listView.model().rowCount()
            currentIndex = self.listView.currentIndex()

            if Qt.Key_Down == key:
                # 按向下方向键时,移动光标选中下一个完成列表中的项
                row = currentIndex.row() + 1
                if (row >= count):
                    row = 0

                index = self.listView.model().index(row, 0)
                self.listView.setCurrentIndex(index)
            elif Qt.Key_Up == key:
                # 按向下方向键时,移动光标选中上一个完成列表中的项
                row = currentIndex.row() - 1
                if (row < 0):
                    row = count - 1

                index = self.listView.model().index(row, 0)
                self.listView.setCurrentIndex(index)
            elif Qt.Key_Escape == key:
                # 按下Esc键时,隐藏完成列表
                self.listView.hide()
            elif Qt.Key_Enter == key or Qt.Key_Return == key:
                # 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
                if (currentIndex.isValid()):
                    text = self.listView.currentIndex().data().toString()
                    self.setText(text)

                self.listView.hide()
            else:
                # 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件
                self.listView.hide()
                QLineEdit.keyPressEvent(self,e)
        else:
            QLineEdit.keyPressEvent(self,e)

    # 动态的显示完成列表
    @pyqtSlot("QString")
    def setCompleter(self, text):

        if (text.isEmpty()):
            self.listView.hide()
            return

        if text.length() > 1 and not self.listView.isHidden():
            return

        # 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中
        sl = QStringList()

        for i in range(self.words.count()):
            if self.words[i].contains(text):
                sl << self.words[i]

        self.model.setStringList(sl)
        self.listView.setModel(self.model)

        if (self.model.rowCount() == 0):
            return

        # Position the text edit
        self.listView.setMinimumWidth(self.width())
        self.listView.setMaximumWidth(self.width())

        p = QPoint(0, self.height())
        x = self.mapToGlobal(p).x()
        y = self.mapToGlobal(p).y() + 1

        self.listView.move(x, y)
        self.listView.show()

    # 点击完成列表中的项,使用此项自动完成输入的单词
    @pyqtSlot("QModelIndex")
    def completeText(self, index):
        text = index.data().toString()
        self.setText(text)
        self.listView.hide()
Example #13
0
class OrderSelectorView(QObject):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parentWidget,
                 label_text_NotSelected, label_text_Selected,
                 model):
        """Constructor"""

        QObject.__init__(self)

        self.model = model

        self.gridLayout = QGridLayout(parentWidget)

        self.verticalLayout_left_list = QVBoxLayout()
        self.label_NotSelected = QLabel(parentWidget)
        self.label_NotSelected.setText(label_text_NotSelected)
        self.verticalLayout_left_list.addWidget(self.label_NotSelected)
        self.listView_NotSelected = QListView(parentWidget)
        self.verticalLayout_left_list.addWidget(self.listView_NotSelected)
        self.gridLayout.addLayout(self.verticalLayout_left_list, 0, 0, 1, 2)

        self.verticalLayout_right_left = QVBoxLayout()
        spacerItem = QSpacerItem(20,178,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.verticalLayout_right_left.addItem(spacerItem)
        self.pushButton_right_arrow = QPushButton(parentWidget)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalPolicy(0)
        sizePolicy.setHeightForWidth(
            self.pushButton_right_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_right_arrow.setSizePolicy(sizePolicy)
        self.pushButton_right_arrow.setMinimumSize(QSize(50,50))
        self.pushButton_right_arrow.setMaximumSize(QSize(50,50))
        self.pushButton_right_arrow.setIcon(QIcon(':/right_arrow.png'))
        self.pushButton_right_arrow.setIconSize(QSize(50,50))
        self.pushButton_right_arrow.setText('')
        self.verticalLayout_right_left.addWidget(self.pushButton_right_arrow)
        self.pushButton_left_arrow = QPushButton(parentWidget)
        sizePolicy.setHeightForWidth(
            self.pushButton_left_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_left_arrow.setSizePolicy(sizePolicy)
        self.pushButton_left_arrow.setMinimumSize(QSize(50,50))
        self.pushButton_left_arrow.setMaximumSize(QSize(50,50))
        self.pushButton_left_arrow.setIcon(QIcon(':/left_arrow.png'))
        self.pushButton_left_arrow.setIconSize(QSize(50,50))
        self.pushButton_left_arrow.setText('')
        self.verticalLayout_right_left.addWidget(self.pushButton_left_arrow)
        spacerItem = QSpacerItem(20,178,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.verticalLayout_right_left.addItem(spacerItem)
        self.gridLayout.addLayout(self.verticalLayout_right_left, 0, 2, 1, 1)

        self.verticalLayout_right_list = QVBoxLayout()
        self.label_Selected = QLabel(parentWidget)
        self.label_Selected.setText(label_text_Selected)
        self.verticalLayout_right_list.addWidget(self.label_Selected)
        self.listView_Selected = QListView(parentWidget)
        self.verticalLayout_right_list.addWidget(self.listView_Selected)
        self.gridLayout.addLayout(self.verticalLayout_right_list, 0, 3, 1, 2)

        self.verticalLayout_up_down = QVBoxLayout()
        spacerItem = QSpacerItem(20,178,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.verticalLayout_up_down.addItem(spacerItem)
        self.pushButton_up_arrow = QPushButton(parentWidget)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalPolicy(0)
        sizePolicy.setHeightForWidth(
            self.pushButton_up_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_up_arrow.setSizePolicy(sizePolicy)
        self.pushButton_up_arrow.setMinimumSize(QSize(50,50))
        self.pushButton_up_arrow.setMaximumSize(QSize(50,50))
        self.pushButton_up_arrow.setIcon(QIcon(':/up_arrow.png'))
        self.pushButton_up_arrow.setIconSize(QSize(50,50))
        self.pushButton_up_arrow.setText('')
        self.verticalLayout_up_down.addWidget(self.pushButton_up_arrow)
        self.pushButton_down_arrow = QPushButton(parentWidget)
        sizePolicy.setHeightForWidth(
            self.pushButton_down_arrow.sizePolicy().hasHeightForWidth())
        self.pushButton_down_arrow.setSizePolicy(sizePolicy)
        self.pushButton_down_arrow.setMinimumSize(QSize(50,50))
        self.pushButton_down_arrow.setMaximumSize(QSize(50,50))
        self.pushButton_down_arrow.setIcon(QIcon(':/down_arrow.png'))
        self.pushButton_down_arrow.setIconSize(QSize(50,50))
        self.pushButton_down_arrow.setText('')
        self.verticalLayout_up_down.addWidget(self.pushButton_down_arrow)
        spacerItem = QSpacerItem(20,178,QSizePolicy.Minimum,QSizePolicy.Expanding)
        self.verticalLayout_up_down.addItem(spacerItem)
        self.gridLayout.addLayout(self.verticalLayout_up_down, 0, 5, 1, 1)

        self.listView_NotSelected.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.listView_Selected.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.listView_NotSelected.setModel(self.model.model_NotSelected)
        self.listView_Selected.setModel(self.model.model_Selected)



    #----------------------------------------------------------------------
    def on_right_arrow_press(self):
        """"""

        NSMod = self.listView_NotSelected.model()
        NSSelMod = self.listView_NotSelected.selectionModel()

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = NSSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            SSelMod.clearSelection()
        while mod_ind_list != []:
            mod_ind = mod_ind_list[0]

            item = NSMod.itemFromIndex(mod_ind)
            new_item = item.clone()
            SMod.appendRow(new_item)
            SSelMod.select(SMod.indexFromItem(new_item),
                           QItemSelectionModel.Select)
            NSMod.removeRow(mod_ind.row())

            mod_ind_list = NSSelMod.selectedRows(0)

    #----------------------------------------------------------------------
    def on_left_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        NSMod = self.listView_NotSelected.model()
        NSSelMod = self.listView_NotSelected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            NSSelMod.clearSelection()
        while mod_ind_list != []:
            mod_ind = mod_ind_list[0]

            item = SMod.itemFromIndex(mod_ind)
            text = item.text()
            if text in self.model.permanently_selected_string_list:
                print '"' + text + '"' + ' cannot be unselected.'
                return
            else:
                new_item = item.clone()
                NSMod.appendRow(new_item)
                NSSelMod.select(NSMod.indexFromItem(new_item),
                                QItemSelectionModel.Select)
                SMod.removeRow(mod_ind.row())

            mod_ind_list = SSelMod.selectedRows(0)


    #----------------------------------------------------------------------
    def on_up_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            sorted_row_ind_list = sorted([mod_ind.row() for mod_ind in mod_ind_list])

        for row_ind in sorted_row_ind_list:
            try:
                upper_item = SMod.item(row_ind-1).clone()
                lower_item = SMod.item(row_ind).clone()
                SMod.removeRow(row_ind-1)
                SMod.insertRow(row_ind-1,lower_item)
                SMod.removeRow(row_ind)
                SMod.insertRow(row_ind,upper_item)
                SSelMod.select(SMod.indexFromItem(lower_item),
                               QItemSelectionModel.Select)
            except:
                break

    #----------------------------------------------------------------------
    def on_down_arrow_press(self):
        """"""

        SMod = self.listView_Selected.model()
        SSelMod = self.listView_Selected.selectionModel()

        mod_ind_list = SSelMod.selectedRows(0)
        if mod_ind_list == []:
            return
        else:
            sorted_row_ind_list = sorted([mod_ind.row() for mod_ind in mod_ind_list],
                                         reverse=True)

        for row_ind in sorted_row_ind_list:
            try:
                upper_item = SMod.item(row_ind).clone()
                lower_item = SMod.item(row_ind+1).clone()
                SMod.removeRow(row_ind)
                SMod.insertRow(row_ind,lower_item)
                SMod.removeRow(row_ind+1)
                SMod.insertRow(row_ind+1,upper_item)
                SSelMod.select(SMod.indexFromItem(upper_item),
                               QItemSelectionModel.Select)
            except:
                break
Example #14
0
class RunDialog(QDialog):

    def __init__(self, run_model, parent):
        QDialog.__init__(self, parent)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.setModal(True)
        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle("Simulations")

        assert isinstance(run_model, BaseRunModel)
        self._run_model = run_model

        layout = QVBoxLayout()
        layout.setSizeConstraint(QLayout.SetFixedSize)

        self.simulations_tracker = SimulationsTracker()
        states = self.simulations_tracker.getStates()

        self.total_progress = SimpleProgress()
        layout.addWidget(self.total_progress)

        status_layout = QHBoxLayout()
        status_layout.addStretch()
        self.__status_label = QLabel()
        status_layout.addWidget(self.__status_label)
        status_layout.addStretch()
        layout.addLayout(status_layout)

        self.progress = Progress()
        self.progress.setIndeterminateColor(self.total_progress.color)
        for state in states:
            self.progress.addState(state.state, QColor(*state.color), 100.0 * state.count / state.total_count)

        layout.addWidget(self.progress)

        self.detailed_progress = None

        legend_layout = QHBoxLayout()
        self.legends = {}
        for state in states:
            self.legends[state] = Legend("%s (%d/%d)", QColor(*state.color))
            self.legends[state].updateLegend(state.name, 0, 0)
            legend_layout.addWidget(self.legends[state])

        layout.addLayout(legend_layout)

        self.running_time = QLabel("")

        ert = None
        if isinstance(run_model, BaseRunModel):
            ert = run_model.ert()

        self.plot_tool = PlotTool()
        self.plot_tool.setParent(None)
        self.plot_button = QPushButton(self.plot_tool.getName())
        self.plot_button.clicked.connect(self.plot_tool.trigger)
        self.plot_button.setEnabled(ert is not None)

        self.kill_button = QPushButton("Kill simulations")
        self.done_button = QPushButton("Done")
        self.done_button.setHidden(True)
        self.restart_button = QPushButton("Restart")
        self.restart_button.setHidden(True)
        self.show_details_button = QPushButton("Details")

        self.realizations_view = QListView()
        self.realizations_view.setModel(QStandardItemModel(self.realizations_view))
        self.realizations_view.setVisible(False)

        button_layout = QHBoxLayout()

        size = 20
        spin_movie = resourceMovie("ide/loading.gif")
        spin_movie.setSpeed(60)
        spin_movie.setScaledSize(QSize(size, size))
        spin_movie.start()

        self.processing_animation = QLabel()
        self.processing_animation.setMaximumSize(QSize(size, size))
        self.processing_animation.setMinimumSize(QSize(size, size))
        self.processing_animation.setMovie(spin_movie)

        button_layout.addWidget(self.processing_animation)
        button_layout.addWidget(self.running_time)
        button_layout.addStretch()
        button_layout.addWidget(self.show_details_button)
        button_layout.addWidget(self.plot_button)
        button_layout.addWidget(self.kill_button)
        button_layout.addWidget(self.done_button)
        button_layout.addWidget(self.restart_button)

        layout.addStretch()
        layout.addLayout(button_layout)

        layout.addWidget(self.realizations_view)

        self.setLayout(layout)

        self.kill_button.clicked.connect(self.killJobs)
        self.done_button.clicked.connect(self.accept)
        self.restart_button.clicked.connect(self.restart_failed_realizations)
        self.show_details_button.clicked.connect(self.show_detailed_progress)

        self.__updating = False
        self.__update_queued = False
        self.__simulation_started = False

        self.__update_timer = QTimer(self)
        self.__update_timer.setInterval(500)
        self.__update_timer.timeout.connect(self.updateRunStatus)
        self._simulations_argments = {}

    def startSimulation(self, arguments):

        self._simulations_argments = arguments

        if not 'prev_successful_realizations' in self._simulations_argments:
            self._simulations_argments['prev_successful_realizations'] = 0
        self._run_model.reset()

        def run():
            self._run_model.startSimulations( self._simulations_argments )

        simulation_thread = Thread(name="ert_gui_simulation_thread")
        simulation_thread.setDaemon(True)
        simulation_thread.run = run
        simulation_thread.start()

        self.__update_timer.start()


    def checkIfRunFinished(self):
        if self._run_model.isFinished():

            self.update_realizations_view()
            self.hideKillAndShowDone()

            if self._run_model.hasRunFailed():
                error = self._run_model.getFailMessage()
                QMessageBox.critical(self, "Simulations failed!", "The simulation failed with the following error:\n\n%s" % error)
                self.reject()


    def updateRunStatus(self):
        self.checkIfRunFinished()

        self.total_progress.setProgress(self._run_model.getProgress())

        self.__status_label.setText(self._run_model.getPhaseName())

        states = self.simulations_tracker.getStates()

        if self._run_model.isIndeterminate():
            self.progress.setIndeterminate(True)

            for state in states:
                self.legends[state].updateLegend(state.name, 0, 0)

        else:
            if self.detailed_progress:
                self.detailed_progress.set_progress(*self._run_model.getDetailedProgress())

            self.progress.setIndeterminate(False)
            total_count = self._run_model.getQueueSize()
            queue_status = self._run_model.getQueueStatus()

            for state in states:
                state.count = 0
                state.total_count = total_count

            for state in states:
                for queue_state in queue_status:
                    if queue_state in state.state:
                        state.count += queue_status[queue_state]

                self.progress.updateState(state.state, 100.0 * state.count / state.total_count)
                self.legends[state].updateLegend(state.name, state.count, state.total_count)

        self.setRunningTime()


    def setRunningTime(self):
        days = 0
        hours = 0
        minutes = 0
        seconds = self._run_model.getRunningTime()

        if seconds >= 60:
            minutes, seconds = divmod(seconds, 60)

        if minutes >= 60:
            hours, minutes = divmod(minutes, 60)

        if hours >= 24:
            days, hours = divmod(hours, 24)

        if days > 0:
            self.running_time.setText("Running time: %d days %d hours %d minutes %d seconds" % (days, hours, minutes, seconds))
        elif hours > 0:
            self.running_time.setText("Running time: %d hours %d minutes %d seconds" % (hours, minutes, seconds))
        elif minutes > 0:
            self.running_time.setText("Running time: %d minutes %d seconds" % (minutes, seconds))
        else:
            self.running_time.setText("Running time: %d seconds" % seconds)


    def killJobs(self):
        kill_job = QMessageBox.question(self, "Kill simulations?", "Are you sure you want to kill the currently running simulations?", QMessageBox.Yes | QMessageBox.No )

        if kill_job == QMessageBox.Yes:
            if self._run_model.killAllSimulations():
                self.reject()


    def hideKillAndShowDone(self):
        self.__update_timer.stop()
        self.processing_animation.hide()
        self.kill_button.setHidden(True)
        self.done_button.setHidden(False)
        self.realizations_view.setVisible(True)
        self.restart_button.setVisible(self.has_failed_realizations() )
        self.restart_button.setEnabled(self._run_model.support_restart)


    def has_failed_realizations(self):
        completed = self._run_model.completed_realizations_mask
        initial = self._run_model.initial_realizations_mask
        for (index, successful) in enumerate(completed):
            if initial[index] and not successful:
                return True
        return False


    def count_successful_realizations(self):
        """
        Counts the realizations completed in the prevoius ensemble run
        :return:
        """
        completed = self._run_model.completed_realizations_mask
        return completed.count(True)

    def create_mask_from_failed_realizations(self):
        """
        Creates a BoolVector mask representing the failed realizations
        :return: Type BoolVector
        """
        completed = self._run_model.completed_realizations_mask
        initial = self._run_model.initial_realizations_mask
        inverted_mask = BoolVector(  default_value = False )
        for (index, successful) in enumerate(completed):
            inverted_mask[index] = initial[index] and not successful
        return inverted_mask


    def restart_failed_realizations(self):

        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Information)
        msg.setText("Note that workflows will only be executed on the restarted realizations and that this might have unexpected consequences.")
        msg.setWindowTitle("Restart Failed Realizations")
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        result = msg.exec_()

        if result == QMessageBox.Ok:
            active_realizations = self.create_mask_from_failed_realizations()
            self._simulations_argments['active_realizations'] = active_realizations
            self._simulations_argments['prev_successful_realizations'] += self.count_successful_realizations()
            self.startSimulation(self._simulations_argments)

    def update_realizations_view(self):
        completed = self._run_model.completed_realizations_mask
        for (index, successful) in enumerate(completed):
            items = self.realizations_view.model().findItems(str(index))
            if not items:
                item = QStandardItem(str(index))
                self.realizations_view.model().appendRow(item)
            else:
                item = items[0]
            item.setCheckState(successful or item.checkState())

    def show_detailed_progress(self):
        if not self.detailed_progress:
            self.detailed_progress = DetailedProgressDialog(self, self.simulations_tracker.getStates())
        self.detailed_progress.set_progress(*self._run_model.getDetailedProgress())
        self.detailed_progress.show()
Example #15
0
class OWQualityControl(widget.OWWidget):
    name = "Quality Control"
    description = "Experiment quality control"
    icon = "../widgets/icons/QualityControl.svg"
    priority = 5000

    inputs = [("Experiment Data", Orange.data.Table, "set_data")]
    outputs = []

    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation", dist_pcorr),
                          ("Euclidean distance", dist_eucl),
                          ("Distance from Spearman correlation", dist_spearman)
                          ]

    settingsHandler = SetContextHandler()

    split_by_labels = settings.ContextSetting({})
    sort_by_labels = settings.ContextSetting({})

    selected_distance_index = settings.Setting(0)

    def __init__(self, parent=None):
        super().__init__(parent)

        ## Attributes
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None
        self.base_group_index = 0

        ## GUI
        box = gui.widgetBox(self.controlArea, "Info")
        self.info_box = gui.widgetLabel(box, "\n")

        ## Separate By box
        box = gui.widgetBox(self.controlArea, "Separate By")
        self.split_by_model = itemmodels.PyListModel(parent=self)
        self.split_by_view = QListView()
        self.split_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.split_by_view.setModel(self.split_by_model)
        box.layout().addWidget(self.split_by_view)

        self.split_by_view.selectionModel().selectionChanged.connect(
            self.on_split_key_changed)

        ## Sort By box
        box = gui.widgetBox(self.controlArea, "Sort By")
        self.sort_by_model = itemmodels.PyListModel(parent=self)
        self.sort_by_view = QListView()
        self.sort_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.sort_by_view.setModel(self.sort_by_model)
        box.layout().addWidget(self.sort_by_view)

        self.sort_by_view.selectionModel().selectionChanged.connect(
            self.on_sort_key_changed)

        ## Distance box
        box = gui.widgetBox(self.controlArea, "Distance Measure")
        gui.comboBox(box,
                     self,
                     "selected_distance_index",
                     items=[name for name, _ in self.DISTANCE_FUNCTIONS],
                     callback=self.on_distance_measure_changed)

        self.scene = QGraphicsScene()
        self.scene_view = QGraphicsView(self.scene)
        self.scene_view.setRenderHints(QPainter.Antialiasing)
        self.scene_view.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.mainArea.layout().addWidget(self.scene_view)

        self.scene_view.installEventFilter(self)

        self._disable_updates = False
        self._cached_distances = {}
        self._base_index_hints = {}
        self.main_widget = None

        self.resize(800, 600)

    def clear(self):
        """Clear the widget state."""
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None

        with disable_updates(self):
            self.split_by_model[:] = []
            self.sort_by_model[:] = []

        self.main_widget = None
        self.scene.clear()
        self.info_box.setText("\n")
        self._cached_distances = {}

    def set_data(self, data=None):
        """Set input experiment data."""
        self.closeContext()
        self.clear()

        self.error(0)
        self.warning(0)

        if data is not None:
            keys = self.get_suitable_keys(data)
            if not keys:
                self.error(0, "Data has no suitable feature labels.")
                data = None

        self.data = data
        if data is not None:
            self.on_new_data()

    def update_label_candidates(self):
        """Update the label candidates selection GUI 
        (Group/Sort By views).

        """
        keys = self.get_suitable_keys(self.data)
        with disable_updates(self):
            self.split_by_model[:] = keys
            self.sort_by_model[:] = keys

    def get_suitable_keys(self, data):
        """ Return suitable attr label keys from the data where
        the key has at least two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def selected_split_by_labels(self):
        """Return the current selected split labels.
        """
        sel_m = self.split_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_sort_by_labels(self):
        """Return the current selected sort labels
        """
        sel_m = self.sort_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_distance(self):
        """Return the selected distance function.
        """
        return self.DISTANCE_FUNCTIONS[self.selected_distance_index][1]

    def selected_base_group_index(self):
        """Return the selected base group index
        """
        return self.base_group_index

    def selected_base_indices(self, base_group_index=None):
        indices = []
        for g, ind in self.groups:
            if base_group_index is None:
                label = group_label(self.selected_split_by_labels(), g)
                ind = [i for i in ind if i is not None]
                i = self._base_index_hints.get(label, ind[0] if ind else None)
            else:
                i = ind[base_group_index]
            indices.append(i)
        return indices

    def on_new_data(self):
        """We have new data and need to recompute all.
        """
        self.closeContext()

        self.update_label_candidates()
        self.info_box.setText(
            "%s genes \n%s experiments" %
            (len(self.data), len(self.data.domain.attributes)))

        self.base_group_index = 0

        keys = self.get_suitable_keys(self.data)
        self.openContext(keys)

        ## Restore saved context settings (split/sort selection)
        split_by_labels = self.split_by_labels
        sort_by_labels = self.sort_by_labels

        def select(model, selection_model, selected_items):
            """Select items in a Qt item model view
            """
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            for ind in indices:
                selection_model.select(model.index(ind),
                                       QItemSelectionModel.Select)

        with disable_updates(self):
            select(self.split_by_view.model(),
                   self.split_by_view.selectionModel(), split_by_labels)

            select(self.sort_by_view.model(),
                   self.sort_by_view.selectionModel(), sort_by_labels)

        with widget_disable(self):
            self.split_and_update()

    def on_split_key_changed(self, *args):
        """Split key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.split_by_labels = self.selected_split_by_labels()
                self.split_and_update()

    def on_sort_key_changed(self, *args):
        """Sort key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.sort_by_labels = self.selected_sort_by_labels()
                self.split_and_update()

    def on_distance_measure_changed(self):
        """Distance measure has changed
        """
        if self.data is not None:
            with widget_disable(self):
                self.update_distances()
                self.replot_experiments()

    def on_view_resize(self, size):
        """The view with the quality plot has changed
        """
        if self.main_widget:
            current = self.main_widget.size()
            self.main_widget.resize(size.width() - 6, current.height())

            self.scene.setSceneRect(self.scene.itemsBoundingRect())

    def on_rug_item_clicked(self, item):
        """An ``item`` in the quality plot has been clicked.
        """
        update = False
        sort_by_labels = self.selected_sort_by_labels()
        if sort_by_labels and item.in_group:
            ## The item is part of the group
            if item.group_index != self.base_group_index:
                self.base_group_index = item.group_index
                update = True

        else:
            if sort_by_labels:
                # If the user clicked on an background item it
                # invalidates the sorted labels selection
                with disable_updates(self):
                    self.sort_by_view.selectionModel().clear()
                    update = True

            index = item.index
            group = item.group
            label = group_label(self.selected_split_by_labels(), group)

            if self._base_index_hints.get(label, 0) != index:
                self._base_index_hints[label] = index
                update = True

        if update:
            with widget_disable(self):
                self.split_and_update()

    def eventFilter(self, obj, event):
        if obj is self.scene_view and event.type() == QEvent.Resize:
            self.on_view_resize(event.size())
        return super().eventFilter(obj, event)

    def split_and_update(self):
        """
        Split the data based on the selected sort/split labels
        and update the quality plot.

        """
        split_labels = self.selected_split_by_labels()
        sort_labels = self.selected_sort_by_labels()

        self.warning(0)
        if not split_labels:
            self.warning(0, "No separate by label selected.")

        self.groups, self.unique_pos = \
                exp.separate_by(self.data, split_labels,
                                consider=sort_labels,
                                add_empty=True)

        self.groups = sorted(self.groups.items(),
                             key=lambda t: list(map(float_if_posible, t[0])))
        self.unique_pos = sorted(
            self.unique_pos.items(),
            key=lambda t: list(map(float_if_posible, t[0])))

        if self.groups:
            if sort_labels:
                group_base = self.selected_base_group_index()
                base_indices = self.selected_base_indices(group_base)
            else:
                base_indices = self.selected_base_indices()
            self.update_distances(base_indices)
            self.replot_experiments()

    def get_cached_distances(self, measure):
        if measure not in self._cached_distances:
            attrs = self.data.domain.attributes
            mat = numpy.zeros((len(attrs), len(attrs)))

            self._cached_distances[measure] = \
                (mat, set(zip(range(len(attrs)), range(len(attrs)))))

        return self._cached_distances[measure]

    def get_cached_distance(self, measure, i, j):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        if key in computed:
            return matrix[i, j]
        else:
            return None

    def get_distance(self, measure, i, j):
        d = self.get_cached_distance(measure, i, j)
        if d is None:
            vec_i = take_columns(self.data, [i])
            vec_j = take_columns(self.data, [j])
            d = measure(vec_i, vec_j)

            mat, computed = self.get_cached_distances(measure)
            mat[i, j] = d
            key = key = (i, j) if i < j else (j, i)
            computed.add(key)
        return d

    def store_distance(self, measure, i, j, dist):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        matrix[j, i] = matrix[i, j] = dist
        computed.add(key)

    def update_distances(self, base_indices=()):
        """Recompute the experiment distances.
        """
        distance = self.selected_distance()
        if base_indices == ():
            base_group_index = self.selected_base_group_index()
            base_indices = [ind[base_group_index] \
                            for _, ind in self.groups]

        assert (len(base_indices) == len(self.groups))

        base_distances = []
        attributes = self.data.domain.attributes
        pb = gui.ProgressBar(self, len(self.groups) * len(attributes))

        for (group, indices), base_index in zip(self.groups, base_indices):
            # Base column of the group
            if base_index is not None:
                base_vec = take_columns(self.data, [base_index])
                distances = []
                # Compute the distances between base column
                # and all the rest data columns.
                for i in range(len(attributes)):
                    if i == base_index:
                        distances.append(0.0)
                    elif self.get_cached_distance(distance, i,
                                                  base_index) is not None:
                        distances.append(
                            self.get_cached_distance(distance, i, base_index))
                    else:
                        vec_i = take_columns(self.data, [i])
                        dist = distance(base_vec, vec_i)
                        self.store_distance(distance, i, base_index, dist)
                        distances.append(dist)
                    pb.advance()

                base_distances.append(distances)
            else:
                base_distances.append(None)

        pb.finish()
        self.distances = base_distances

    def replot_experiments(self):
        """Replot the whole quality plot.
        """
        self.scene.clear()
        labels = []

        max_dist = numpy.nanmax(list(filter(None, self.distances)))
        rug_widgets = []

        group_pen = QPen(Qt.black)
        group_pen.setWidth(2)
        group_pen.setCapStyle(Qt.RoundCap)
        background_pen = QPen(QColor(0, 0, 250, 150))
        background_pen.setWidth(1)
        background_pen.setCapStyle(Qt.RoundCap)

        main_widget = QGraphicsWidget()
        layout = QGraphicsGridLayout()
        attributes = self.data.domain.attributes
        if self.data is not None:
            for (group, indices), dist_vec in zip(self.groups, self.distances):
                indices_set = set(indices)
                rug_items = []
                if dist_vec is not None:
                    for i, attr in enumerate(attributes):
                        # Is this a within group distance or background
                        in_group = i in indices_set
                        if in_group:
                            rug_item = ClickableRugItem(
                                dist_vec[i] / max_dist, 1.0,
                                self.on_rug_item_clicked)
                            rug_item.setPen(group_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)
                            rug_item.group_index = indices.index(i)
                            rug_item.setZValue(rug_item.zValue() + 1)
                        else:
                            rug_item = ClickableRugItem(
                                dist_vec[i] / max_dist, 0.85,
                                self.on_rug_item_clicked)
                            rug_item.setPen(background_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)

                        rug_item.group = group
                        rug_item.index = i
                        rug_item.in_group = in_group

                        rug_items.append(rug_item)

                rug_widget = RugGraphicsWidget(parent=main_widget)
                rug_widget.set_rug(rug_items)

                rug_widgets.append(rug_widget)

                label = group_label(self.selected_split_by_labels(), group)
                label_item = QGraphicsSimpleTextItem(label, main_widget)
                label_item = GraphicsSimpleTextLayoutItem(label_item,
                                                          parent=layout)
                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                labels.append(label_item)

        for i, (label, rug_w) in enumerate(zip(labels, rug_widgets)):
            layout.addItem(label, i, 0, Qt.AlignVCenter)
            layout.addItem(rug_w, i, 1)
            layout.setRowMaximumHeight(i, 30)

        main_widget.setLayout(layout)
        self.scene.addItem(main_widget)
        self.main_widget = main_widget
        self.rug_widgets = rug_widgets
        self.labels = labels
        self.on_view_resize(self.scene_view.size())