def setupToolbar(self): self.toolBar = ViewToolBar(self) self.fileToolBar = FileToolBar(self) self.fileToolBar.actionOpen.triggered.connect(self.onFileOpen) self.fileToolBar.actionNew.triggered.connect(self.newDataFile) self.batchToolBar = BatchToolBar(self) self.batchToolBar.batchChanged.connect(self.loadItems) self.batchToolBar.actionReload.triggered.connect(self.loadItems) self.actionReloadFile.triggered.connect(self.loadItems) self.sortToolBar = SortToolBar(self) self.addToolBar(QtCore.Qt.TopToolBarArea, self.fileToolBar) self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) self.addToolBar(QtCore.Qt.TopToolBarArea, self.sortToolBar) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.batchToolBar) self.navToolBar = NavToolBar(self) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.navToolBar) self.navToolBar.coordUpdated.connect(self.loader.setCoordinate)
class AtMainWindow(QtWidgets.QMainWindow): coordUpdated = QtCore.pyqtSignal("PyQt_PyObject") abort = QtCore.pyqtSignal() def __init__(self, file_=None, *args, **kw): super(AtMainWindow, self).__init__(*args, **kw) loadUI(splitext(__file__)[0]+'.ui', self) self.setWindowTitle(version.appstr) self.setAcceptDrops(True) self.featuredlg = AtFeatureSelectionDlg(self) self.featuredlg.hide() self.loaderThread = AtThread(self) self.loader = AtLoader() self._lastdir = expanduser("~") try: self.assistant = AtAssistant(MANUAL) self.assistant.hide() except IOError: QMessageBox.information(self, "Information", "Sorry help files are not installed") self.setupToolbar() self.tileview = AtGraphicsView( parent=self, gsize=self.navToolBar.galsize) self.toolBar.valueChanged.connect(self.tileview.zoom) self.toolBar.flagsChanged.connect( self.tileview.setViewFlags, Qt.QueuedConnection) self.setCentralWidget(self.tileview) self.setupDock() self.setupProgressBar() self.loader.fileOpened.connect(self.updateToolbars) self.loader.progressUpdate.connect(self.updateProgressBar) self.loader.itemCountChanged.connect(self.batchToolBar.setItemCount) self.loader.itemLoaded.connect(self.tileview.addItem) self.abort.connect(self.loader.abort) self.actionNewFile.triggered.connect(self.newDataFile) self.actionOpenHdf.triggered.connect(self.onFileOpen) self.actionCloseHdf.triggered.connect(self.onFileClose) self.actionPreferences.triggered.connect(self.onPreferences) # Data Export self.actionExportViewPanel.triggered.connect(self.saveImage) self.actionSaveData2Csv.triggered.connect(self.saveData2Csv) self.actionSaveCountingStats.triggered.connect(self.saveCountingStats) # Plots self.actionGroupedByClass.triggered.connect(self.plotGroupedByClass) self.actionGroupedByTreatment.triggered.connect( self.plotGroupedByTreatment) self.actionIntensity.triggered.connect(self.plotIntesity) self.actionSize.triggered.connect(self.plotSize) self.actionStdDev.triggered.connect(self.plotStdDev) self.actionPlotAll.triggered.connect(self.plotAll) self.actionAboutQt.triggered.connect(self.onAboutQt) self.actionAboutAnnotationTool.triggered.connect(self.onAbout) self.actionFeatureSelection.triggered.connect( self.showFeatureDlg) self.actionHelpManual.triggered.connect(self.onHelpManual) self.actionShortcuts.triggered.connect(self.onHelpShortcuts) self.actionGettingStarted.triggered.connect(self.onHelpGettingStarted) self.actionRefresh.triggered.connect(self.refresh) self.actionSelectAll.triggered.connect( self.tileview.actionSelectAll.trigger) self.actionInvertSelection.triggered.connect( self.tileview.actionInvertSelection.trigger) self.actionSelectByTreatment.triggered.connect(self.selectByTreatment) self.tileview.zoomChanged.connect(self.updateZoomFactor) self.tileview.quickSort.connect(self.sortToolBar.quickSortBtn.toggle) self.tileview.quickSort.connect(self.onQuickSort) self.featuredlg.selectionChanged.connect( self.annotation.predictionInvalid) self.loader.started.connect(self.onLoadingStarted) self.zoom = QtWidgets.QLabel('100%') self.statusbar.insertPermanentWidget(0, self.zoom) self.statusbar.insertPermanentWidget(1, QtWidgets.QLabel('Number of items:')) self.nitems = QtWidgets.QLabel('0') self.statusbar.insertPermanentWidget(2, self.nitems) self._restoreSettings() self.navToolBar.hide() self.show() if file_ is not None: self.loader.openFile(file_) self.loadItems() def updateProgressBar(self, value): if value < 0: self.progressbar.setRange(0, 0) self.progressbar.setValue(value) def updateZoomFactor(self, factor): self.zoom.setText("%d%s" %(round(100*factor, 0), "%")) def dragEnterEvent(self, event): event.acceptProposedAction() def dragMoveEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): mimeData = event.mimeData() if mimeData.hasUrls(): if len(mimeData.urls()) == 1: self.abort.emit() self.loaderThread.wait() self.loader.close() self.onDropEvent(fix_path(mimeData.urls()[0].path())) event.acceptProposedAction() def dragLeaveEvent(self, event): event.accept() def onInitalStartup(self): self.onHelpGettingStarted() def onHelpManual(self): self.assistant.show() self.assistant.raise_() self.assistant.openKeyword('Shortcuts') def onHelpShortcuts(self): self.onHelpManual() self.assistant.openKeyword("Shortcuts") def onHelpGettingStarted(self): self.onHelpManual() self.assistant.openKeyword("Getting started") def refresh(self): if self.loader.file is None: return self.tileview.actionRefresh.trigger() self.contrast.enhanceContrast() def showFeatureDlg(self): self.featuredlg.show() self.featuredlg.raise_() def onPreferences(self): dlg = AtPreferencesDialog(self) dlg.exec_() def onAboutQt(self): QMessageBox.aboutQt(self, "about Qt") def onAbout(self): dlg = AtAboutDialog(self) dlg.exec_() def resetCursor(self): QApplication.restoreOverrideCursor() def setWaitingCursor(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) def _saveSettings(self): settings = QtCore.QSettings(version.organisation, version.appname) settings.beginGroup('Gui') settings.setValue('config', version.config) settings.setValue('state', self.saveState()) settings.setValue('geometry', self.saveGeometry()) settings.setValue('classifier', self.annotation.classifiers.currentText()) settings.endGroup() AtConfig().saveSettings() def _restoreSettings(self): settings = QtCore.QSettings(version.organisation, version.appname) settings.beginGroup('Gui') if settings.contains('config'): if version.config != settings.value('config'): # FIXME - helpengine needs to be delayed for unknown reason QtCore.QTimer.singleShot(500, self.onInitalStartup) return else: QtCore.QTimer.singleShot(500, self.onInitalStartup) if settings.contains('geometry'): geometry = settings.value('geometry') self.restoreGeometry(geometry) if settings.contains('state'): state = settings.value('state') self.restoreState(state) if settings.contains('classifier'): clfname = settings.value("classifier") self.annotation.setCurrentClassifier(clfname) AtConfig().restoreSettings() settings.endGroup() self.batchToolBar.setBatchSize(AtConfig().batch_size) def closeEvent(self, event): if self.annotation.isModified(): msg = "The classifier has not been saved. Close program anyway?" reply = QMessageBox.question( self, 'Warning', msg, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() return self._saveSettings() try: self.abort.emit() self.loaderThread.wait() self.loader.close() if self.assistant is not None: self.assistant.close() except AttributeError: pass def setupDock(self): self.contrast = AtContrastWidget(self, self.tileview) self.sorting = AtSortWidget(self, self.tileview, self.featuredlg) self.annotation = AtAnnotationWidget( self, self.tileview, self.featuredlg) self.contrastdock = QtWidgets.QDockWidget("Contrast", self) self.contrastdock.setWidget(self.contrast) self.contrastdock.setObjectName("contrast") self.addDockWidget(Qt.LeftDockWidgetArea, self.contrastdock) self.sortdock = QtWidgets.QDockWidget("Sorting", self) self.sortdock.setWidget(self.sorting) self.sortdock.setObjectName("sorting") self.addDockWidget(Qt.RightDockWidgetArea, self.sortdock) self.annodock = QtWidgets.QDockWidget("Training", self) self.annodock.setWidget(self.annotation) self.annodock.setObjectName("training") self.addDockWidget(Qt.RightDockWidgetArea, self.annodock) self.tabifyDockWidget(self.sortdock, self.annodock) # add action to the view menu sort_action = self.sortdock.toggleViewAction() sort_action.setShortcuts(QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_S)) self.menuView.addAction(sort_action) anno_action = self.annodock.toggleViewAction() anno_action.setShortcuts(QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_A)) self.menuView.addAction(anno_action) contrast_action = self.contrastdock.toggleViewAction() contrast_action.setShortcuts( QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_C)) self.menuView.addAction(contrast_action) # crosslink sorter dock and sorter toolbar self.sortToolBar.sortAlgorithm.currentIndexChanged.connect( self.sorting.sortAlgorithm.setCurrentIndex) self.sorting.sortAlgorithm.currentIndexChanged.connect( self.sortToolBar.sortAlgorithm.setCurrentIndex) self.sortToolBar.sortAscendingBtn.clicked.connect( self.sorting.sortAscending) self.sortToolBar.sortDescendingBtn.clicked.connect( self.sorting.sortDescending) self.sortToolBar.quickSortBtn.toggled.connect( self.tileview.setQuickSortMode) def setupProgressBar(self): frame = QtWidgets.QFrame(self) self.progressbar = QtWidgets.QProgressBar(frame) self.progressbar.setMaximumHeight(15) self.abortBtn = QtWidgets.QPushButton('abort', self) self.abortBtn.clicked.connect(self.abort.emit) self.abortBtn.setMaximumHeight(20) hbox = QtWidgets.QHBoxLayout(frame) hbox.addWidget(self.progressbar) hbox.addWidget(self.abortBtn) hbox.setContentsMargins(0, 0, 0, 0) self.loaderThread.started.connect(frame.show) self.loaderThread.started.connect(self.setWaitingCursor) self.loaderThread.finished.connect(frame.hide) self.loaderThread.finished.connect(self.resetCursor) frame.hide() self.statusBar().addPermanentWidget(frame) def saveImage(self): filename = QFileDialog.getSaveFileName( self, "Save image as ...", self._lastdir.replace('.hdf', '.png'), "png - Image (*.png)")[0] if filename: scene = self.tileview.scene() size = scene.sceneRect().size().toSize() image = QtGui.QImage(size, QtGui.QImage.Format_RGB32) painter = QtGui.QPainter(image) image.fill(QtCore.Qt.white) scene.render(painter) painter.end() image.save(filename) self.statusBar().showMessage("Image saved to %s" %filename) def saveData2Csv(self): filename = QFileDialog.getSaveFileName( self, "Save csv-file as ...", self._lastdir.replace(".hdf", ".csv"), "Comma separated values (*.csv)")[0] if filename: if AtConfig().only_classifier_features: features = self.featuredlg.checkedItems() else: features = self.featuredlg.allItems() exporter = CsvExporter(filename, self.tileview.items, features) exporter.save() self.statusBar().showMessage("Image saved to %s" %filename) def saveCountingStats(self): filename = QFileDialog.getSaveFileName( self, "Save csv-file as ...", self._lastdir.replace(".hdf", "_statistics.csv"), "Comma separated values (*.csv)")[0] if filename: features = self.featuredlg.checkedItems() exporter = StatsExporter(filename, self.tileview.items, features) exporter.save() self.statusBar().showMessage("Image saved to %s" %filename) def _validPlotData(self): flag = False if not self.tileview.items: QMessageBox.information(self, "Nothing to plot!", "No data file is opened") elif not self.annotation.hasValidClassifier(): QMessageBox.information(self, "Nothing to plot!", "No valid classifier trained or loaded") elif not self.annotation.hasValidPrediction(): QMessageBox.information(self, "Nothing to plot!", "Press 'Predict' before plotting") else: flag = True return flag def plotGroupedByTreatment(self, pos=False): if self._validPlotData(): classes = self.annotation.currentClassifier().classes barplot = plots.BarGraph(self.tileview.items, classes, self.actionExcludeTrainingData.isChecked(), self) self.actionCloseAll.triggered.connect(barplot.close) if pos: barplot.move(pos) barplot.show() else: barplot.exec_() def plotGroupedByClass(self, pos=False): if self._validPlotData(): classes = self.annotation.currentClassifier().classes barplot = plots.BarGraph2(self.tileview.items, classes, self.actionExcludeTrainingData.isChecked(), self) self.actionCloseAll.triggered.connect(barplot.close) if pos: barplot.move(pos) barplot.show() else: barplot.exec_() def plotIntesity(self, pos=False): if self._validPlotData(): indices = self.featuredlg.indexByName(CoreFeatures.Intensity) classes = self.annotation.currentClassifier().classes boxplot = plots.BoxPlot( self.tileview.items, indices, classes, "Intensity", self.actionExcludeTrainingData.isChecked(), self) self.actionCloseAll.triggered.connect(boxplot.close) if pos: boxplot.move(pos) boxplot.show() else: boxplot.exec_() def plotSize(self, pos=False): if self._validPlotData(): indices = self.featuredlg.indexByName(CoreFeatures.Size) classes = self.annotation.currentClassifier().classes boxplot = plots.BoxPlot( self.tileview.items, indices, classes, "Size", self.actionExcludeTrainingData.isChecked(), self) self.actionCloseAll.triggered.connect(boxplot.close) if pos: boxplot.move(pos) boxplot.show() else: boxplot.exec_() def plotStdDev(self, pos=False): if self._validPlotData(): indices = self.featuredlg.indexByName(CoreFeatures.StdDev) classes = self.annotation.currentClassifier().classes boxplot = plots.BoxPlot( self.tileview.items, indices, classes, "Standard Deviation", self.actionExcludeTrainingData.isChecked(), self) self.actionCloseAll.triggered.connect(boxplot.close) if pos: boxplot.move(pos) boxplot.show() else: boxplot.exec_() def plotAll(self): """Plot everything for people who don't know what do""" if self._validPlotData(): shift = QtCore.QPoint(20, 20) pos = self.pos() + 4*shift pos = [pos + i*shift for i in xrange(5)] self.plotGroupedByClass(pos[0]) self.plotGroupedByTreatment(pos[1]) self.plotIntesity(pos[2]) self.plotSize(pos[3]) self.plotStdDev(pos[4]) def updateToolbars(self, props): # in case of cellh5 file if props.gal_settings_mutable: self.navToolBar.show() self.navToolBar.updateToolbar(props) self.contrast.setChannelNames(props.channel_names, props.colors) self.nitems.setText(str(props.n_items)) def setupToolbar(self): self.toolBar = ViewToolBar(self) self.fileToolBar = FileToolBar(self) self.fileToolBar.actionOpen.triggered.connect(self.onFileOpen) self.fileToolBar.actionNew.triggered.connect(self.newDataFile) self.batchToolBar = BatchToolBar(self) self.batchToolBar.batchChanged.connect(self.loadItems) self.batchToolBar.actionReload.triggered.connect(self.loadItems) self.actionReloadFile.triggered.connect(self.loadItems) self.sortToolBar = SortToolBar(self) self.addToolBar(QtCore.Qt.TopToolBarArea, self.fileToolBar) self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) self.addToolBar(QtCore.Qt.TopToolBarArea, self.sortToolBar) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.batchToolBar) self.navToolBar = NavToolBar(self) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.navToolBar) self.navToolBar.coordUpdated.connect(self.loader.setCoordinate) def newDataFile(self): try: dlg = ImportDialog( self, Qt.WindowMinMaxButtonsHint|Qt.WindowCloseButtonHint) dlg.loadData.connect(self._openAndLoad) dlg.exec_() except Exception as e: QMessageBox.critical(None, "Error", str(e)) def onQuickSort(self): self.sorting.removeAll() items = self.tileview.selectedItems() self.sorting.addItems(items) self.sorting.applyDefaultSortAlgorithm() self.sorting.sortAscending() self.tileview.centerOn(items[0]) def selectByTreatment(self): label, flag = QInputDialog.getItem(self, "Select a Treatment", "Treatment", self.tileview.treatments, editable=False) if flag: self.tileview.selectByTreatment(label) def addToSortPanel(self): self.sorting.addItems(self.tileview.selectedItems()) def onFileClose(self): self.loader.close() self.tileview.clear() self.featuredlg.clear() self.sorting.clear() def onFileOpen(self): file_ = QFileDialog.getOpenFileName( self, "Open hdf5 file", self._lastdir, "Hdf files (*.hdf5 *.ch5 *.hdf *.h5)")[0] if bool(file_): self.tileview.zoom(1.0) self._fileOpen(file_) self.loadItems() def _fileOpen(self, file_): self._lastdir = abspath(file_) try: self.loader.openFile(file_) except Exception as e: self.statusBar().showMessage(str(e)) msg = "Could not open file\n %s" %str(e) QMessageBox.critical(self, "Error", msg) else: self.statusBar().showMessage(basename(file_)) def _openAndLoad(self, file_): self.onFileClose() self._fileOpen(file_) self.loadItems() def onLoadingStarted(self): self.annotation.setFeatureNames(self.loader.featureNames) try: self.featuredlg.setFeatureGroups(self.loader.featureGroups) self.sorting.setFeatureGroups(self.loader.featureGroups) except RuntimeError as e: pass def onDropEvent(self, path): self._fileOpen(path) self.loadItems() def loadItems(self): self.abort.emit() self.loaderThread.wait() self.tileview.setViewFlags(self.toolBar.viewFlags()) self.tileview.clear() self.progressbar.setValue(0) self.tileview.updateRaster(self.navToolBar.galsize) self.tileview.updateNColumns(self.tileview.size().width()) idx = self.batchToolBar.indices() self.nitems.setText("%d/%d" %(len(idx), self.batchToolBar.count)) self.progressbar.setRange(0, len(idx)) self.loader.setBatch(idx) self.loader.setGallerySize(self.navToolBar.galsize) self.loaderThread.start(self.loader)