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 onPaint(self, event=None): if not self.shouldDraw: return try: if not self.haveInitedGL: self.initGL() self.haveInitedGL = True dc = wx.PaintDC(self) self.SetCurrent(self.context) width, height = self.GetClientSize() glViewport(0, 0, width, height) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) squareOffsets = [(0, 0), (0, 1), (1, 1), (1, 0)] stepSizes = cockpit.interfaces.stageMover.getCurStepSizes()[:2] # Draw hard stage motion limits hardLimits = cockpit.interfaces.stageMover.getHardLimits()[:2] # Rearrange limits to (x, y) tuples. hardLimits = list(zip(hardLimits[0], hardLimits[1])) stageHeight = abs(hardLimits[1][0] - hardLimits[1][1]) # Set up transform from stage to screen units glMatrixMode(GL_MODELVIEW) glLoadMatrixf(self.modelView()) #Loop over objective offsets to draw limist in multiple colours. for obj in self.listObj: offset = self.objective.nameToOffset.get(obj) colour = self.objective.nameToColour.get(obj) glLineWidth(4) if obj is not self.objective.curObjective: colour = (min(1, colour[0] + 0.7), min(1, colour[1] + 0.7), min(1, colour[2] + 0.7)) glLineWidth(2) glEnable(GL_LINE_STIPPLE) glLineStipple(3, 0xAAAA) glColor3f(*colour) glBegin(GL_LINE_LOOP) for (xIndex, yIndex) in squareOffsets: glVertex2f(hardLimits[xIndex][0] - offset[0], hardLimits[yIndex][1] + offset[1]) glEnd() glDisable(GL_LINE_STIPPLE) # Draw soft stage motion limits -- a dotted box, solid black # corners, and coordinates. If we're currently setting safeties, # then the second corner is the current mouse position. safeties = cockpit.interfaces.stageMover.getSoftLimits()[:2] x1, x2 = safeties[0] y1, y2 = safeties[1] if self.firstSafetyMousePos is not None: x1, y1 = self.firstSafetyMousePos x2, y2 = self.lastMousePos if x1 > x2: x1, x2 = x2, x1 if y1 > y2: y1, y2 = y2, y1 softLimits = [(x1, y1), (x2, y2)] # First the dotted green box. glEnable(GL_LINE_STIPPLE) glLineWidth(2) glLineStipple(3, 0x5555) glColor3f(0, 1, 0) glBegin(GL_LINE_LOOP) for (x, y) in [(x1, y1), (x1, y2), (x2, y2), (x2, y1)]: glVertex2f(x - offset[0], y + offset[1]) glEnd() glDisable(GL_LINE_STIPPLE) # Now the corners. glColor3f(0, 0, 0) glBegin(GL_LINES) for (vx, vy), (dx, dy) in [ (softLimits[0], (self.maxExtent * .1, 0)), (softLimits[0], (0, self.maxExtent * .1)), (softLimits[1], (-self.maxExtent * .1, 0)), (softLimits[1], (0, -self.maxExtent * .1)) ]: secondVertex = [vx + dx, vy + dy] glVertex2f(vx - offset[0], vy + offset[1]) glVertex2f(secondVertex[0] - offset[0], secondVertex[1] + offset[1]) glEnd() glLineWidth(1) # Now the coordinates. Only draw them if the soft limits aren't # the hard limits, to avoid clutter. if safeties != hardLimits: for i, (dx, dy) in enumerate([(4000, -700), (2000, 400)]): x = softLimits[i][0] y = softLimits[i][1] self.drawTextAt((x + dx, y + dy), "(%d, %d)" % (x, y), size=self.textSize * .75) glDisable(GL_LINE_STIPPLE) # Draw device-specific primitives. # This uses vertex buffers with vertices in stage co-ordinates, # so need to update the modelview matrix to render them in the # right place. glEnable(GL_LINE_STIPPLE) glLineStipple(1, 0xAAAA) glColor3f(0.4, 0.4, 0.4) primitives = cockpit.interfaces.stageMover.getPrimitives() for p in primitives: if p not in self.primitives: self.primitives[p] = Primitive.factory(p) self.primitives[p].render() glDisable(GL_LINE_STIPPLE) #Draw possibloe stage positions for current objective obj = self.objective.curObjective offset = self.objective.nameToOffset.get(obj) colour = self.objective.nameToColour.get(obj) glLineWidth(2) # Draw stage position motorPos = self.curStagePosition[:2] squareSize = self.maxExtent * .025 glColor3f(*colour) glBegin(GL_LINE_LOOP) for (x, y) in squareOffsets: glVertex2f( motorPos[0] - offset[0] + squareSize * x - squareSize / 2, motorPos[1] + offset[1] + squareSize * y - squareSize / 2) glEnd() # Draw motion crosshairs glColor3f(1, 0, 0) glBegin(GL_LINES) for i, stepSize in enumerate(stepSizes): if stepSize is None: # No step control along this axis. continue offset = [0, 0] offset[i] = stepSize glVertex2f(motorPos[0] - offset[0], motorPos[1] - offset[1]) glVertex2f(motorPos[0] + offset[0], motorPos[1] + offset[1]) glEnd() # Draw direction of motion delta = motorPos - self.prevStagePosition[:2] if sum(numpy.fabs(delta)) > macroStageBase.MIN_DELTA_TO_DISPLAY: self.drawArrow((motorPos[0] - self.offset[0], motorPos[1] + self.offset[1]), delta, (0, 0, 1), arrowSize=self.maxExtent * .1, arrowHeadSize=self.maxExtent * .025) glLineWidth(1) # The crosshairs don't always draw large enough to show, # so ensure that at least one pixel in the middle # gets drawn. glBegin(GL_POINTS) glVertex2f(motorPos[0] - self.offset[0], motorPos[1] + self.offset[1]) glEnd() # Draw scale bar glColor3f(0, 0, 0) glLineWidth(1) glBegin(GL_LINES) yOffset = self.minY + 0.9 * (self.viewDeltaY + 0.5 * (self.viewExtent - stageHeight)) glVertex2f(hardLimits[0][0], yOffset) glVertex2f(hardLimits[0][1], yOffset) # Draw notches in the scale bar every 1mm. for scaleX in range(int(hardLimits[0][0]), int(hardLimits[0][1]) + 1000, 1000): width = self.viewExtent * .015 if scaleX % 5000 == 0: width = self.viewExtent * .025 y1 = yOffset - width / 2 y2 = yOffset + width / 2 glVertex2f(scaleX, y1) glVertex2f(scaleX, y2) glEnd() glLineWidth(1) # Draw stage coordinates. Use a different color for the mover # currently under keypad control. coordsLoc = (self.maxX - self.viewExtent * .05, self.minY + self.viewExtent * .1) allPositions = cockpit.interfaces.stageMover.getAllPositions() curControl = cockpit.interfaces.stageMover.getCurHandlerIndex() for axis in [0, 1]: step = stepSizes[axis] if stepSizes[axis] is None: step = 0 positions = [p[axis] for p in allPositions] self.drawStagePosition( ['X:', 'Y:'][axis], positions, curControl, step, (coordsLoc[0], coordsLoc[1] - axis * self.textLineHeight), self.viewExtent * .25, self.viewExtent * .05, self.textSize) events.publish('macro stage xy draw', self) glFlush() self.SwapBuffers() # Set the event, so our refreshWaiter() can update # our stage position info. self.drawEvent.set() except Exception as e: cockpit.util.logger.log.error( "Exception drawing XY macro stage: %s", e) cockpit.util.logger.log.error(traceback.format_exc()) self.shouldDraw = False