def __init__(self, data, size, params): self.camera = params.get("acquisition.camera", "camera1") self.data = data self.height = size[0] # Pre HAL2 movies. if params.has(self.camera + ".scalemin"): self.image_min = params.get(self.camera + ".scalemin") self.image_max = params.get(self.camera + ".scalemax") else: self.image_min = params.get("display00.camera1.display_min") self.image_max = params.get("display00.camera1.display_max") self.parameters = params self.parameters_file = params.get("parameters_file", "NA") self.width = size[1] location = list( map(float, params.get("acquisition.stage_position").split(","))) self.x_um = location[0] self.y_um = location[1] # Calculate location in pixels. a_point = coord.Point(self.x_um, self.y_um, "um") self.x_pix = a_point.x_pix self.y_pix = a_point.y_pix
def loadFromMosaicFileData(self, data, directory): if (data[0] == "section"): self.addSection(coord.Point(float(data[2]), float(data[3]), "um"), float(data[4])) return True else: return False
def loadPositions(self, filename): pos_fp = open(filename, "r") while 1: line = pos_fp.readline() if not line: break [x, y] = line.split(",") self.addPosition(coord.Point(float(x), float(y), "um"))
def __init__(self, comm=None, item_store=None, parameters=None, **kwds): super().__init__(**kwds) self.comm = comm self.current_center = coord.Point(0.0, 0.0, "um") self.current_z = 0.0 self.directory = parameters.get("directory") self.extrapolate_count = parameters.get("extrapolate_picture_count") self.filename = parameters.get("image_filename") self.fractional_overlap = parameters.get("fractional_overlap", 0.05) self.grid_size = [] self.item_store = item_store self.last_image = None self.movie_queue = [] self.objectives = None self.smc = None self.z_inc = 0.01 # The idea is that in the future other modules might want to # change how movies are taken and loaded. This will hopefully # make this easier. # # This class handles creating a SteveItem() from the movie. self.movie_loader = None # This class handles taking the movie. self.movie_taker = None
def loadPositions(self, filename): with open(filename) as fp: for line in fp: try: [x, y] = line.split(",") self.addPosition(coord.Point(float(x), float(y), "um")) except ValueError: pass
def handleTakeMovie(self, ignored): """ Handle movies triggered from the context menu and the space bar key. """ self.image_capture.takeMovies([ coord.Point(self.mosaic_event_coord.x_um, self.mosaic_event_coord.y_um, "um") ])
def handleMouseMove(self, a_point): # Not sure whether I should include the offset here. # offset_point = coord.Point(a_point.x_um - self.current_offset.x_um, # a_point.y_um - self.current_offset.y_um, # "um") offset_point = coord.Point(a_point.x_um, a_point.y_um, "um") self.ui.mosaicLabel.setText("{0:.2f}, {1:.2f}".format( offset_point.x_um, offset_point.y_um))
def getCurrentOffset(self): """ Return the offset of the current objective. The current objective should always be the one that was used to take the last movie and/or the one that was returned by HAL when queried. """ objective_name = self.getCurrentName() if objective_name is not None: [obj_um_per_pix, x_offset_um, y_offset_um] = self.getData(objective_name) return coord.Point(x_offset_um, y_offset_um, "um")
def handleGetPositionComplete(self, a_point): if not self.requested_stage_pos: # Update cross hair offset_point = coord.Point(a_point.x_um + self.current_offset.x_um, a_point.y_um + self.current_offset.y_um, "um") self.view.setCrosshairPosition(offset_point.x_pix, offset_point.y_pix) else: self.requested_stage_pos = False self.ui.xStartPosSpinBox.setValue(a_point.x_um) self.ui.yStartPosSpinBox.setValue(a_point.y_um) self.comm.commDisconnect()
def handleTakeSpiral(self, n_pictures): """ Handle taking a spiral pattern. """ movie_queue = [] # Starting point. movie_queue.append( coord.Point(self.mosaic_event_coord.x_um, self.mosaic_event_coord.y_um, "um")) # Spiral. movie_queue += imageCapture.createSpiral(n_pictures) self.image_capture.takeMovies(movie_queue)
def addImage(self, image): # If image is not an object then we are done. if not image: self.toggleTakingPicturesStatus(False) self.comm.commDisconnect() return objective = image.parameters.get( "mosaic." + image.parameters.get("mosaic.objective")).split(",")[0] [um_per_pixel, x_offset, y_offset] = self.ui.objectivesGroupBox.getData(objective) magnification = coord.Point.pixels_to_um / um_per_pixel self.current_offset = coord.Point(x_offset, y_offset, "um") self.view.addImage(image, objective, magnification, self.current_offset) self.view.setCrosshairPosition(image.x_pix, image.y_pix) if (len(self.picture_queue) > 0): next_item = self.picture_queue[0] if (type(next_item) == type(coord.Point(0, 0, "um"))): self.setCenter(next_item) next_x_um = self.current_center.x_um next_y_um = self.current_center.y_um else: [tx, ty] = next_item next_x_um = self.current_center.x_um + 0.95 * float( image.width) * coord.Point.pixels_to_um * tx / magnification next_y_um = self.current_center.y_um + 0.95 * float( image.height ) * coord.Point.pixels_to_um * ty / magnification self.picture_queue = self.picture_queue[1:] self.comm.captureStart(next_x_um, next_y_um) else: if self.taking_pictures: self.toggleTakingPicturesStatus(False) self.comm.commDisconnect()
def handleImageGrid(self, dummy): if not self.taking_pictures: # Build position list pos_list = mosaicView.createGrid(self.ui.xSpinBox.value(), self.ui.ySpinBox.value()) # Define first position first_pos = coord.Point(self.ui.xStartPosSpinBox.value(), self.ui.yStartPosSpinBox.value(), "um") pos_list.insert(0, first_pos) # Take pictures self.takePictures(pos_list) else: # Abort button self.picture_queue = []
def mousePressEvent(self, event): """ If the left mouse button is pressed then the view is centered on the current cursor position. If the right mouse button is pressed then the current location of the cursor in the scene is recorded. If self.extrapolate_start exists then self.handleExtrapolatePict() is called, otherwise the popup menu is displayed. """ if event.button() == QtCore.Qt.LeftButton: self.centerOn(self.mapToScene(event.pos())) elif event.button() == QtCore.Qt.RightButton: pointf = self.mapToScene(event.pos()) a_coord = coord.Point(pointf.x(), pointf.y(), "pix") if self.extrapolate_start: self.extrapolateTakeMovie.emit(a_coord) else: self.mosaicViewContextMenuEvent.emit(event, a_coord)
def handleTakeGrid(self): """ Handle taking a grid pattern. """ movie_queue = [] # Starting point. movie_queue.append( coord.Point(self.mosaic_event_coord.x_um, self.mosaic_event_coord.y_um, "um")) # Grid. movie_queue += imageCapture.createGrid(self.ui.xSpinBox.value(), self.ui.ySpinBox.value()) self.image_capture.takeMovies(movie_queue)
def handleMessageReceived(self, message): if message.hasError(): hdebug.logText("tcp error: " + message.getErrorMessage()) self.messages = [] self.waiting_for_response = False return # # If the message does not involve taking a movie and there are no more # messages then emit the otherComplete signal. # if (message.getData("is_other") == True) and (len(self.messages) == 0): self.otherComplete.emit() if (message.getType() == "Get Mosaic Settings"): self.got_settings = True #coord.Point.pixels_to_um = message.getResponse("pixels_to_um") i = 1 while message.getResponse("obj" + str(i)) is not None: self.newObjectiveData.emit( message.getResponse("obj" + str(i)).split(",")) i += 1 if (message.getType() == "Get Objective"): if self.curr_objective is None or ( self.curr_objective != message.getResponse("objective")): self.curr_objective = message.getResponse("objective") self.changeObjective.emit(self.curr_objective) if (message.getType() == "Get Stage Position"): a_point = coord.Point(message.getResponse("stage_x"), message.getResponse("stage_y"), "um") self.getPositionComplete.emit(a_point) # # self.loadImage() will emit the captureComplete signal. # if (message.getType() == "Take Movie"): self.loadImage(self.directory + message.getData("name") + ".dax") if (len(self.messages) > 0): self.tcp_client.sendMessage(self.messages.pop(0)) else: self.waiting_for_response = False
def keyPressEvent(self, event): """ Handles key press events. Valid events are: 'space' Take a picture. '3' Take a 3 picture spiral. '5' Take a 5 picture spiral. '7' Take a 7 picture spiral. '9' Take a 9 picture spiral. 'g' Take a grid of pictures. 'p' Add the current cursor position to the list of positions. 's' Add the current cursor position to the list of sections. """ event_pos = self.mapFromGlobal(QtGui.QCursor.pos()) pointf = self.mapToScene(event_pos) a_coord = coord.Point(pointf.x(), pointf.y(), "pix") self.mosaicViewKeyPressEvent.emit(event, a_coord) super().keyPressEvent(event)
def handleExtrapolateTakeMovie(self, a_coord): """ This is called on the next right click after the extrapolate action was selected. """ movie_queue = [] # Starting point. x_um = a_coord.x_um + (a_coord.x_um - self.mosaic_view.extrapolate_start.x_um) y_um = a_coord.y_um + (a_coord.y_um - self.mosaic_view.extrapolate_start.y_um) movie_queue.append(coord.Point(x_um, y_um, "um")) # Spiral. movie_queue += imageCapture.createSpiral(self.extrapolate_count) self.mosaic_view.extrapolate_start = None self.image_capture.takeMovies(movie_queue)
def handleImageGridButton(self, ignored): """ Handle taking a grid pattern when the 'Acquire' button is clicked. """ movie_queue = [] # Starting point. x_start_um = self.ui.xStartPosSpinBox.value() y_start_um = self.ui.yStartPosSpinBox.value() movie_queue.append(coord.Point(x_start_um, y_start_um, "um")) # Grid. movie_queue += imageCapture.createGrid(self.ui.xSpinBox.value(), self.ui.ySpinBox.value()) self.ui.imageGridButton.setText("Abort") self.ui.imageGridButton.setStyleSheet("QPushButton { color: red }") self.image_capture.takeMovies(movie_queue)
def takeSingleMovie(self, movie_pos): """ This takes a single movie as the specified position corrected for the current offset. Clients should not use this method, they should always takeMovies(). """ current_offset = self.objectives.getCurrentOffset() # Bail out if we don't have any objectives information. This # probably means HAL is not running or we can't talk to it. if current_offset is None: return pos = coord.Point(movie_pos.x_um - current_offset.x_um, movie_pos.y_um - current_offset.y_um, "um") self.smc = self.movie_taker(comm_instance=self.comm, disconnect=False, directory=self.directory, filename=self.filename, finalizer_fn=self.handleMovieTaken, pos=pos) return self.smc.start()
def nextMovie(self): """ Take the next movie, or disconnect if there are no more movies to take. """ if (len(self.movie_queue) > 0): # Figure out where to take the movie. elt = self.movie_queue[0] if isinstance(elt, list): [dx, dy] = elt [im_x_um, im_y_um] = self.last_image.getSizeUm() next_x_um = self.current_center.x_um + ( 1.0 - self.fractional_overlap) * im_x_um * dx next_y_um = self.current_center.y_um + ( 1.0 - self.fractional_overlap) * im_y_um * dy movie_pos = coord.Point(next_x_um, next_y_um, "um") else: self.current_center = elt movie_pos = elt # Remove from the queue. self.movie_queue = self.movie_queue[1:] # Take the movie, checking for failure to communicate with HAL. if not self.takeSingleMovie(movie_pos): self.movie_queue = [] self.smc = None self.sequenceComplete.emit() else: self.comm.stopCommunication() self.smc = None self.sequenceComplete.emit()
def mouseMoveEvent(self, event): """ Tracks mouse movements across the view. """ pointf = self.mapToScene(event.pos()) self.mouseMove.emit(coord.Point(pointf.x(), pointf.y(), "pix"))
def updateMosaicLabel(self, a_point): offset_point = coord.Point(a_point.x_um - self.current_offset.x_um, a_point.y_um - self.current_offset.y_um, "um") self.ui.mosaicLabel.setText("{0:.2f}, {1:.2f}".format( offset_point.x_um, offset_point.y_um))
def __init__(self, parameters, parent=None): QtWidgets.QMainWindow.__init__(self, parent) # Coordinate system setup, the internal scale is 1 pixel is 100nm. coord.Point.pixels_to_um = 0.1 # variables self.current_center = coord.Point(0.0, 0.0, "um") self.current_offset = coord.Point(0.0, 0.0, "um") self.debug = parameters.get("debug") self.file_filter = "\S+.dax" self.parameters = parameters self.picture_queue = [] self.regexp_str = "" self.requested_stage_pos = False self.settings = QtCore.QSettings("storm-control", "steve") self.snapshot_directory = self.parameters.get("directory") self.spin_boxes = [] self.stage_tracking_timer = QtCore.QTimer(self) self.taking_pictures = False self.stage_tracking_timer.setInterval(500) # ui setup self.ui = steveUi.Ui_MainWindow() self.ui.setupUi(self) self.move(self.settings.value("position", self.pos())) self.resize(self.settings.value("size", self.size())) # hide some things that we don't currently use & resize group-box. self.ui.backgroundComboBox.hide() self.ui.backgroundLabel.hide() self.ui.moveAllSectionsCheckBox.hide() self.ui.showFeaturesCheckBox.hide() self.ui.thresholdLabel.hide() self.ui.thresholdSlider.hide() self.ui.sectionViewSettingsGroupBox.setMaximumHeight(50) self.setWindowIcon(QtGui.QIcon("steve.ico")) # handling file drops self.ui.centralwidget.__class__.dragEnterEvent = self.dragEnterEvent self.ui.centralwidget.__class__.dropEvent = self.dropEvent self.ui.centralwidget.setAcceptDrops(True) # Create a validator for scaleLineEdit. self.scale_validator = QtGui.QDoubleValidator(1.0e-6, 1.0e+6, 6, self.ui.scaleLineEdit) self.ui.scaleLineEdit.setValidator(self.scale_validator) # Initialize view. self.view = mosaicView.MosaicView(parameters, self.ui.mosaicFrame) layout = QtWidgets.QGridLayout(self.ui.mosaicFrame) layout.addWidget(self.view) self.ui.mosaicFrame.setLayout(layout) self.view.show() # Initialize positions list. self.positions = positions.Positions(parameters, self.view.getScene(), self.ui.positionsFrame) layout = QtWidgets.QGridLayout(self.ui.positionsFrame) layout.addWidget(self.positions) self.ui.positionsFrame.setLayout(layout) self.positions.show() # Initialize sections. self.sections = sections.Sections(parameters, self.view.getScene(), self.ui.sectionsDisplayFrame, self.ui.sectionsScrollArea, self.ui.sectionsTab) # Initialize communications. self.comm = capture.Capture(parameters) # signals self.ui.actionQuit.triggered.connect(self.quit) self.ui.actionAdjust_Contrast.triggered.connect( self.handleAdjustContrast) self.ui.actionDelete_Images.triggered.connect(self.handleDeleteImages) self.ui.actionLoad_Movie.triggered.connect(self.handleLoadMovie) self.ui.actionLoad_Mosaic.triggered.connect(self.handleLoadMosaic) self.ui.actionLoad_Positions.triggered.connect( self.handleLoadPositions) self.ui.actionSave_Mosaic.triggered.connect(self.handleSaveMosaic) self.ui.actionSave_Positions.triggered.connect( self.handleSavePositions) self.ui.actionSave_Snapshot.triggered.connect(self.handleSnapshot) self.ui.actionSet_Working_Directory.triggered.connect( self.handleSetWorkingDirectory) self.ui.foregroundOpacitySlider.valueChanged.connect( self.handleOpacityChange) self.ui.getStagePosButton.clicked.connect(self.handleGetStagePosButton) self.ui.imageGridButton.clicked.connect(self.handleImageGrid) self.ui.scaleLineEdit.textEdited.connect(self.handleScaleChange) self.ui.tabWidget.currentChanged.connect(self.handleTabChange) self.ui.trackStageCheckBox.stateChanged.connect(self.handleTrackStage) self.ui.xSpinBox.valueChanged.connect(self.handleGridChange) self.ui.ySpinBox.valueChanged.connect(self.handleGridChange) self.stage_tracking_timer.timeout.connect( self.handleStageTrackingTimer) self.view.addPosition.connect(self.addPositions) self.view.addSection.connect(self.addSection) self.view.getObjective.connect(self.handleGetObjective) self.view.gotoPosition.connect(self.gotoPosition) self.view.mouseMove.connect(self.updateMosaicLabel) self.view.scaleChange.connect(self.updateScaleLineEdit) self.view.takePictures.connect(self.takePictures) self.sections.addPositions.connect(self.addPositions) self.sections.takePictures.connect(self.takePictures) self.comm.captureComplete.connect(self.addImage) self.comm.changeObjective.connect(self.handleChangeObjective) self.comm.disconnected.connect(self.handleDisconnected) self.comm.getPositionComplete.connect(self.handleGetPositionComplete) self.comm.newObjectiveData.connect(self.handleNewObjectiveData) self.comm.otherComplete.connect(self.handleOtherComplete) self.ui.objectivesGroupBox.valueChanged.connect( self.handleMOValueChange) # Try and get settings from HAL. self.comm.commConnect() self.comm.getSettings()
def currentLocation(self): return coord.Point(self.x_spin_box.value(), self.y_spin_box.value(), "um")
def load(self, directory, index, x, y, angle): section_item = SectionItem( a_point=coord.Point(float(x), float(y), "um")) section_item.setAngle(float(angle)) return section_item
def load(self, directory, x, y): return PositionItem(coord.Point(float(x), float(y), "um"))
def handleChangeObjective(self, objective): self.ui.objectivesGroupBox.changeObjective(objective) [magnification, x_offset, y_offset] = self.ui.objectivesGroupBox.getData(objective) self.current_offset = coord.Point(x_offset, y_offset, "um")
def setCenter(self, a_point): x_um = a_point.x_um - self.current_offset.x_um y_um = a_point.y_um - self.current_offset.y_um self.current_center = coord.Point(x_um, y_um, "um")
def movePosition(self, dx_um, dy_um): a_point = coord.Point(self.a_point.x_um + dx_um, self.a_point.y_um + dy_um, "um") self.setLocation(a_point)
def handleExtrapolate(self, ignored): """ This is called when the extrapolate action is selected from the context menu. """ self.mosaic_view.extrapolate_start = coord.Point( self.mosaic_event_coord.x_um, self.mosaic_event_coord.y_um, "um")