def __init__(self, name, groupName, callbacks, wavelength, exposureTime, trigHandler=None, trigLine=None): # Note we assume all light sources are eligible for experiments. # However there's no associated callbacks for a light source. super().__init__(name, groupName, True, callbacks, depot.LIGHT_TOGGLE) self.wavelength = float(wavelength or 0) self.defaultExposureTime = exposureTime self.exposureTime = exposureTime # Current enabled state self.state = deviceHandler.STATES.disabled # Set up trigger handling. if trigHandler and trigLine: h = trigHandler.registerDigital(self, trigLine) self.triggerNow = h.triggerNow if 'setExposing' not in callbacks: cb = lambda name, state: trigHandler.setDigital( trigLine, state) callbacks['setExposing'] = cb else: self.triggerNow = lambda: None # Most lasers use bulb-type triggering. Ensure they're not left on after # an abort event. if trigHandler and trigLine: onAbort = lambda *args: trigHandler.setDigital(trigLine, False) events.subscribe(events.USER_ABORT, onAbort)
def __init__(self, parent): wx.Frame.__init__(self, parent, title = "Camera views", style=wx.FRAME_NO_TASKBAR | wx.CAPTION) self.numCameras = len(depot.getHandlersOfType(depot.CAMERA)) self.panel = wx.Panel(self) # Make a 2xN grid of camera canvases, with menus above for selecting # which camera to use in that location. self.sizer = wx.FlexGridSizer(2, 5, 5) ## List of ViewPanels we contain. self.views = [] for i in range(self.numCameras): view = viewPanel.ViewPanel(self.panel) self.views.append(view) self.SetPosition((675, 280)) events.subscribe(events.CAMERA_ENABLE, self.onCameraEnableEvent) events.subscribe("image pixel info", self.onImagePixelInfo) cockpit.gui.keyboard.setKeyboardHandlers(self) self.Bind(wx.EVT_CLOSE, self.onClose) self.resetGrid() self.SetDropTarget(cockpit.gui.viewFileDropTarget.ViewFileDropTarget(self))
def __init__(self, name, config): super().__init__(name, config) ## Connection to the XYZ stage controller (serial.Serial instance) self.xyzConnection = None ## Lock around sending commands to the XYZ stage controller. self.xyzLock = threading.Lock() ## Cached copy of the stage's position. Initialized to an impossible # value; this will be modified in initialize. self.xyzPositionCache = (10 ** 100, 10 ** 100, 10 ** 100) ## Target positions for movement in X, Y and Z, or None if that ch is # not moving. self.xyzMotionTargets = [None, None, None] ## If there is a config section for the smaractMCS2, grab the config and # subscribe to events. try : limitString = config.get('softlimits') parsed = re.search(LIMITS_PAT, limitString) if not parsed: # Could not parse config entry. raise Exception('Bad config: smaractMCS2XYZ Limits.') # No transform tuple else: lstr = parsed.groupdict()['limits'] self.softlimits=eval(lstr) except: print ("No softlimits section setting default limits") self.softlimits = ((-15000, -15000, -15000), (15000, 15000, 15000)) events.subscribe(events.USER_ABORT, self.onAbort)
def __init__(self, name, config={}): super().__init__(name, config) ## Connection to the XY stage controller (serial.Serial instance). self._proxy = Pyro4.Proxy(config.get('uri')) ## Lock around sending commands to the XY stage controller. self.xyLock = threading.Lock() ## Cached copy of the stage's position. self.positionCache = (None, None) ## Target positions for movement in X and Y. self.motionTargets = [None, None] ## Flag to show that sendPositionUpdates is running. self.sendingPositionUpdates = False ## Status dict updated by remote. self.status = {} ## Keys for status items that should be logged self.logger = valueLogger.ValueLogger( name, keys=list(map('t_'.__add__, self._temperature_names))) try: xlim = self._proxy.get_value_limits('MotorSetpointX') ylim = self._proxy.get_value_limits('MotorSetpointY') except: xlim, ylim = zip(*DEFAULT_LIMITS) # _proxy may return (0,0) if it can't query the hardware. if not any(xlim): xlim, _ = zip(*DEFAULT_LIMITS) if not any(ylim): _, ylim = zip(*DEFAULT_LIMITS) self.hardlimits = tuple(zip(xlim, ylim)) self.softlimits = self.hardlimits events.subscribe(events.USER_ABORT, self.onAbort)
def makeUI(self, parent): # TODO - this should probably live in a base deviceHandler. self.panel = wx.Panel(parent) sizer = wx.BoxSizer(wx.VERTICAL) # Readout mode control sizer.Add(wx.StaticText(self.panel, label="Readout mode")) modeButton = wx.Choice(self.panel, choices=self._modenames) self.updateModeButton(modeButton) sizer.Add(modeButton, flag=wx.EXPAND) events.subscribe(events.SETTINGS_CHANGED % self, lambda: self.updateModeButton(modeButton)) modeButton.Bind(wx.EVT_CHOICE, lambda evt: self.setReadoutMode(evt.GetSelection())) sizer.AddSpacer(4) # Gain control sizer.Add(wx.StaticText(self.panel, label="Gain")) gainButton = wx.Button(self.panel, label="%s" % self.settings.get('gain', None)) if 'gain' not in self.settings: gainButton.Disable() gainButton.Bind(wx.EVT_BUTTON, self.onGainButton) sizer.Add(gainButton, flag=wx.EXPAND) events.subscribe( events.SETTINGS_CHANGED % self, lambda: gainButton.SetLabel( "%s" % self.settings.get('gain', None))) sizer.AddSpacer(4) # Settings button adv_button = wx.Button(parent=self.panel, label='Settings') adv_button.Bind(wx.EVT_LEFT_UP, self.showSettings) sizer.Add(adv_button) self.panel.SetSizerAndFit(sizer) return self.panel
def __init__(self): ## Maps axis to the handlers for that axis, sorted by their range of # motion. self.axisToHandlers = depot.getSortedStageMovers() ## XXX: We have a single index for all axis, even though each ## axis may have a different number of stages. While we don't ## refactor this assumption, we just make copies of the movers ## with the most precise movement (issues #413 and #415) self.n_stages = max([len(s) for s in self.axisToHandlers.values()]) for axis, stages in self.axisToHandlers.items(): stages.extend([stages[-1]] * (self.n_stages - len(stages))) ## Indicates which stage handler is currently under control. self.curHandlerIndex = 0 ## Maps Site unique IDs to Site instances. self.idToSite = {} ## Maps handler names to events indicating if those handlers # have stopped moving. self.nameToStoppedEvent = {} events.subscribe("stage mover", self.onMotion) events.subscribe("stage stopped", self.onStop) ## Device-speficic primitives to draw on the macrostage. self.primitives = set() for h in depot.getHandlersOfType(depot.STAGE_POSITIONER): ps = h.getPrimitives() if ps: self.primitives.update(ps) self.primitives.discard(None)
def __init__(self, name, config): super().__init__(name, config) self.STAGE_CAL = config.get('cal') # e.g. 13.750 self.PICO_CONTROLLER = config.get('ipaddress') # e.g. 172.16.0.30' self.PICO_PORT = config.get('port') # e.g. 23 ## Maps the cockpit's axis ordering (0: X, 1: Y, 2: Z) to the # XY stage's ordering which is #x controller 1, motor 1 - '1>1' #y controller 1, motor 2 - '1>2' #z controller 2, motor 1 - '2>1' #Needs moving to config file! self.axisMapper = {0: '1>1', 1: '1>2', 2: '2>1'} ## Connection to the Z piezo controller (Pyro4.Proxy of a # telnetlib.Telnet instance) self.zConnection = None ## Lock around sending commands to the Z piezo controller. self.zLock = threading.Lock() ## Connection to the XY stage controller (serial.Serial instance) self.xyConnection = None ## Lock around sending commands to the XY stage controller. self.xyLock = threading.Lock() ## Cached copy of the stage's position. Initialized to an impossible # value; this will be modified in initialize. self.xyPositionCache = [10**100, 10**100, 7500] events.subscribe(events.USER_ABORT, self.onAbort)
def __init__(self, name, config={}): super().__init__(name, config) try: limitString = config.get('softlimits', '') parsed = re.search(LIMITS_PAT, limitString) if not parsed: # Could not parse config entry. raise Exception('Bad config: Aerotech Limits.') # No transform tuple else: lstr = parsed.groupdict()['limits'] self.softlimits = eval(lstr) except: print("No softlimits section setting default limits") self.softlimits = (-30000, 7000) # Subscribe to abort events. events.subscribe(events.USER_ABORT, self.onAbort) # The cockpit axis does this stage moves along. self.axis = int(config.get('axis', 2)) # Socket used to communicate with controller. self.socket = None # Last known position (microns) self.position = None # Axis acceleration (mm / s^2) self.acceleration = 200 # Axis maximum speed (mm / s^2) self.speed = 20
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) ## Handle of the current camera we're controlling. self.curCamera = None ## Position of the currently displayed image - only updated on dbl-click. self.imagePos = None columnSizer = wx.BoxSizer(wx.VERTICAL) ## Clickable text box showing the name of the currently-selected # camera. self.selector = wx.StaticText(self, style=wx.RAISED_BORDER | wx.ALIGN_CENTRE | wx.ST_NO_AUTORESIZE, size=(VIEW_WIDTH, 30)) self.selector.Bind(wx.EVT_LEFT_DOWN, self.onSelector) self.selector.SetDoubleBuffered(True) columnSizer.Add(self.selector, 0) ## Panel for holding our canvas. self.canvasPanel = wx.Panel(self) self.canvasPanel.SetMinSize((VIEW_WIDTH, VIEW_HEIGHT)) columnSizer.Add(self.canvasPanel) self.SetSizerAndFit(columnSizer) ## Canvas we paint the camera's view onto. Created when we connect a # camera, and destroyed after. self.canvas = None self.disable() events.subscribe("filter change", self.onFilterChange) self.Bind(wx.EVT_LEFT_DCLICK, self.onMouse)
def __init__(self, name, groupName, callbacks, dlines=None, alines=None): # \param name: handler name # \param groupname: handler and device group name # \param callbacks: callbacks, as above # \param dlines: optional, number of digital lines # \param alines: optional, number of analogue lines # Note that even though this device is directly involved in running # experiments, it is never itself a part of an experiment, so # we pass False for isEligibleForExperiments here. super().__init__(name, groupName, False, callbacks, depot.EXECUTOR) # Base class contains empty dicts used by mixins so that methods like # getNumRunnableLines can be implemented here for all mixin combos. This # works just great, but is probably a horrible abuse of OOP. It might be # cleaner to have a single list of clients. self.digitalClients = {} self.analogClients = {} # Number of digital and analogue lines. self._dlines = dlines self._alines = alines if not isinstance(self, DigitalMixin): self.registerDigital = self._raiseNoDigitalException self.getDigital = self._raiseNoDigitalException self.setDigital = self._raiseNoDigitalException self.readDigital = self._raiseNoDigitalException self.writeDigital = self._raiseNoDigitalException self.triggerDigital = self._raiseNoDigitalException if not isinstance(self, AnalogMixin): self.registerAnalog = self._raiseNoAnalogException self.setAnalog = self._raiseNoAnalogException self.getAnalog = self._raiseNoAnalogException self.setAnalogClient = self._raiseNoAnalogException self.getAnalogClient = self._raiseNoAnalogException events.subscribe(events.PREPARE_FOR_EXPERIMENT, self.onPrepareForExperiment) events.subscribe(events.CLEANUP_AFTER_EXPERIMENT, self.cleanupAfterExperiment)
def __init__(self, name, groupName, callbacks, wavelength, minPower, maxPower, curPower, isEnabled=True, units='mW'): # Validation: required = set(['getPower', 'setPower']) missing = required.difference(callbacks) if missing: e = Exception('%s %s missing callbacks: %s.' % (self.__class__.__name__, name, ' '.join(missing))) raise e super().__init__(name, groupName, False, callbacks, depot.LIGHT_POWER) LightPowerHandler._instances.append(self) self.wavelength = wavelength self.minPower = minPower self.maxPower = maxPower self.lastPower = curPower self.powerSetPoint = None self.isEnabled = isEnabled self.units = units events.subscribe('save exposure settings', self.onSaveSettings) events.subscribe('load exposure settings', self.onLoadSettings)
def __init__(self, name, groupName, callbacks, exposureMode, trigHandler=None, trigLine=None): # Note we assume that cameras are eligible for experiments. deviceHandler.DeviceHandler.__init__(self, name, groupName, True, callbacks, depot.CAMERA) ## True if the camera is currently receiving images. self.isEnabled = False self._exposureMode = exposureMode self.wavelength = None self.dye = None # Set up trigger handling. if trigHandler and trigLine: h = trigHandler.registerDigital(self, trigLine) self.triggerNow = h.triggerNow else: softTrigger = self.callbacks.get('softTrigger', None) self.triggerNow = lambda: softTrigger if softTrigger: depot.addHandler( cockpit.handlers.imager.ImagerHandler( "%s imager" % name, "imager", {'takeImage': softTrigger})) #subscribe to save and load setting calls to enabvle saving and #loading of configurations. events.subscribe('save exposure settings', self.onSaveSettings) events.subscribe('load exposure settings', self.onLoadSettings)
def run(self): # For debugging purposes experiment.lastExperiment = self self.sanityCheckEnvironment() self.prepareHandlers() self.cameraToReadoutTime = dict([(c, c.getTimeBetweenExposures(isExact = True)) for c in self.cameras]) for camera, readTime in self.cameraToReadoutTime.items(): if type(readTime) is not decimal.Decimal: raise RuntimeError("Camera %s did not provide an exact (decimal.Decimal) readout time" % camera.name) for camera, func in self.camToFunc.items(): events.subscribe(events.NEW_IMAGE % camera.name, func) for exposureTime in self.exposureTimes: if self.shouldAbort: break self.camToImages = {} self.camToNumImagesReceived = {} self.camToLock = {} for camera in self.cameras: # Prepare a memory buffer to store images in. width, height = camera.getImageSize() self.camToImages[camera] = numpy.zeros((self.numExposures, height, width)) self.camToNumImagesReceived[camera] = 0 self.camToLock[camera] = threading.Lock() # Indicate any frame transfer cameras for reset at start of # table. if camera.getExposureMode() == cockpit.handlers.camera.TRIGGER_AFTER: self.cameraToIsReady[camera] = False self.table = self.generateActions(exposureTime) self.table.sort() self.examineActions() self.table.sort() self.table.enforcePositiveTimepoints() self.lastMinuteActions() self.doneReceivingThread = threading.Thread(target = self.waiter) self.doneReceivingThread.start() self.execute() if self.shouldAbort: break # Wait until it's been a short time after the last received image. self.doneReceivingThread.join() progress = cockpit.gui.progressDialog.ProgressDialog("Processing images", "Processing images for exposure time %.4f" % exposureTime, parent = None) self.processImages(exposureTime) progress.Destroy() for camera, func in self.camToFunc.items(): events.unsubscribe(events.NEW_IMAGE % camera.name, func) self.save() self.showResults() self.cleanup()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) ## Whether or not to draw the mosaic tiles self.shouldDrawMosaic = True ## True if we're in the processing of changing the soft motion limits. self.amSettingSafeties = False ## Position the mouse first clicked when setting safeties, or None if # we aren't setting safeties. self.firstSafetyMousePos = None ## Last seen mouse position self.lastMousePos = [0, 0] primitive_specs = wx.GetApp().Config['stage'].getlines('primitives', []) self._primitives = [Primitive.factory(spec) for spec in primitive_specs] hardLimits = cockpit.interfaces.stageMover.getHardLimits() self.minX, self.maxX = hardLimits[0] self.minY, self.maxY = hardLimits[1] ## X extent of the stage, in microns. stageWidth = self.maxX - self.minX ## Y extent of the stage, in microns. stageHeight = self.maxY - self.minY ## Max of X or Y stage extents. self.maxExtent = max(stageWidth, stageHeight) ## X and Y view extent. if stageHeight > stageWidth: self.viewExtent = 1.2 * stageHeight self.viewDeltaY = stageHeight * 0.1 else: self.viewExtent = 1.05 * stageWidth self.viewDeltaY = stageHeight * 0.05 # Push out the min and max values a bit to give us some room around # the stage to work with. In particular we need space below the display # to show our legend. self.centreX = ((self.maxX - self.minX) / 2) + self.minX self.centreY = ((self.maxY - self.minY) / 2) + self.minY self.minX = self.centreX - self.viewExtent / 2 self.maxX = self.centreX + self.viewExtent / 2 self.minY = self.centreY - self.viewExtent / 2 - self.viewDeltaY self.maxY = self.centreY + self.viewExtent / 2 - self.viewDeltaY ## Size of text to draw. I confess I don't really understand how this # corresponds to anything, but it seems to work out. self.textSize = .004 self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_LEFT_UP, self.OnLeftClick) self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDoubleClick) self.Bind(wx.EVT_RIGHT_UP, self.OnRightClick) self.Bind(wx.EVT_RIGHT_DCLICK, self.OnRightDoubleClick) # Bind context menu event to None to prevent main window context menu # being displayed in preference to our own. self.Bind(wx.EVT_CONTEXT_MENU, lambda event: None) events.subscribe("soft safety limit", self.onSafetyChange) events.subscribe('objective change', self.onObjectiveChange) self.SetToolTip(wx.ToolTip("Left double-click to move the stage. " + "Right click for gotoXYZ and double-click to toggle displaying of mosaic " + "tiles."))
def __init__(self, name="dummy XY stage", config={}): super().__init__(name, config) # List of 2 doubles indicating our X/Y position. self.curPosition = [1000, 1000] events.subscribe(events.USER_ABORT, self.onAbort) # Is this device in use? self.active = False self.deviceType = "stage positioner" self.axes = [0, 1]
def startCollecting(self): for camera in self.cameras: def func(data, timestamp, camera=camera): return self.onImage(self.cameraToIndex[camera], data, timestamp) self.lambdas.append(func) events.subscribe(events.NEW_IMAGE % camera.name, func) self.minMaxVals.append((float('inf'), float('-inf'))) events.subscribe(events.USER_ABORT, self.onAbort) self.statusThread.start()
def exposureMode(self, triggerType): """Set exposure mode. If the device set a softTrigger handler, subscribe to "dummy take image" if exposureMode is TRIGGER_SOFT, otherwise unsubscribe.""" self._exposureMode = triggerType softTrigger = self.callbacks.get('softTrigger', None) events.unsubscribe("dummy take image", softTrigger) if softTrigger: events.subscribe("dummy take image", softTrigger)
def finalizeInitialization(self): if self.excitation: self.setExMode(self.excitation[0]) self.temperatureConnection.connect(self.receiveTemperatureData) # self.setStageMode('Inverted') #set default emission path #IMD 20170316 comment out as this is a hack for OMXT # self.setDetMode('w/o AO & 209 nm pixel size') #Subscribe to objective change to map new detector path to new pixel sizes via fake objective events.subscribe('objective change', self.onObjectiveChange)
def __init__(self, parent, size, id=-1, *args, **kwargs): super().__init__(parent, id, size=size, *args, **kwargs) ## WX context for drawing. self.context = wx.glcanvas.GLContext(self) ## Whether or not we have done some one-time-only logic. self.haveInitedGL = False ## Whether or not we should try to draw self.shouldDraw = True ## Font for drawing text try: self.font = ftgl.TextureFont(cockpit.gui.FONT_PATH) self.font.setFaceSize(18) except Exception as e: print("Failed to make font:", e) ## X values below this are off the canvas. We leave it up to children # to fill in proper values for these. self.minX = 0 ## X values above this are off the canvas self.maxX = 1000 ## Y values below this are off the canvas self.minY = 0 ## Y values above this are off the canvas self.maxY = 1000 ## (X, Y, Z) vector describing the stage position as of the last # time we drew ourselves. We need this to display motion deltas. self.prevStagePosition = numpy.zeros(3) ## As above, but for the current position. self.curStagePosition = numpy.zeros(3) ## Event used to indicate when drawing is done, so we can update # the above. self.drawEvent = threading.Event() ##objective offset info to get correct position and limits self.objective = depot.getHandlersOfType(depot.OBJECTIVE)[0] self.listObj = list(self.objective.nameToOffset.keys()) self.listOffsets = list(self.objective.nameToOffset.values()) self.offset = self.objective.getOffset() ## Boolean to just force a redraw. self.shouldForceRedraw = False ## Thread that ensures we don't spam redisplaying ourselves. self.redrawTimerThread = threading.Thread(target=self.refreshWaiter, name="macrostage-refresh") self.redrawTimerThread.start() self.Bind(wx.EVT_PAINT, self.onPaint) self.Bind(wx.EVT_SIZE, lambda event: event) self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: event) # Do nothing, to avoid flashing events.subscribe(events.STAGE_POSITION, self.onMotion) events.subscribe("stage step index", self.onStepIndexChange)
def __init__(self, device, parent=None, handler=None): wx.Frame.__init__(self, parent, wx.ID_ANY, style=wx.FRAME_FLOAT_ON_PARENT) self.device = device self.SetTitle("%s settings" % device.name) self.settings = {} self.current = {} self.handler = handler #self.handler.addListener(self) #self.panel = wx.Panel(self, wx.ID_ANY, style=wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.VERTICAL) self.grid = wx.propgrid.PropertyGrid( self, style=wx.propgrid.PG_SPLITTER_AUTO_CENTER) self.grid.SetColumnProportion(0, 2) self.grid.SetColumnProportion(1, 1) self.populateGrid() self.Bind(wx.propgrid.EVT_PG_CHANGED, self.onPropertyChange) sizer.Add(self.grid, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALIGN_TOP) sizer.AddSpacer(2) buttonSizer = wx.BoxSizer(wx.HORIZONTAL) #saveButton = wx.Button(self, id=wx.ID_SAVE) #saveButton.SetToolTip(wx.ToolTip("Save current settings as defaults.")) #saveButton.Bind(wx.EVT_BUTTON, self.onSave) #buttonSizer.Add(saveButton, 0, wx.ALIGN_RIGHT, 0, 0) okButton = wx.Button(self, id=wx.ID_OK) okButton.Bind(wx.EVT_BUTTON, self.onClose) okButton.SetToolTip( wx.ToolTip("Apply settings and close this window.")) buttonSizer.Add(okButton, 0, wx.ALIGN_RIGHT) cancelButton = wx.Button(self, id=wx.ID_CANCEL) cancelButton.Bind(wx.EVT_BUTTON, self.onClose) cancelButton.SetToolTip( wx.ToolTip("Close this window without applying settings.")) buttonSizer.Add(cancelButton, 0, wx.ALIGN_RIGHT) applyButton = wx.Button(self, id=wx.ID_APPLY) applyButton.SetToolTip(wx.ToolTip("Apply these settings.")) applyButton.Bind(wx.EVT_BUTTON, lambda evt: self.device.updateSettings(self.current)) buttonSizer.Add(applyButton, 0, wx.ALIGN_RIGHT) sizer.Add(buttonSizer, 0, wx.ALIGN_CENTER, 0, 0) self.SetSizerAndFit(sizer) self.SetMinSize((256, -1)) events.subscribe(events.SETTINGS_CHANGED % self.device, self.updateGrid) self.Bind(wx.EVT_SHOW, lambda evt: self.updateGrid())
def toggleSyncViews(self, event=None): self.syncViews = not self.syncViews if self.syncViews: events.subscribe( events.SYNCED_VIEW, self.setView, ) else: events.unsubscribe( events.SYNCED_VIEW, self.setView, )
def __init__(self, name, groupName, isEligibleForExperiments, callbacks, cameras, lights): super().__init__(name, groupName, isEligibleForExperiments, callbacks, depot.LIGHT_FILTER) self.cameras = cameras or [] self.lights = lights or [] self.lastFilter = None #subscribe to save and load setting calls to enabvle saving and #loading of configurations. events.subscribe('save exposure settings', self.onSaveSettings) events.subscribe('load exposure settings', self.onLoadSettings)
def __init__(self, images, title, *args, **kwargs): super().__init__(*args, **kwargs) self.images = images self.title = title ## Current image/pixel under examination. self.curViewIndex = numpy.zeros(5, dtype=numpy.int) ## Panel for holding UI widgets. self.panel = wx.Panel(self) self.panel.SetBackgroundColour(wx.WHITE) # Set up our UI -- just a collection of sliders to change the # image we show, plus some keyboard shortcuts. ## Maps axes to the Sliders for those axes. self.axisToSlider = dict() sizer = wx.BoxSizer(wx.VERTICAL) sliderSizer = wx.BoxSizer(wx.HORIZONTAL) for i, label in enumerate(['Wavelength', 'Time', 'Z']): if self.images.shape[i] > 1: # We need a slider for this dimension. sliderSizer.Add(self.makeSlider(i, label)) sizer.Add(sliderSizer) self.canvas = cockpit.gui.imageViewer.viewCanvas.ViewCanvas( self.panel, size=(self.images.shape[-1], self.images.shape[-2] + 40)) sizer.Add(self.canvas) self.panel.SetSizerAndFit(sizer) temp = wx.BoxSizer(wx.VERTICAL) temp.Add(self.panel) self.SetSizerAndFit(temp) self.Show() # For some reason, if we don't do this, then the first time we try # to access curViewIndex in self.setCurImage we get strange values. self.setCurImage() events.subscribe('image pixel info', self.onImagePixelInfo) self.Bind(wx.EVT_CLOSE, self.onClose) accelTable = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_NUMPAD_MULTIPLY, 1), (wx.ACCEL_NORMAL, wx.WXK_LEFT, 2), (wx.ACCEL_NORMAL, wx.WXK_RIGHT, 3), (wx.ACCEL_NORMAL, wx.WXK_UP, 4), (wx.ACCEL_NORMAL, wx.WXK_DOWN, 5)]) self.SetAcceleratorTable(accelTable) self.Bind(wx.EVT_MENU, self.onRescale, id=1) for id, delta in [(2, (0, -1)), (3, (0, 1)), (4, (-1, 0)), (5, (1, 0))]: self.Bind(wx.EVT_MENU, lambda event, delta=delta: self.shiftView(delta), id=id)
def __init__(self, name, groupName, settings=None, settingIndex=None, callbacks={}): super().__init__(name, groupName, False, callbacks, depot.DRAWER) self.settings = settings self.settingIndex = settingIndex ## List of ToggleButtons, one per setting. self.buttons = [] # Last thing to do is update UI to show default selections. events.subscribe('cockpit initialization complete', self.changeDrawer)
def __init__(self, name="dummy XY stage", config={}): config['primitives'] = \ "r 12500 6000 3000 3000\n" \ "c 5000 6000 3000\n" \ "c 20000, 6000, 3000\n" super(DummyMover, self).__init__(name, config) # List of 2 doubles indicating our X/Y position. self.curPosition = [1000, 1000] events.subscribe('user abort', self.onAbort) # Is this device in use? self.active = False self.deviceType = "stage positioner" self.axes = [0, 1]
def __init__(self, name, groupName, nameToPixelSize, nameToTransform, nameToOffset, nameToColour, nameToLensID, curObjective, callbacks = {}): super().__init__(name, groupName, False, {}, depot.OBJECTIVE) self.nameToPixelSize = nameToPixelSize self.nameToTransform = nameToTransform self.nameToOffset = nameToOffset self.nameToColour = nameToColour self.nameToLensID = nameToLensID self.curObjective = curObjective self.callbacks = callbacks ## List of ToggleButtons, one per objective. self.buttons = [] events.subscribe('save exposure settings', self.onSaveSettings) events.subscribe('load exposure settings', self.onLoadSettings)
def enable(self, camera): self.selector.SetLabel(camera.descriptiveName) self.selector.SetBackgroundColour(camera.color) self.selector.Refresh() self.curCamera = camera # NB the 512 here is the largest texture size our graphics card can # gracefully handle. self.canvas = cockpit.gui.imageViewer.viewCanvas.ViewCanvas( self.canvasPanel, size=(VIEW_WIDTH, VIEW_HEIGHT)) self.canvas.SetSize((VIEW_WIDTH, VIEW_HEIGHT)) self.canvas.resetView() # Subscribe to new image events only after canvas is prepared. events.subscribe(events.NEW_IMAGE % self.curCamera.name, self.onImage)
def __init__(self): ## Maps axis to the handlers for that axis, sorted by their range of # motion. self.axisToHandlers = depot.getSortedStageMovers() if set(self.axisToHandlers.keys()) != {0, 1, 2}: raise ValueError('stage mover requires 3 axis: X, Y, and Z') # FIXME: we should have sensible defaults. self._saved_top = userConfig.getValue('savedTop', default=3010.0) self._saved_bottom = userConfig.getValue('savedBottom', default=3000.0) ## XXX: We have a single index for all axis, even though each ## axis may have a different number of stages. While we don't ## refactor this assumption, we just make copies of the movers ## with the most precise movement (issues #413 and #415) self.n_stages = max([len(s) for s in self.axisToHandlers.values()]) for axis, stages in self.axisToHandlers.items(): stages.extend([stages[-1]] * (self.n_stages - len(stages))) ## Indicates which stage handler is currently under control. self.curHandlerIndex = 0 ## Maps Site unique IDs to Site instances. self.idToSite = {} # Compute the hard motion limits for each axis as the # summation of all limits for handlers on that axis. hard_limits = [None] * 3 for axis in range(3): lower = 0.0 upper = 0.0 # We need set() to avoid duplicated handlers, and we might # have duplicated handlers because of the hack to meet # cockpit requirements that all axis have the same number # of handlers (see comments on issue #413). for handler in set(self.axisToHandlers[axis]): handler_limits = handler.getHardLimits() lower += handler_limits[0] upper += handler_limits[1] hard_limits[axis] = (lower, upper) # Use a tuple to prevent changes to it, and assemble it like # this to enable static code analysis. self._hard_limits = (hard_limits[0], hard_limits[1], hard_limits[2]) ## Maps handler names to events indicating if those handlers # have stopped moving. self.nameToStoppedEvent = {} events.subscribe(events.STAGE_MOVER, self.onMotion) events.subscribe(events.STAGE_STOPPED, self.onStop)
def __init__(self, parent): wx.Frame.__init__(self, parent, title = "Status information", style = wx.RESIZE_BORDER | wx.CAPTION | wx.FRAME_NO_TASKBAR) self.panel = wx.Panel(self) ## Maps status light names to the lights themselves. Each light is # a ToggleButton instance. self.nameToLight = {} events.subscribe('new status light', self.onNewLight) events.subscribe('update status light', self.onNewStatus) # Some lights that we know we need. self.onNewLight('image count', '') self.onNewLight('device waiting', '') self.Show()
def __init__(self, parent, size, id=-1, *args, **kwargs): super().__init__(parent, id, size=size, *args, **kwargs) ## WX context for drawing. self.context = wx.glcanvas.GLContext(self) ## Whether or not we have done some one-time-only logic. self.haveInitedGL = False ## Whether or not we should try to draw self.shouldDraw = True ## Font for drawing text self.face = cockpit.gui.freetype.Face(18) ## X values below this are off the canvas. We leave it up to children # to fill in proper values for these. self.minX = 0 ## X values above this are off the canvas self.maxX = 1000 ## Y values below this are off the canvas self.minY = 0 ## Y values above this are off the canvas self.maxY = 1000 ## (X, Y, Z) vector describing the stage position as of the last # time we drew ourselves. We need this to display motion deltas. self.prevStagePosition = numpy.zeros(3) ## As above, but for the current position. self.curStagePosition = numpy.zeros(3) ## Event used to indicate when drawing is done, so we can update # the above. self.drawEvent = threading.Event() ## Boolean to just force a redraw. self.shouldForceRedraw = False ## Thread that ensures we don't spam redisplaying ourselves. self.redrawTimerThread = threading.Thread(target=self.refreshWaiter, name="macrostage-refresh", daemon=True) self.redrawTimerThread.start() self.Bind(wx.EVT_PAINT, self.onPaint) self.Bind(wx.EVT_SIZE, lambda event: event) self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: event) # Do nothing, to avoid flashing events.subscribe(events.STAGE_POSITION, self.onMotion) events.subscribe("stage step index", self.onStepIndexChange)