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}
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)
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
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 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
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)
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())
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)
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()
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
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 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())