class LabelAssistDialog(QDialog): """ A simple UI for showing bookmarks and navigating to them. FIXME: For now, this window is tied to a particular lane. If your project has more than one lane, then each one will have it's own bookmark window, which is kinda dumb. """ def __init__(self, parent, topLevelOperatorView): super(LabelAssistDialog, self).__init__(parent) # Create thread router to populate table on main thread self.threadRouter = ThreadRouter(self) # Set object classification operator view self.topLevelOperatorView = topLevelOperatorView self.setWindowTitle("Label Assist") self.setMinimumWidth(500) self.setMinimumHeight(700) layout = QGridLayout() layout.setContentsMargins(10, 10, 10, 10) # Show variable importance table rows = 0 columns = 4 self.table = QTableWidget(rows, columns) self.table.setHorizontalHeaderLabels( ['Frame', 'Max Area', 'Min Area', 'Labels']) self.table.verticalHeader().setVisible(False) # Select full row on-click and call capture double click self.table.setSelectionBehavior(QTableView.SelectRows) self.table.doubleClicked.connect(self._captureDoubleClick) layout.addWidget(self.table, 1, 0, 3, 2) # Create progress bar self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(0) self.progressBar.hide() layout.addWidget(self.progressBar, 4, 0, 1, 2) # Create button to populate table self.computeButton = QPushButton('Compute object info') self.computeButton.clicked.connect(self._triggerTableUpdate) layout.addWidget(self.computeButton, 5, 0) # Create close button closeButton = QPushButton('Close') closeButton.clicked.connect(self.close) layout.addWidget(closeButton, 5, 1) # Set dialog layout self.setLayout(layout) def _triggerTableUpdate(self): # Check that object area is included in selected features featureNames = self.topLevelOperatorView.SelectedFeatures.value if 'Standard Object Features' not in featureNames or 'Count' not in featureNames[ 'Standard Object Features']: box = QMessageBox( QMessageBox.Warning, 'Warning', 'Object area is not a selected feature. Please select this feature on: \"Standard Object Features > Shape > Size in pixels\"', QMessageBox.NoButton, self) box.show() return # Clear table self.table.clearContents() self.table.setRowCount(0) self.table.setSortingEnabled(False) self.progressBar.show() self.computeButton.setEnabled(False) # Compute object features and number of labels per frame def compute_features(): features = self.topLevelOperatorView.ObjectFeatures([]).wait() labels = self.topLevelOperatorView.LabelInputs([]).wait() return features, labels req = Request(compute_features) req.notify_finished(self._populateTable) req.submit() @threadRouted def _populateTable(self, features_and_labels): features, labels = features_and_labels self.progressBar.hide() self.computeButton.setEnabled(True) for frame, objectFeatures in features.iteritems(): # Insert row rowNum = self.table.rowCount() self.table.insertRow(self.table.rowCount()) # Get max and min object areas areas = objectFeatures['Standard Object Features']['Count'] maxObjArea = numpy.max(areas[numpy.nonzero(areas)]) minObjArea = numpy.min(areas[numpy.nonzero(areas)]) # Get number of labeled objects labelNum = numpy.count_nonzero(labels[frame]) # Load fram number item = QTableWidgetItem(str(frame)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.table.setItem(rowNum, 0, item) # Load max object areas item = QTableWidgetItemWithFloatSorting( str("{: .02f}".format(maxObjArea))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.table.setItem(rowNum, 1, item) # Load min object areas item = QTableWidgetItemWithFloatSorting( str("{: .02f}".format(minObjArea))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.table.setItem(rowNum, 2, item) # Load label numbers item = QTableWidgetItemWithFloatSorting( str("{: .01f}".format(labelNum))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.table.setItem(rowNum, 3, item) # Resize column size to fit dialog size self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch) # Sort by max object area self.table.setSortingEnabled(True) self.table.sortByColumn(1) def _captureDoubleClick(self): # Navigate to selected frame index = self.table.selectedIndexes()[0] frameStr = self.table.model().data(index).toString() if frameStr: frameNum = int(frameStr) self.parent().editor.posModel.time = frameNum
class LabelAssistDialog(QDialog): """ A simple UI for showing bookmarks and navigating to them. FIXME: For now, this window is tied to a particular lane. If your project has more than one lane, then each one will have it's own bookmark window, which is kinda dumb. """ def __init__(self, parent, topLevelOperatorView): super(LabelAssistDialog, self).__init__(parent) # Create thread router to populate table on main thread self.threadRouter = ThreadRouter(self) # Set object classification operator view self.topLevelOperatorView = topLevelOperatorView self.setWindowTitle("Label Assist") self.setMinimumWidth(500) self.setMinimumHeight(700) layout = QGridLayout() layout.setContentsMargins(10, 10, 10, 10) # Show variable importance table rows = 0 columns = 4 self.table = QTableWidget(rows, columns) self.table.setHorizontalHeaderLabels(['Frame', 'Max Area', 'Min Area', 'Labels']) self.table.verticalHeader().setVisible(False) # Select full row on-click and call capture double click self.table.setSelectionBehavior(QTableView.SelectRows); self.table.doubleClicked.connect(self._captureDoubleClick) layout.addWidget(self.table, 1, 0, 3, 2) # Create progress bar self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(0) self.progressBar.hide() layout.addWidget(self.progressBar, 4, 0, 1, 2) # Create button to populate table self.computeButton = QPushButton('Compute object info') self.computeButton.clicked.connect(self._triggerTableUpdate) layout.addWidget(self.computeButton, 5, 0) # Create close button closeButton = QPushButton('Close') closeButton.clicked.connect(self.close) layout.addWidget(closeButton, 5, 1) # Set dialog layout self.setLayout(layout) def _triggerTableUpdate(self): # Check that object area is included in selected features featureNames = self.topLevelOperatorView.SelectedFeatures.value if 'Standard Object Features' not in featureNames or 'Count' not in featureNames['Standard Object Features']: box = QMessageBox(QMessageBox.Warning, 'Warning', 'Object area is not a selected feature. Please select this feature on: \"Standard Object Features > Shape > Size in pixels\"', QMessageBox.NoButton, self) box.show() return # Clear table self.table.clearContents() self.table.setRowCount(0) self.table.setSortingEnabled(False) self.progressBar.show() self.computeButton.setEnabled(False) def compute_features_for_frame(tIndex, t, features): # Compute features and labels (called in parallel from request pool) roi = [slice(None) for i in range(len(self.topLevelOperatorView.LabelImages.meta.shape))] roi[tIndex] = slice(t, t+1) roi = tuple(roi) frame = self.topLevelOperatorView.SegmentationImages(roi).wait() frame = frame.squeeze().astype(numpy.uint32, copy=False) # Dirty trick: We don't care what we're passing here for the 'image' parameter, # but vigra insists that we pass *something*, so we'll cast the label image as float32. features[t] = vigra.analysis.extractRegionFeatures(frame.view(numpy.float32), frame, ['Count'], ignoreLabel=0) tIndex = self.topLevelOperatorView.SegmentationImages.meta.axistags.index('t') tMax = self.topLevelOperatorView.SegmentationImages.meta.shape[tIndex] features = {} labels = {} def compute_all_features(): # Compute features in parallel pool = RequestPool() for t in range(tMax): pool.add( Request( partial(compute_features_for_frame, tIndex, t, features) ) ) pool.wait() # Compute labels labels = self.topLevelOperatorView.LabelInputs([]).wait() req = Request(compute_all_features) req.notify_finished( partial(self._populateTable, features, labels) ) req.submit() @threadRouted def _populateTable(self, features, labels, *args): self.progressBar.hide() self.computeButton.setEnabled(True) for time, feature in features.iteritems(): # Insert row rowNum = self.table.rowCount() self.table.insertRow(self.table.rowCount()) # Get max and min object areas areas = feature['Count']#objectFeatures['Standard Object Features']['Count'] maxObjArea = numpy.max(areas[numpy.nonzero(areas)]) minObjArea = numpy.min(areas[numpy.nonzero(areas)]) # Get number of labeled objects labelNum = numpy.count_nonzero(labels[time]) # Load fram number item = QTableWidgetItem(str(time)) item.setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled ) self.table.setItem(rowNum, 0, item) # Load max object areas item = QTableWidgetItemWithFloatSorting(str("{: .02f}".format(maxObjArea))) item.setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled ) self.table.setItem(rowNum, 1, item) # Load min object areas item = QTableWidgetItemWithFloatSorting(str("{: .02f}".format(minObjArea))) item.setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled ) self.table.setItem(rowNum, 2, item) # Load label numbers item = QTableWidgetItemWithFloatSorting(str("{: .01f}".format(labelNum))) item.setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled ) self.table.setItem(rowNum, 3, item) # Resize column size to fit dialog size self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch) # Sort by max object area self.table.setSortingEnabled(True) self.table.sortByColumn(1) def _captureDoubleClick(self): # Navigate to selected frame index = self.table.selectedIndexes()[0] frameStr = self.table.model().data(index).toString() if frameStr: frameNum = int(frameStr) self.parent().editor.posModel.time = frameNum
class ActionEditorDialog(QWidget): # Redefine the tr() function for this class. def tr(self, text): return qApp.translate("ActionEditorDialog", text) def __init__(self, actions, parent=None): super(ActionEditorDialog, self).__init__(parent) self.actions = actions help = QLabel(translate("Shortcut Settings", '<b>Double click a cell in the Shortcut Column' \ ' to <br />modify the key sequence.</b>')) self.actionTable = QTableWidget(self) self.actionTable.setSelectionBehavior(QTableWidget.SelectRows) self.actionTable.setEditTriggers(QTableWidget.DoubleClicked) self.actionTable.setColumnCount(2) self.actionTable.setHorizontalHeaderLabels( [translate("Shortcut Settings", "Description"), translate("Shortcut Settings", "Shortcut")] ) self.actionTable.horizontalHeader().setStretchLastSection(True) self.actionTable.verticalHeader().hide() self.actionTable.setItemDelegate(ActionEditorDelegate(self)) self.connect(self.actionTable, SIGNAL("cellChanged(int, int)"), self.validateAction) row = 0 for action in self.actions: if action.text().isEmpty(): continue self.actionTable.insertRow(self.actionTable.rowCount()) item = QTableWidgetItem() item.setText(action.text()) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) self.actionTable.setItem(row, 0, item) item = QTableWidgetItem() item.setText(action.shortcut().toString()) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable) item.oldShortcutText = item.text() self.actionTable.setItem(row, 1, item) row += 1 self.actionTable.resizeColumnsToContents() mainLayout = QVBoxLayout() mainLayout.addWidget(help) mainLayout.setMargin(8) mainLayout.setSpacing(8) mainLayout.addWidget(self.actionTable) self.setLayout(mainLayout) self._model = self.actionTable.model() self._model.edited = False self.actionTable.model().edited = False self.setWindowTitle(translate("Shortcut Settings", "Edit Shortcuts")) def applySettings(self, control=None): if not self._model.edited: return row = 0 for action in self.actions: if not action.text().isEmpty(): action.setText(self.actionTable.item(row, 0).text()) action.setShortcut(QKeySequence(self.actionTable.item(row, 1).text())) row += 1 self.saveSettings(self.actions) self._model.edited = False def _loadSettings(self, actions): cparser = PuddleConfig(os.path.join(CONFIGDIR, 'user_shortcuts')) for action in actions: shortcut = cparser.get('shortcuts', unicode(action.text()), '') if shortcut: action.setShortcut(QKeySequence(shortcut)) _loadSettings = classmethod(_loadSettings) def saveSettings(self, actions): cparser = PuddleConfig(os.path.join(CONFIGDIR, 'user_shortcuts')) for action in actions: shortcut = unicode(action.shortcut().toString()) cparser.set('shortcuts', unicode(action.text()), shortcut) saveSettings = classmethod(saveSettings) def validateAction(self, row, column): if column != 1: return item = self.actionTable.item(row, column) shortcutText = QKeySequence(item.text()).toString() thisRow = self.actionTable.row(item) if not shortcutText.isEmpty(): for row in range(self.actionTable.rowCount()): if row == thisRow: continue other = self.actionTable.item(row, 1) if other.text() == shortcutText: other.setText(item.oldShortcutText) break item.setText(shortcutText) item.oldShortcutText = shortcutText self.actionTable.resizeColumnToContents(1)