# **** Import Section **** # # This section includes all the import files needed for the script. # The first set of imports are jython specific, the second set are # java/nemo specific (but the order is not important) from time import * from string import * from nemo.pdi import * from nemo.experiment import GenericDataPoint # Device interface objects buttonBox = 0 display = 0 # The data type dt = 0 dataSet = None # This is a class. It contains variables and methods for a common purpose. # This particualr class, called ExampleClass, has methods for displaying # various types of Graphics and for logging data. class ExampleClass: # The __init__ method is called when the class is instatiated (when # an instance of this class is created, as in: # myClass = ExampleClass() # This is a good place to initialize any variables necessary. # # The first argument for each method in a class should be called 'self'. # 'self' is basically a self-reference, referring to this instance of # a class. def __init__(self, aDisplayHandle, aDataType): self.displayHandle = aDisplayHandle self.dataType = aDataType # Create several graphics for later use and store them in class # variables. self.rectangle1 = self.createRectangle(40, 35, 20, 30) self.rectangle1.name = self.rectangle1.name + '-1' self.rectangle1Showing = 0 self.rectangle2 = self.createRectangle(62, 35, 10, 20) self.rectangle2.name = self.rectangle2.name + '-2' self.rectangle2Showing = 0 self.circle = self.createCircle() self.circleShowing = 0 self.image = self.createImage() self.imageShowing = 0 self.line = self.createLine() self.lineShowing = 0 self.text = self.createText() # This variable will be true when the task is running. self.running = 0 # Create a Rectangle Graphic using the given (x,y) position and (w,h) dimensions # and then return it to the caller. def createRectangle(self, x, y, w, h): rectangle = GraphicFactory.createGraphic(GraphicFactory.FILL_RECTANGLE) rectangle.name = 'rectangle example' rectangle.description = 'a solid red rectangle' # Specify the RGB components of the color (red). rectangle.red = 255 rectangle.green = 0 rectangle.blue = 0 # Specify the position and size. rectangle.x = str(x) rectangle.y = str(y) rectangle.w = str(w) rectangle.h = str(h) # We can return any variable from a method, without having to specify # a return type in the method sgnature. Here we return the rectangle # Graphic. return rectangle # Create a Circle Graphic and then return it to the caller. def createCircle(self): circle = GraphicFactory.createGraphic(GraphicFactory.FILL_CIRCLE) circle.name = 'circle example' circle.description = 'a solid blue circle' # Specify the RGB components of the color (blue). circle.red = 0 circle.green = 0 circle.blue = 255 # Specify the position and diameter. circle.x = '45' circle.y = '0' circle.diameter = '10' # Here we return the Circle Graphic. return circle # Create a Text Graphic and then return it to the caller. def createText(self): text = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) text.name = 'text example' text.description = 'sample text' # Specify the RGB components of the color (white). text.red = 255 text.green = 255 text.blue = 255 text.alpha = 50 # This is for transparency # Specify the position. text.x = '5' text.y = '88' # Specify the Font name. Note that the font you specify is not guaranteed # to exist on the machine that runs the task. text.fontName = 'Arial' # Text can have the following styles: NORMAL (default), BOLD, ITALIC. Create # combinations by using the bitwise 'OR' of two or more constants together. text.fontStyle = text.BOLD | text.ITALIC # Specify a bounding box for the text. The text is guaranteed to be sized # such that it will fit into the bounding box. text.fontWidth = '90' text.fontHeight = '10' # The font will not be stretched to fit the boudning box exactly, so more # than likely there will be padding. By default, the padding is measured # so that the text will be centered within the boudning box. However, # you can specify the alignment of the text: ALIGN_LEFT, ALIGN_RIGHT, and # ALIGN_CENTER (default). text.alignment = text.ALIGN_CENTER # Specify the actual text to appear in the Graphic. text.text = 'enter graphic key or Q to quit' # Here we return the Text Graphic. return text # Create a Line Graphic and then return it to the caller. def createLine(self): line = GraphicFactory.createGraphic(GraphicFactory.DRAW_LINE) line.name = 'line example' line.description = 'an orange line' # Specify the RGB components of the color (orange). line.red = 255 line.green = 128 line.blue = 0 # Specify the positions of the endpoints of the line. line.x1 = '32' line.y1 = '17' line.x2 = '25' line.y2 = '17' # Here we return the Line Graphic. return line # Create an image Graphic and then return it to the caller. def createImage(self): image = GraphicFactory.createGraphic(GraphicFactory.DRAW_IMAGE) image.name = 'image example' image.description = 'a self-contained image' # Specify the position. image.x = '75' image.y = '75' # Specify a bounding box for the size of the image. If no width # or height is specified, the native image size is used. This # bounding box works like the text Graphic bounding box. The # aspect ratio is locked, so the image retains proper proportions. image.w = '25' image.h = '25' # There are two ways to specify the image contents. The first way # stores the image bytes in the Graphic object. We use the task # handle to request the data from NEMO: image.imageBytes = task.getResource('myImage.jpg') # The second way to specify the image contents is to use the resource # architecture. A resource is some file that this script has acces to # on the server. The script can request that the resource be sent to the # client before any Graphic actually needs it. At some point later, a # Graphic object can refer to the resource by name to use it. Assuming # that the script already sent the resource called 'myImage.jpg' to the # cient, this is how an image Graphic uses a resource: image.isResource = 1 image.imageName = 'myImage.jpg' # Note that only one of the above two methods needs to be used for a # given image Graphic. # Here we return the image Graphic. return image # This method takes a data point as a paramter and tries to log it to the # data logger defined in this class. def logDataPoint(self, aDataPoint): # Make sure the logger has been initialized before trying to # access it. if (self.dataLogger <> None): self.dataLogger.logDataPoint(aDataPoint) # Set the graphics mode to show. Send a graphic to the client. The Graphic # contains a text instruction. def run(self): # Start a Run, marked by the given name. task.startRun('My Run Name') # Start a Data Logger for the current Run using the given Data Type name # and client. Each client will have its own Data Logger. self.dataLogger = task.startDataSet(self.dataType.getName(), 0) # Set the running mode to 1 self.running = 1 # Show the graphics for client 0. self.displayHandle.showGraphics(0) # Add a graphic, self.text, to the drawing list for client 0. self.displayHandle.addGraphic(0, self.text) # The task is finished. Close the Data Logger. Stop the run. Stop the experiment. def finish(self): # Set the running mode to 0 self.running = 0 # Close the Data Logger. task.endDataSet(self.dataLogger) # End the Run. task.endRun() # Set the Data Logger to None (which is equivalent to null). Future # attemps to log data will now fail. self.dataLogger = None # Clear the Graphics for client 0. self.displayHandle.clearGraphics(0) # Pause for 7 seconds. sleep(7) # Stop the experiment. NEMO will now store the data in the database. task.endExperiment() # This method responds to events from the display device. It takes the event # data and logs information about it in a readable form. def handleDisplayEvent(self, anEvent): # The Graphic Event contains variables for: # - the originating client, # - the time at which the event occurred, # - the type of the graphic event, and # - a list of the graphics in the drawing queue. client = anEvent.getClient() aTime = anEvent.getTime() type = anEvent.getType() # Store information about each Graphic into a string summary. graphicsList = "" graphics = anEvent.getGraphics() for graphicData in graphics: graphicsList = graphicsList + graphicData + " " # Remove the trailing white space graphicsList = graphicsList.strip() # Translate the graphic event type, which is an integer value, # into something readable for logging. typeText = "" if (type == GraphicalDisplay.HIDE): typeText = "HIDE" elif (type == GraphicalDisplay.SHOW): typeText = "SHOW" elif (type == GraphicalDisplay.DRAW): typeText = "ADD GRAPHIC" elif (type == GraphicalDisplay.CLEAR_GRAPHICS): typeText = "CLEAR ALL" elif (type == GraphicalDisplay.ADD_GRAPHICS): typeText = "ADD GRAPHICS" elif (type == GraphicalDisplay.CREATE_GROUP): typeText = "ADD GRAPHIC GROUP" # Create a GenericDataPoint instance. gdp = GenericDataPoint() # Set the Data Type gdp.dt = self.dataType # Assign values to the fields of this data type # This data type has fields for the time and a description of what # happened (in string form). gdp.setField("time", str(aTime)) gdp.setField("event", str(typeText) + ":{" + graphicsList + "}") # Call the method in this class to log the data point. self.logDataPoint(gdp) # Reponds to a button press from the client. def handleButtonEvent(self, anEvent): # Only listen to button events when we're in running mode 1 if (not self.running): return # The Button Event contains variables for: # - the originating client, # - the time at which the key was pressed, and # - the key pressed. client = anEvent.getClient() pressTime = anEvent.getTime() key = anEvent.getData() # If, for some reason, there is no key identified, return without # doing anything. if (len(key) == 0): return # Verify that they button pressed is either a letter, a digit, or some # punctuation character. If it's not, then return without doing anything. if (not key[0] in (letters+digits+'`~!@#$%^&*()-=_+{}[]\\|;:\'\",<.>/?')): return # Based on which key is pressed, take various actions. if (key == 'Q' or key == 'q'): # The Q key i sused when the task is done. self.finish() elif (key == 'C' or key == 'c'): # The C key shows or hides the circle graphic. if (self.circleShowing): self.displayHandle.removeGraphic(0, self.circle.name) else: self.displayHandle.addGraphic(0, self.circle) self.circleShowing = not self.circleShowing elif (key == 'L' or key == 'l'): # The L key shows or hides the line graphic. if (self.lineShowing): self.displayHandle.removeGraphic(0, self.line.name) else: self.displayHandle.addGraphic(0, self.line) self.lineShowing = not self.lineShowing elif (key == '1'): # The 1 key shows or hides the rectangle1 graphic. if (self.rectangle1Showing): self.displayHandle.removeGraphic(0, self.rectangle1.name) else: self.displayHandle.addGraphic(0, self.rectangle1) self.rectangle1Showing = not self.rectangle1Showing elif (key == '2'): # The 2 key shows or hides the rectangle2 graphic. if (self.rectangle2Showing): self.displayHandle.removeGraphic(0, self.rectangle2.name) else: self.displayHandle.addGraphic(0, self.rectangle2) self.rectangle2Showing = not self.rectangle2Showing elif (key == 'I' or key == 'i'): # The I key shows or hides the image graphic. if (self.imageShowing): self.displayHandle.removeGraphic(0, self.image.name) else: self.displayHandle.addGraphic(0, self.image) self.imageShowing = not self.imageShowing elif (key == '0'): # The 0 key clears all the graphics. self.displayHandle.clearGraphics(0) self.displayHandle.addGraphic(0, self.text) self.lineShowing = 0 self.imageShowing = 0 self.circleShowing = 0 self.rectangle1Showing = 0 self.rectangle2Showing = 0 # This method responds to events from the scanner device. It takes the event # data and logs information about it in a readable form. def handleScannerEvent(self, anEvent): # The Scanner Event contains variables for: # - the originating client, # - the time at which the event occurred, and # - the type of the scanner event. client = event.getClient() aTime = event.getTime() aType = event.getType() # Translate the scanner event type, which is an integer value, # into something readable for logging. typeText = "" if (aType == Scanner.START_SCAN): typeText = "STARTED SCANNER" elif (aType == Scanner.STOP_SCAN): typeText = "STOPPED SCANNER" elif (aType == Scanner.GET_IMAGES): typeText = "IMAGES" else: typeText = "UNKNOWN TYPE" # Create the data point. gdp = GenericDataPoint() gdp.dt = self.dataType gdp.setField("time", str(aTime)) gdp.setField("event", str(typeText)) # Log the data point. self.logDataPoint(gdp) # **** fromDisplay Method **** # # This method is called from NEMO and is passed an # event when a graphic is displayed on the # screen. def fromDisplay(event): global exampleClassInstance exampleClassInstance.handleDisplayEvent(event) # **** fromButtonBox Method **** # # This method is called from NEMO and is passed an # event when a button is pressed on a button box. def fromButtonBox(event): global exampleClassInstance exampleClassInstance.handleButtonEvent(event) # **** fromScanner Method **** # # This method is called from NEMO and is passed an # event when the scanner performs an operation. def fromScanner(event): global exampleClassInstance exampleClassInstance.handleScannerEvent(event) # This method is the entry point. When the experiment starts, this method is called. def start(): # The global keyword signifies that the variable is defined in the global scope global exampleClassInstance # Send the resource with the given name to the specified client. task.sendResource(0, 'myImage.jpg') # Call the run method of our ExampleClass instance. exampleClassInstance.run() # # Any code that is not in a method or class is executed at the beginning-- # at the time when the script is first interpreted. (Note: you can make calls # to methods and classes, but only those that you call explicitly call will # be executed. # # Hence, the following code is executed automatically before the experiment starts. # # Specify the number of subjects this experiment requires. task.setExptSize(1) # Set the name of this experiment task.setExperimentName("Example Experiment") # Specify which function will be called when the expeirment starts. # Note: 'start' is the name of a function. This is not a call to the # 'start' funtion, just a reference to it (note that start is not # followed by it's own open and close parentheses- this means the # function isn't actually being called just referenced). task.setStartFunction(start) # Request a Data Type with the name 'example_data_type' from NEMO. dataType = task.getDataType('example_data_type') # Specify which devices this experiment will use. NEMO provides a method (addDevice) # to create the devices using the following parameters: # - name : the name of the device, which may be viewed by Operators # - class name : the name of the NEMO class for the desired device # - handler method : a reference to the method that will handle incoming data # from this device # The call to this NEMO method returns a Device Handle. This Device Handle can be # used to communicate to the device. # # Note: When specifying the class name of the device, we're using the logical # device, and the Operator at the client location will pick the corresponding # physical device. displayHandle = task.addDevice("Display", "nemo.pdi.GraphicalDisplay", fromDisplay) scanner = task.addDevice("Scanner", "nemo.pdi.Scanner", fromScanner) buttonBox = task.addDevice("ButtonBox", "nemo.pdi.ButtonBox", fromButtonBox) # Create an instance of the Class we defined above. exampleClassInstance = ExampleClass(displayHandle, dataType)