Home | News | Documentation | Download

TGListView signals

Hello,

I am trying TGListView to work in pyROOT (just to mention - there is no proper documentation how to use it and every mistake makes software crash…). I cannot receive a signal when clicked. Should print my messagem but does nothing:

class pDataFlowWindow(ROOT.TGMainFrame):
	# Handler of the list events
	def handle_list(self):
		print "dbl click"
	# # Constructor
	def __init__(self, parent, width, height):
		ROOT.TGMainFrame.__init__(self, parent, width, height)
		self.MainFrame = ROOT.TGVerticalFrame(self, width, height)
		self.AddFrame(self.MainFrame, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))
		
		self.eventlist = ROOT.TGListView(self.MainFrame, 600, 400)
		lo = ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY)
		self.eventlist_container = ROOT.TGLVContainer(self.eventlist, ROOT.kSunkenFrame)
		self.eventlist_container.Associate(self.MainFrame)
		self.eventlist.SetContainer(self.eventlist_container)

		# Event handling for the list
		self.list_dispatch = ROOT.TPyDispatcher(self.handle_list)
		self.eventlist.Connect("Clicked(TGLVEntry*, Int_t)", "TPyDispatcher", self.list_dispatch, "Dispatch()")

		# Set headers of the list	
		self.eventlist.SetHeaders(5)
		self.eventlist.SetHeader("Event #", ROOT.kTextLeft, ROOT.kTextLeft, 0)
		self.eventlist.SetHeader("Gen. packet type", ROOT.kTextLeft, ROOT.kTextLeft, 1)
		self.eventlist.SetHeader("Packet type", ROOT.kTextLeft, ROOT.kTextLeft, 2)
		self.eventlist.SetHeader("Time", ROOT.kTextLeft, ROOT.kTextLeft, 3)
		self.eventlist.SetHeader("Tree", ROOT.kTextLeft, ROOT.kTextLeft, 4)
	

		en = ROOT.TGLVEntry(self.eventlist_container, str(tree.GetTree().evt_number), ""))
		self.eventlist_container.AddItem(en)
		self.eventlist_container.SetViewMode(ROOT.EListViewMode.kLVDetails)		
		self.MainFrame.AddFrame(self.eventlist, lo)
		
		
		self.SetWindowName('Data flow display')
		self.MapSubwindows()
		self.Resize(self.GetDefaultSize())
		self.MapWindow()

What am I doing wrong?

I found out that TGContainer signals are proecessed for the TGLVContainer. So I can add:

self.eventlist_container.Connect("DoubleClicked(TGFrame*, Int_t)", "TPyDispatcher", self.list_dispatch, "Dispatch()")

However, apart from getting singal, I also get an error and probably inability to determine which item was clicked:

:0: RuntimeWarning: Cint::G__CallFunc::SetArgArray() too many arguments specified (2 expected 0)

Hi,

the RuntimeWarning comes from the fact that the connected method is given as “Dispatch()”, whereas it should be “Dispatch(TGFrame*, Int_t)”. Since TPyDispatcher does not have that pre-defined, one needs to be created. This is the easiest, I think:[code]$ cat mydispatcher.C
#include “TPyDispatcher.h”

#include “TGListView.h”

class MyDispatcher : public TPyDispatcher {
public:
MyDispatcher( PyObject* callable ) : TPyDispatcher( callable ) {}

public:
PyObject* Dispatch( TGFrame* o1, Int_t i1 ) {
return DispatchVA( “li”, o1, i1 );
}

ClassDef( MyDispatcher, 1 ); // my custom Python dispatcher class
};

ClassImp(MyDispatcher)[/code]
and then load it with:ROOT.gROOT.LoadMacro( "mydispatcher.C+" )
connect it with:self.list_dispatch = ROOT.MyDispatcher(self.handle_list) self.eventlist_container.Connect("DoubleClicked(TGFrame*, Int_t)", "MyDispatcher", self.list_dispatch, "Dispatch(TGFrame*, Int_t)")
and use it with: def handle_list(self, frame, val): print "dbl click", ROOT.BindObject(frame, "TGFrame"), val
HTH,
Wim

Thanks, I was afraid that I will need to invoke CINT and I guess it will slow-down the start up of the program… Two more questions:

  1. Why no signal is there for TGListView?
  2. Do you plan to add such missing definitions to TPyDispatcher?

Hi,

CINT is always in play, that’s how signal/slot works (also from compiled C++).

For 1.) is outside my expertise. Perhaps ask on the general support forurm?

For 2.) I can add these one-by-one as they come up, but to add all “missing definitions”, I’d need a list of all possible signatures that exists. I’m not sure there is such a list (maybe searching for “SIGNAL” in $ROOTSYS/include/*.h would do it), nor would such a list be static.

Cheers,
Wim

I am exactly refering to the one-by-one adding. I mean, do you plan to add for example this signal soon, so I could expect it in the new ROOT version?

A shame that there is no way to do it automatically…

One another problem that the object passed as TGFrame* is ‘int’. How can I transform it into TGFrame, or, better, to TGLVEntry?

Hi,

I bit the bullet and added all signatures of methods that have a “SIGNAL” comment to them, from a grep in my $ROOTSYS/include/*.h. I’m not able to check it all, so will see what comes of it.

The specific dispatch that you needed is there, but I wrote it slightly differently so that there’s no need for a BindObject call on the python side and auto-down-casting is done as well.

HTH,
Wim

That was done with ‘ROOT.BindObject(frame, “TGFrame”)’ (see the code I wrote above), but is no longer needed with the code I just committed to the repository.

Cheers,
Wim

Great, works, thanks! I somehow missed the bind object…

I am really curious about your adding all the signals in the new version. Unfortunately for a time being I need to maintain compatibility with slightly older versions of ROOT.

One another thing. I need to access either subnames of the TGLVEntry or an underlying data set with SetUserData. The first one seems to be inaccessible due to a lack of a proper method (waiting for the answer on the main support forum). The second one is then used by void* GetUserData(). How to convert this void* to anything that I can use in pyROOT?

Hi,

in v5-34, BindObject() will not work on a void*. (It does in ROOT6, where I’ve done a load of cleanup for void* handling, in particular to make both returns and argument passing more consistent.)

For v5-34 (and ROOT6), you can do the visually ugly, but functionally equivalent:ROOT.TPython.ObjectProxy_FromVoidPtr( GetUserData(), '<UserClassNameAsString>')

Cheers,
Wim

I am not certain what should be the if I set the user data with following code:

i=0;
a = np.array([i])
element[-1].SetUserData(a)
self.eventlist_container.AddItem(element[-1])

where element[-1] of course is TGLVEntry. Could you help?

Hi,

sorry, not following? Where did GetUserData() go in this example? The string must be the class name of whatever the void* is pointing to. (I was expecting the user data to be some kind of instance.)

In this example however, it is pointing to an array of doubles. I tried a couple of variations, but I don’t think that it can be done w/o a C++ helper function. Something like:double* cast_l2d(long* l) { return (double*)l; }. The result has a SetSize() method that can be used to patch up the size.

That said … if you have a the element already on the python side, why not add the extra data a as a data member to the python object?

Cheers,
Wim

To be honest… I don’t know how to add extra data to TGLVElement. My problem is that I need to get either one of the subnames, which is not possible yet, or associate element with a number, so than I can use this number when the element is clicked and get additional information from elsewhere (from a TTree). Therefore I tried to SetUserData to a number, which is an entry number for the TTree.

Hi,

if it is just a number that needs to be passed around, then use an array of longs (e.g. array.array(‘l’, [number])), as the void* result is represented as an array of longs (for lack of anything better), so it can be indexed and the first element of the returned result will be ‘number’ again.

HTH,
Wim

The problem is, that I get non-sense result. Maybe I am doing something wrong:

b = array.array('l', [10])
element[-1].SetUserData(b)

and than in handler of the signal:

entry = ROOT.BindObject(frame, "TGLVEntry")
if val==1: print "dbl click", entry.GetTitle(), entry.GetUserData()[0]

The value printed is for example 139708223433456

Hi,

that should work, assuming that there’s still a reference outstanding to ‘b’, to keep it alive. Is there?

Cheers,
Wim

No, it was lost. Thanks! So I was doing it correctly with numpy from the beginning, but introduced this stupid bug.