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()
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 ) {}
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
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.
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.
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.
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?
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>')
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?
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.
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.