How to replace the KeyListeners of the ROIManager?

What is wrong in the minimal example below in jython? I’m removing the KeyListeners of the RoiManager and adding mine instead but the old KeyListeners are persisting.

It seems that the issue had already been encountered.

I see that the RoiManager is adding 2 keylisteners here:

ImageJ ij = IJ.getInstance();
addKeyListener(ij);
list.addKeyListener(ij);

but I still do not understand what is happening.
Any idea?
Thanks

Minimal example:

import ij
from ij import IJ

from ij.plugin.frame import RoiManager
from java.awt.event import KeyEvent
from java.awt.event import KeyAdapter

class ListenToMyKey(KeyAdapter):
    def keyPressed(this, event):
        keycode = keyEvent.getKeyCode()
        if keycode == KeyEvent.VK_A:
        	IJ.log('A pressed')

def logKeyListeners():
	IJ.log('RoiManagerKeyListeners --- '
		+ str(manager.getKeyListeners())
		+ ', n = ' 
		+ str(len(manager.getKeyListeners())))

manager = RoiManager.getInstance()
if manager == None:
    manager = RoiManager()

myKeyListener = ListenToMyKey()

logKeyListeners()
map(manager.removeKeyListener, manager.getKeyListeners())
logKeyListeners()
manager.addKeyListener(myKeyListener)
logKeyListeners()

Log output:

RoiManagerKeyListeners --- array(java.awt.event.KeyListener, [ij.ImageJ[frame0,0,966,585x114,layout=java.awt.BorderLayout,title=(Fiji Is Just) ImageJ,resizable,normal]]), n = 1
RoiManagerKeyListeners --- array(java.awt.event.KeyListener), n = 0
RoiManagerKeyListeners --- array(java.awt.event.KeyListener, [org.python.proxies.__builtin__$ListenToMyKey$0@106a5248]), n = 1

1 Like

Maybe your problem is that in the RoiManager the KeyListener is also added to the list.
See line 122 in RoiManager

1 Like

Thanks, that’s what I mentioned in the code extract above (list.addKeyListener(ij);).

What I did not mention though is that I also tried to remove the keyListeners from the List and it did not work either.

In the code below I’m removing all listeners I can find in the docs., still not working. The only thing that “works” is the removeAll() but as it says, it removes everything…

Otherwise I see that all buttons receive a KeyListener too here b.addKeyListener(IJ.getInstance());, but I do not have access to the buttons for a removal.

The scrollPane probably inherits the listeners from the List here
JScrollPane scrollPane = new JScrollPane(list, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);.

Maybe the solution would be to have access to the keyListeners of the scrollPane and of the components of the Panel.

import ij
from ij import IJ

from ij.plugin.frame import RoiManager
from java.awt.event import KeyEvent
from java.awt.event import KeyAdapter

class ListenToMyKey(KeyAdapter):
    def keyPressed(this, keyEvent):
        keycode = keyEvent.getKeyCode()
        if keycode == KeyEvent.VK_A:
        	IJ.log('A pressed')

def logKeyListeners(obj):
	IJ.log('KeyListeners of ' + str(obj) + ' '
		+ str(manager.getKeyListeners())
		+ ', n = ' 
		+ str(len(manager.getKeyListeners())))

manager = RoiManager.getInstance()
if manager == None:
    manager = RoiManager()

myKeyListener = ListenToMyKey()

logKeyListeners(manager)
map(manager.removeKeyListener, manager.getKeyListeners())
logKeyListeners(manager)
manager.addKeyListener(myKeyListener)
logKeyListeners(manager)

theList = manager.getList()
logKeyListeners(theList)
map(theList.removeKeyListener, theList.getKeyListeners())
logKeyListeners(theList)
theList.addKeyListener(myKeyListener)
logKeyListeners(theList)

map(
	manager.removeWindowFocusListener,
	manager.getWindowFocusListeners())
map(
	manager.removeWindowListener,
	manager.getWindowListeners())
map(
	manager.removeWindowStateListener,
	manager.getWindowStateListeners())
map(
	manager.removeContainerListener,
	manager.getContainerListeners())
map(
	manager.removeComponentListener,
	manager.getComponentListeners())
map(
	manager.removeFocusListener,
	manager.getFocusListeners())
map(
	manager.removeHierarchyBoundsListener,
	manager.getHierarchyBoundsListeners())
map(
	manager.removeHierarchyListener,
	manager.getHierarchyListeners())
map(
	manager.removeInputMethodListener,
	manager.getInputMethodListeners())
map(
	manager.removeMouseListener,
	manager.getMouseListeners())
map(
	manager.removeMouseMotionListener,
	manager.getMouseMotionListeners())
map(
	manager.removeMouseWheelListener,
	manager.getMouseWheelListeners())
map(
	manager.removePropertyChangeListener,
	manager.getPropertyChangeListeners())
	
#manager.removeAll()

Thanks for giving it a thought

Sorry for still :sleeping: when replying.

From my view this can’t work because getList() delivers a copy of the list.

and you don’t have access to the list.

Can’t you derive a new class from the RoiManager and exchange the Listener in there?

BTW, what is your target?

Thanks again for looking at this.

Oh yes, good point.

Yes.

Hmm… I am not sure about how I would do that.

I am making a GUI to annotate images with ROIs. The task is quite narrow and I am relying on keyboard shortcuts to optimize the user experience. It works well, the only issue is that upon focusing in the RoiManager window, the shortcuts of the RoiManager start being listened to.

The only thing I am using in the RoiManager window is the List pane, not the buttons. Maybe I should hide the RoiManager window and display my own custom list. There must be a way to do that.

A constraint I am sticking to is that it should remain within a single jython script.

Here is another idea.
Since the RoiManager uses the ij Listeners you can adapt the IJ key behaviour as needed.
How does that work?

  • In your ImageJ folder you find the ij.jar.
  • Open this file as a zip-archive e.g. with the 7-zip software.
  • Copy the fileIJ_Props.txt to a local folder.
  • In this txt file you find the line
    selection24=“Add to Manager[t]”,ij.plugin.Selection(“add”)
  • modify this line to comment out the key [t] behavior in the RoiManager to
    #selection24=“Add to Manager[t]”,ij.plugin.Selection(“add”)
  • save the file
  • exchange the file inside ij.jar with the modified one
  • Restart ImageJ

Does this work?

Hello Thomas -

Have you tried the removeKeyListener() method of RoiManager's
Component superclass?

(I haven’t tried it – just a thought.)

Thanks, mm

It successfully removes the standard behavior, but my listener does not get activated.

That is not the complete solution, but that was the right idea:

And this was wrong from me:

I actually do have access to all buttons and panes through the getComponents() function. The trick is that I should replace the keyListeners of all components (which requires to go down to the components of the components of the components).

Thanks @phaub and @mountain_man for your help.

Working minimal example below.

import ij
from ij import IJ

from ij.plugin.frame import RoiManager
from java.awt.event import KeyEvent
from java.awt.event import KeyAdapter

class ListenToMyKey(KeyAdapter):
    def keyPressed(this, keyEvent):
        keycode = keyEvent.getKeyCode()
        if keycode == KeyEvent.VK_A:
        	IJ.log('A pressed')

manager = RoiManager.getInstance()
if manager == None:
    manager = RoiManager()

myKeyListener = ListenToMyKey()

for id0,comp0 in enumerate(manager.getComponents()):
	print '-', id0, comp0, '\n'
	for kl in comp0.getKeyListeners():
		comp0.removeKeyListener(kl)
	comp0.addKeyListener(myKeyListener)		
	for id1,comp1 in  enumerate(comp0.getComponents()):
		print '--', id1, comp1, '\n'
		for kl in comp1.getKeyListeners():
			comp1.removeKeyListener(kl)
		comp1.addKeyListener(myKeyListener)	
		try:
			for id2,comp2 in  enumerate(comp1.getComponents()):
				print '---', id2, comp2, '\n'
				for kl in comp2.getKeyListeners():
					comp2.removeKeyListener(kl)
				comp2.addKeyListener(myKeyListener)
		except:
			pass
1 Like