# @int (Label = "Number of categories") N_category ''' This script can be used to manually classify full images from a stack into N user-defined categories. A first window pops up to request the number of categories. A second window follows asking for the name and /character to use as a keyboard shortcut for those categories. NB : - Existing ImageJ shortcuts are deactivated - The keyboard mapping depends on the keyboard layout (Virtual Keycode VK used for the events), so it might give strange result with keyboard others than german ones (the best would be to catch up keyevent directly when assigning shortcuts) TO DO : Add mesaurement possibility ? The addValue was not working so well in this case. Duplicate to another code to try with the result table ''' from ij.gui import GenericDialog from ij import WindowManager from java.awt.event import KeyEvent, KeyAdapter from ij.measure import ResultsTable import ij.IJ as IJ ############### GUI : CATEGORY AND SHORTCUT DIALOG ############# Win = GenericDialog("Category name and keyboard shortcut") # Title of the window Win.enableYesNoCancel('OK','Cancel') # OK, Cancel (chosen) and Cancel button Win.hideCancelButton() # keep only one cancel button for i in range(N_category): Win.addStringField("Category: ","Category_"+str(i)) Win.addStringField("Keyboard Shortcut",str(i)) Win.addMessage("") # skip one line Win.showDialog() ################# After OK clicking ########### List_Category = [] List_Shortcut = [] DicoDigit = {i:j for i,j in zip('0123456789',range(96,106))} # create Char:KeyCode mapping for the numpad keystroke (since they do not return ASCII) DicoCategory = {} # Map KeyCode:Category # Recover fields from the formular if (Win.wasOKed()): for i in range(N_category): Cat = Win.getNextString() Shortcut_str = Win.getNextString().upper() # ImageJ is not case sensitive regarding ASCII code, always return the upper case value if len(Shortcut_str) != 1 : raise Exception('Shortcut should be a 1-character string') if Shortcut_str in '0123456789' : # for digits there will be 2 KeyCode for 1 category : 1 for the keycode from the numpad, a second for the keycode from the top row of digit on the keyboard KeyCode1 = ord(Shortcut_str) # Keycode for the digit generated from the top row of the keyboard KeyCode2 = DicoDigit[Shortcut_str] # Keycode for the digit generated via the numpad DicoCategory[KeyCode1] = Cat DicoCategory[KeyCode2] = Cat else : KeyCode = ord(Shortcut_str) DicoCategory[KeyCode] = Cat #print DicoCategory ################## KEYEVENTS ######### Table = ResultsTable() #Table.show("Classification") def doSomething(imp, keyEvent): # function called at each key pressed on an image imp (imp is an ImagePlus object) Key = keyEvent.getKeyCode() keyEvent.consume() # Prevent further propagation of the key event #print Key if Key in DicoCategory.keys() : # Check that the pressed key is one of the shortcuts #print Key Table.incrementCounter() # Add one additional row before filling it # recover the image or slice name (getTitle return the name for single image but not for stacks. Hence generic solution is to use Stack object works in bopth cases) Stack = imp.getStack() SliceName = Stack.getSliceLabel(imp.currentSlice) if SliceName is None: # the slice label can be empty sometime SliceName = 'Slice' + str(imp.currentSlice) else : SliceName = SliceName.split('\n',1)[0] Table.addValue("Image",SliceName) Table.addValue("Category",DicoCategory[Key]) Table.show('Classification') # Update table #Table.updateResults() # only for result table but then addValue does not work ! if imp.getStackSize() != 1 and imp.currentSlice != imp.getStackSize(): # if We have a stack and the current slice is not the last slice imp.setSlice(imp.currentSlice+1) imp.updateStatusbarValue() # update Z and pixel value (called by next slice so should do it too) # Bring back the focus to the image (otherwise the table is in the front and one need to reclick on the image) Id = imp.getID() IJ.selectWindow(Id) ## LISTENING CLASS class ListenToKey(KeyAdapter): def keyPressed(this, event): imp = event.getSource().getImage() # Get image on which we clicked doSomething(imp, event) # Call feedback function with source image and key that was typed listener = ListenToKey() # Create an instance of the listening class ie start listening to the event # Add our listener to each image for imp in map(WindowManager.getImage, WindowManager.getIDList()): win = imp.getWindow() if win is None: continue canvas = win.getCanvas() # Remove existing key listeners kls = canvas.getKeyListeners() map(canvas.removeKeyListener, kls) # Add our key listener canvas.addKeyListener(listener) # Optionally re-add existing key listeners # map(canvas.addKeyListener, kls)