Handling keys in application

Hello,

What is the most proper way to handle keys in a main window of my app?

I managed to implement it successfully for some other window with TGLVContainer, which has a KeyPressed() signal. However TGFrame classes do not seem to have this signal. So, do I have to somehow implement HandleKey() virtual method in my Python class and handle it?

Hi,

Try to use TGFrame::AddInput() with [color=#0080FF]kKeyPressMask | kKeyReleaseMask[/color] as parameter. For example (in C++):

And then you can (for example) use signal/slots to handle the key pressed events

Cheers, Bertrand.

Thank you, however I am not fluent enough in event handling. Should it come now as a specific TGFrame event? Or should I create some event by myself? I cannot find use of this mechanism anywere in the docs/tutorials.

I managed to get the events connecting the window to ProcessedEvent(Even_t*) and AddInput(). For that I had to add to my custom dispatcher the function

	PyObject* Dispatch( Event_t *e)
	{
		cout << e->fCode << endl;
		return Dispatch(e);
	}

However, I think that connecting this method connects all the other events - in general my window takes forever to open. Probably I should pass non-keyboard events to normal analysis. If it is the true, how should I do that?

Hi,

I’m not sure I understand what you mean. Connecting a slot to a signal doesn’t prevent the event to be processed by the window (the ProcessedEvent signal is emitted after the event has been processed)
And you can check if the event is a key pressed event this way:

   if (event->fType == kGKeyPress)

Cheers, Bertrand.

OK, it was my mistake - I was using Dispatch() instead of DispatchVA() and causing an infinite loop. Is it documented anywhere?

Anyway, I have a more general problem. This work only if the window is in focus. Meaning, that if I hoover a mouse over a canvas that is element of the window, it does not work anymore. I would like it to happen for everything that happens in the window. One example is pressing “ctrl-s” to invoke save function. Is there a way?

I don’t understand what you are doing (i.e. why and where are you using Dispatch ?). Could you post a running piece of code showing what you’re trying to achieve?

[quote=“LeWhoo”]Anyway, I have a more general problem. This work only if the window is in focus. Meaning, that if I hoover a mouse over a canvas that is element of the window, it does not work anymore. I would like it to happen for everything that happens in the window. One example is pressing “ctrl-s” to invoke save function. Is there a way?[/quote]This is generally the Window manager which is controlling that. You can capture the keyboard (with gVirtualX->GrabKey()), but then you have to release it at some point…

Cheers, Bertrand.

Running example is as always difficult, because the code is big and complicated. But the taks I want to achieve is extremely simple:

  1. Invoke menus and other tasks with keyboard, like mentioned ctrl-s for saving (in my case saving the canvas to a file). This is the function that almost all GUI app have :slight_smile:
  2. When I press a keyboard cursor I want to change the Canvas image. However, this needs to work, similar to the ctrl-s example, regardless of where the mouse is - over the canvas, over the frames of the frame, etc. In my current implementation it doesn’t.

I use dispatch, because I just don’t know how to implement it :slight_smile: If I understand correctly, the only way to catch the event generated with keyboard after using AddInput() is to connect “ProcessedEvent” to the frame. I think ProcessedEvent is not directly handled by PyROOT, therefore I have to write my own dispatcher in C and call it from Python. In such a case, following an example for some other controls given by wlav, I need to use DispatchVA() to dispatch the event to my PyROOT function.

I welcome an easier solution. Actually, this is so basic task, that I hope an easier solution exists :slight_smile:

OK, so let’s try to solve your issues in a simple way :wink:

So simply add AddInput() and use gVirtualX->GrabKey(), like for example with ctrl-s:

yourFrame->AddInput(kKeyPressMask); gVirtualX->GrabKey(yourFrame->GetId(), gVirtualX->KeysymToKeycode(kKey_s), kKeyControlMask, on);

You can try to add gVirtualX->SetInputFocus(yourFrame->GetId());in addition to the method described above, but this will work only if the Window Manager allows it, and we cannot prevent other applications to steal the focus… There is not much we can do to change the way the Window Manager control the focus (but you can most probably change your WM settings)

There are several ways in C++, but I’m afraid I can’t really help on the Python side…

Cheers, Bertrand

Again, I tried to find on my own, but I failed. What happens adter GrabKey? How do I know that the key was pressed? I need to connect this GrabKey() somehow to my function, probably through a signal, but I don’t know what signal.

Here is a simple (but complete) example, and in Python! :wink::

[code]from ROOT import *

def PrintMessage( event ):
if event.fType == kGKeyPress:
print “PrintMessage() : key pressed!”

m = TPyDispatcher( PrintMessage )

class pMainFrame( TGMainFrame ):
def init( self, parent, width, height ):
TGMainFrame.init( self, parent, width, height )

  self.fButton = TGTextButton( self, 'E&xit Application', 10 )
  self.AddFrame( self.fButton, TGLayoutHints( kLHintsExpandX | kLHintsCenterY, 20, 20, 20, 20) )
  self.fQuitDispatch = TPyDispatcher( self.quit )
  self.fButton.Connect( 'Clicked()', 'TPyDispatcher', self.fQuitDispatch, 'Dispatch()' )

  self.fTextEntry = TGTextEntry( self, '' , 50)
  self.AddFrame( self.fTextEntry, TGLayoutHints( kLHintsExpandX | kLHintsCenterY, 20, 20, 20, 20) )

  self.AddInput( kKeyPressMask )
  gVirtualX.GrabKey(self.GetId(), gVirtualX.KeysymToKeycode(kKey_F3), kKeyControlMask, 1 )

  self.fEventDispatch = TPyDispatcher( self.EventSlot )
  self.Connect( 'ProcessedEvent(Event_t*)', 'TPyDispatcher', self.fEventDispatch, 'Dispatch(Event_t*)' )
  self.Connect( 'ProcessedEvent(Event_t*)', 'TPyDispatcher', m, 'Dispatch(Event_t*)' )

  self.MapSubwindows()
  self.Layout()
  self.MapWindow()
  self.Resize(150,100)

def del( self ):
self.Cleanup()

def HandleKey( self, event ):
print “pMainFrame::HandleKey() : key pressed!”

def EventSlot( self, event ):
if event.fType == kGKeyPress:
print “pMainFrame::EventSlot() : key pressed!”

def quit(self):
print 'Bye bye…'
self.CloseWindow()
gApplication.Terminate()
exit()

if name == ‘main’:
window = pMainFrame( gClient.GetRoot(), 200, 200 )

[/code]
Cheers, Bertrand.

Thanks! I saw that you posted two posts before the forum crash then they disappeared before I could use them, so thanks for reposting.

So you do exactly what I wanted to do at the beginning, except the “AddInput”. However, I failed, probably due to too old version of ROOT. On 5.34.15 I get:

Error in <TQObject::CheckConnectArgs>: slot TPyDispatcher::Dispatch(Event_t*) does not exist

in your sample and my attempts.

OK, it works in the latest ROOT version.

Is it easy to check when was handling of Dipatch(Even_t *) added to root sources?

Hi,

It was added (in v5-34-00-patches) on Tue, 17 Jun 2014 00:20:27 +0200 (15:20 -0700). See this commit

Cheers, Bertrand.

Thanks.