diff --git a/bindings/pyroot/pythonizations/doc/index.md b/bindings/pyroot/pythonizations/doc/index.md index 91ebc0931029f..8bbe4d2b015d2 100644 --- a/bindings/pyroot/pythonizations/doc/index.md +++ b/bindings/pyroot/pythonizations/doc/index.md @@ -1,4 +1,285 @@ \defgroup Pythonizations Python interface \brief Python-specific functionalities offered by ROOT -This page lists the so-called "pythonizations", that is those functionalities offered by ROOT for classes and functions which are specific to Python usage of the package and provide a more pythonic experience. \ No newline at end of file +This page lists the so-called "pythonizations", that is those functionalities offered by ROOT for classes and functions which are specific to Python usage of the package and provide a more pythonic experience. + +### Pythonization example + +This example shows how to use the `@pythonization` decorator to add extra +behaviour to C++ user classes that are used from Python via PyROOT. +Let's first define a new C++ class. In this tutorial, we will see how we can +"pythonize" this class, i.e. how we can add some extra behaviour to it to +make it more pythonic or easier to use from Python. +Note: In this example, the class is defined dynamically for demonstration +purposes, but it could also be a C++ class defined in some library or header. +For more information about loading C++ user code to be used from Python with +PyROOT, please see: +https://root.cern.ch/manual/python#loading-user-libraries-and-just-in-time-compilation-jitting + +~~~{.py} +ROOT.gInterpreter.Declare(""" +class MyClass {}; +""") +~~~ + +Next, we define a pythonizor function: the function that will be responsible +for injecting new behaviour in our C++ class `MyClass`. +To convert a given Python function into a pythonizor, we need to decorate it +with the @pythonization decorator. Such decorator allows us to define which +which class we want to pythonize by providing its class name and its +namespace (if the latter is not specified, it defaults to the global +namespace, i.e. '::'). +The decorated function - the pythonizor - must accept either one or two +parameters: +1. The class to be pythonized (proxy object where new behaviour can be +injected) +2. The fully-qualified name of that class (optional). +Let's see all this with a simple example. Suppose I would like to define how +`MyClass` objects are represented as a string in Python (i.e. what would be +shown when I print that object). For that purpose, I can define the following +pythonizor function. There are two important things to be noted here: +- The @pythonization decorator has one argument that specifies our target +class is `MyClass`. +- The pythonizor function `pythonizor_of_myclass` provides and injects a new +implementation for `__str__`, the mechanism that Python provides to define +how to represent objects as strings. This new implementation +always returns the string "This is a MyClass object". + +~~~{.py} +@pythonization('MyClass') +def pythonizor_of_myclass(klass): + klass.__str__ = lambda o : 'This is a MyClass object' +~~~ +Once we have defined our pythonizor function, let's see it in action. +We will now use the `MyClass` class for the first time from Python: we will +create a new instance of that class. At this moment, the pythonizor will +execute and modify the class - pythonizors are always lazily run when a given +class is used for the first time from a Python script. + +~~~{.py} +my_object = ROOT.MyClass() +~~~ + +Since the pythonizor already executed, we should now see the new behaviour. +For that purpose, let's print `my_object` (should show "This is a MyClass +object"). + +~~~{.py} +print(my_object) +~~~ + +The previous example is just a simple one, but there are many ways in which a +class can be pythonized. Typical examples are the redefinition of dunder +methods (e.g. `__iter__` and `__next__` to make your objects iterable from +Python). If you need some inspiration, many ROOT classes are pythonized in +the way we just saw; their pythonizations can be seen at: +https://github.com/root-project/root/tree/master/bindings/pyroot/pythonizations/pythonROOT/pythonizatio +The @pythonization decorator offers a few more options when it comes to +matching classes that you want to pythonize. We saw that we can match a +single class, but we can also specify a list of classes to pythonize. +The following code defines a couple of new classes: + +~~~{.py} +ROOT.gInterpreter.Declare(""" +namespace NS { + class Class1 {}; + class Class2 {}; +} +""") +~~~ + +Note that these classes belong to the `NS` namespace. As mentioned above, the +@pythonization decorator accepts a parameter with the namespace of the class +or classes to be pythonized. Therefore, a pythonizor that matches both classes +would look like this: + +~~~{.py} +@pythonization(['Class1', 'Class2'], ns='NS') +def pythonize_two_classes(klass): + klass.new_attribute = 1 +~~~ + +Both classes will have the new attribute: + +~~~{.py} +o1 = ROOT.NS.Class1() +o2 = ROOT.NS.Class2() +print("Printing new attribute") +for o in o1, o2: + print(o.new_attribute) +~~~ + +In addition, @pythonization also accepts prefixes of classes in a certain +namespace in order to match multiple classes in that namespace. To signal that +what we provide to @pythonization is a prefix, we need to set the `is_prefix` +argument to `True` (default is `False`). +A common case where matching prefixes is useful is when we have a templated +class and we want to pythonize all possible instantiations of that template. +For example, we can pythonize the `std::vector` (templated) class like so: + +~~~{.py} +@pythonization('vector<', ns='std', is_prefix=True) +def vector_pythonizor(klass): + # first_elem returns the first element of the vector if it exists + klass.first_elem = lambda v : v[0] if v else None +~~~ + +Since we defined a prefix to do the match, the pythonization will be applied +both if we instantiate e.g. a vector of integers and a vector of doubles. + +~~~{.py} +v_int = ROOT.std.vector['int']([1,2,3]) +v_double = ROOT.std.vector['double']([4.,5.,6.]) +print("First element of integer vector: {}".format(v_int.first_elem())) +print("First element of double vector: {}".format(v_double.first_elem())) +~~~ + +These are some examples of combinations of prefixes and namespaces and the +corresponding classes that they match: +- '' : all classes in the global namespace. +- '', ns='NS1::NS2' : all classes in the `NS1::NS2` namespace. +- 'Prefix' : classes whose name starts with `Prefix` in the global namespace. +- 'Prefix', ns='NS' : classes whose name starts with `Prefix` in the `NS` +namespace +Moreover, a pythonizor function can have a second optional parameter that +contains the fully-qualified name of the class being pythonized. This can be +useful e.g. if we would like to do some more complex filtering of classes in +our pythonizor, for instance using regular expressions. + +~~~{.py} +@pythonization('pair<', ns='std', is_prefix=True) +def pair_pythonizor(klass, name): + print('Pythonizing class ' + name) +~~~ + +The pythonizor above will be applied to any instantiation of `std::pair` - we +can see this with the print we did inside the pythonizor. +Note that we could use the `name` parameter to e.g. further filter which +particular instantiations we would like to pythonize. + +~~~{.py} +p1 = ROOT.std.pair['int','int'](1,2) # prints 'Pythonizing class std::pair' +p2 = ROOT.std.pair['int','double'](1,2.) # prints 'Pythonizing class std::pair' +~~~ + +Note that, to pythonize multiple classes in different namespaces, we can +stack multiple @pythonization decorators. For example, if we define these +classes: + +~~~{.py} +ROOT.gInterpreter.Declare(""" +class FirstClass {}; +namespace NS { + class SecondClass {}; +} +""") +~~~ + +We can pythonize both of them with a single pythonizor function like so: + +~~~{.py} +@pythonization('FirstClass') +@pythonization('SecondClass', ns='NS') +def pythonizor_for_first_and_second(klass, name): + print('Executed for class ' + name) +~~~ + +If we now access both classes, we should see that the pythonizor runs twice. + +~~~{.py} +f = ROOT.FirstClass() +s = ROOT.NS.SecondClass() +~~~ + +So far we have seen how pythonizations can be registered for classes that +have not been used yet. We have discussed how, in that case, the pythonizor +functions are executed lazily when their target class/es are used for the +first time in the application. +However, it can also happen that our target class/es have already been +accessed by the time we register a pythonization. In such a scenario, the +pythonizor is applied immediately (at registration time) to the target +class/es +Let's see an example of what was just explained. We will define a new class +and immediately create an object of that class. We can check how the object +still does not have a new attribute `pythonized` that we are going to inject +in the next step. + +~~~{.py} +ROOT.gInterpreter.Declare(""" +class MyClass2 {}; +""") +o = ROOT.MyClass2() +try: + print(o.pythonized) +except AttributeError: + print("Object has not been pythonized yet!") +~~~ + +After that, we will register a pythonization for `MyClass2`. Since the class +has already been used, the pythonization will happen right away. + +~~~{.py} +@pythonization('MyClass2') +def pythonizor_for_myclass2(klass): + klass.pythonized = True +~~~ + +Now our object does have the `pythonized` attribute: + +~~~{.py} +print(o.pythonized) # prints True +~~~ + +### Pythonization printing example +This example illustrates the pretty printing feature of PyROOT, which reveals +the content of the object if a string representation is requested, e.g., by +Python's print statement. The printing behaves similar to the ROOT prompt +powered by the C++ interpreter cling. +Create an object with PyROOT + +~~~{.py} +obj = ROOT.std.vector("int")(3) +for i in range(obj.size()): + obj[i] = i +~~~ + +Print the object, which reveals the content. Note that `print` calls the special +method `__str__` of the object internally. + +~~~{.py} +print(obj) +~~~ + +The output can be retrieved as string by any function that triggers the `__str__` +special method of the object, e.g., `str` or `format`. + +~~~{.py} +print(str(obj)) +print("{}".format(obj)) +~~~ + +Note that the interactive Python prompt does not call `__str__`, it calls +`__repr__`, which implements a formal and unique string representation of +the object. + +~~~{.py} +print(repr(obj)) +obj +~~~ + +The print output behaves similar to the ROOT prompt, e.g., here for a ROOT histogram. + +~~~{.py} +hist = ROOT.TH1F("name", "title", 10, 0, 1) +print(hist) +~~~ + +If cling cannot produce any nice representation for the class, we fall back to a +"" format, which is what `__repr__` returns + +~~~{.py} +ROOT.gInterpreter.Declare('class MyClass {};') +m = ROOT.MyClass() +print(m) +print(str(m) == repr(m)) +~~~ \ No newline at end of file diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/__init__.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/__init__.py index 84c4784792133..7088720886afc 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/__init__.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/__init__.py @@ -55,6 +55,7 @@ def pythonization(class_name, ns='::', is_prefix=False): Returns: function: function that receives the user-defined function and decorates it. + ''' # Type check and parsing of target argument. diff --git a/documentation/doxygen/makeNotebooks.sh b/documentation/doxygen/makeNotebooks.sh index c962daa33e375..86bb2758b0e8b 100755 --- a/documentation/doxygen/makeNotebooks.sh +++ b/documentation/doxygen/makeNotebooks.sh @@ -38,19 +38,18 @@ while read notebook dependencies; do done fi done <AddButton("formula1", ".x graphics/formula1.C", "Simple Formula and Functions"); bar->AddButton("surfaces", ".x graphs/surfaces.C", "Surface Drawing Options"); bar->AddButton("fillrandom", ".x hist/fillrandom.C", "Histograms with Random Numbers from a Function"); - bar->AddButton("fit1", ".x fit/fit1.C", "A Simple Fitting Example"); - bar->AddButton("multifit", ".x fit/multifit.C", "Fitting in Subranges of Histograms"); + bar->AddButton("fit1", ".x math/fit/fit1.C", "A Simple Fitting Example"); + bar->AddButton("multifit", ".x math/fit/multifit.C", "Fitting in Subranges of Histograms"); bar->AddButton("h1ReadAndDraw", ".x hist/h1ReadAndDraw.C", "Drawing Options for 1D Histograms"); bar->AddButton("graph", ".x graphs/graph.C", "Example of a Simple Graph"); bar->AddButton("gerrors", ".x graphs/gerrors.C", "Example of a Graph with Error Bars"); @@ -34,8 +34,8 @@ void demos() { bar->AddButton("geometry", ".x geom/rootgeom.C", "Example of TGeoManager drawing"); bar->AddButton("file", ".x io/file.C", "The ROOT File Format"); bar->AddButton("fildir", ".x io/fildir.C", "The ROOT File, Directories and Keys"); - bar->AddButton("tree", ".x tree/tree.C", "The Tree Data Structure"); - bar->AddButton("ntuple1", ".x tree/ntuple1.C", "Ntuples and Selections"); + bar->AddButton("tree", ".x io/tree/tree.C", "The Tree Data Structure"); + bar->AddButton("ntuple1", ".x io/tree/ntuple1.C", "Ntuples and Selections"); bar->AddButton("benchmarks", ".x legacy/benchmarks.C", "Runs several tests and produces an benchmark report"); bar->AddButton("rootmarks", ".x legacy/rootmarks.C", "Prints an Estimated ROOTMARKS for Your Machine"); bar->SetButtonWidth(90); diff --git a/tutorials/pyroot/demo.py b/tutorials/demos.py similarity index 53% rename from tutorials/pyroot/demo.py rename to tutorials/demos.py index 706ce4fc9e970..a8213f9384863 100644 --- a/tutorials/pyroot/demo.py +++ b/tutorials/demos.py @@ -1,6 +1,6 @@ ## \file -## \ingroup tutorial_pyroot -## To run, do "python /demo.py" +## \ingroup Tutorials +## To run, do "python /demos.py" ## ## \macro_code ## @@ -9,9 +9,9 @@ import os, sys import ROOT -# To run, do "python /demo.py" +# To run, do "python /demos.py" -# enable running from another directory than the one where demo.py resides +# enable running from another directory than the one where demos.py resides workdir = os.path.dirname( sys.argv[0] ) if workdir: os.chdir( workdir ) @@ -31,29 +31,23 @@ bar.AddButton( 'Help on Demos', r'TPython::Exec( "' + to_run.format('demoshelp.py') + '" );', 'Click Here For Help on Running the Demos' ) bar.AddButton( 'browser', r'TPython::Exec( "b = ROOT.TBrowser()" );', 'Start the ROOT browser' ) -bar.AddButton( 'framework', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/framework.py') + '" );', 'An Example of Object Oriented User Interface' ) -bar.AddButton( 'first', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/first.py') + '" );', 'An Example of Slide with Root' ) bar.AddButton( 'hsimple', r'TPython::Exec( "' + to_run.format('hsimple.py') + '" );', 'Creating histograms/Ntuples on file', "button" ) -bar.AddButton( 'hsum', r'TPython::Exec( "' + to_run.format('hsum.py') + '" );', 'Filling Histograms and Some Graphics Options' ) -bar.AddButton( 'formula1', r'TPython::Exec( "' + to_run.format('formula1.py') + '" );', 'Simple Formula and Functions' ) -bar.AddButton( 'surfaces', r'TPython::Exec( "' + to_run.format('surfaces.py') + '" );', 'Surface Drawing Options' ) -bar.AddButton( 'fillrandom', r'TPython::Exec( "' + to_run.format('fillrandom.py') + '" );','Histograms with Random Numbers from a Function' ) -bar.AddButton( 'fit1', r'TPython::Exec( "' + to_run.format('fit1.py') + '" );', 'A Simple Fitting Example' ) -bar.AddButton( 'multifit', r'TPython::Exec( "' + to_run.format('multifit.py') + '" );', 'Fitting in Subranges of Histograms' ) -bar.AddButton( 'h1draw', r'TPython::Exec( "' + to_run.format('h1ReadAndDraw.py') + '" );', 'Drawing Options for 1D Histograms' ) -bar.AddButton( 'graph', r'TPython::Exec( "' + to_run.format('graph.py') + '" );', 'Example of a Simple Graph' ) -bar.AddButton( 'gerrors', r'TPython::Exec( "' + to_run.format('gerrors.py') + '" );', 'Example of a Graph with Error Bars' ) -bar.AddButton( 'tornado', r'TPython::Exec( "' + to_run.format('tornado.py') + '" );', 'Examples of 3-D PolyMarkers' ) -bar.AddButton( 'shapes', r'TPython::Exec( "' + to_run.format('shapes.py') + '" );', 'The Geometry Shapes' ) -bar.AddButton( 'geometry', r'TPython::Exec( "' + to_run.format('geometry.py') + '" );', 'Creation of the NA49 Geometry File' ) -bar.AddButton( 'na49view', r'TPython::Exec( "' + to_run.format('na49view.py') + '" );', 'Two Views of the NA49 Detector Geometry' ) -bar.AddButton( 'file', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/file.py') + '" );', 'The ROOT File Format' ) -bar.AddButton( 'fildir', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/fildir.py') + '" );', 'The ROOT File, Directories and Keys' ) -bar.AddButton( 'tree', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/tree.py') + '" );', 'The Tree Data Structure' ) -bar.AddButton( 'ntuple1', r'TPython::Exec( "' + to_run.format('ntuple1.py') + '" );', 'Ntuples and Selections' ) -bar.AddButton( 'rootmarks', r'TPython::Exec( "' + to_run.format('../legacy/pyroot/rootmarks.py') +'" );', 'Prints an Estimated ROOTMARKS for Your Machine' ) +bar.AddButton( 'hsum', r'TPython::Exec( "' + to_run.format('hist/hsum.py') + '" );', 'Filling Histograms and Some Graphics Options' ) +bar.AddButton( 'formula1', r'TPython::Exec( "' + to_run.format('graphics/formula1.py') + '" );', 'Simple Formula and Functions' ) +bar.AddButton( 'surfaces', r'TPython::Exec( "' + to_run.format('graphs/surfaces.py') + '" );', 'Surface Drawing Options' ) +bar.AddButton( 'fillrandom', r'TPython::Exec( "' + to_run.format('hist/fillrandom.py') + '" );','Histograms with Random Numbers from a Function' ) +bar.AddButton( 'fit1', r'TPython::Exec( "' + to_run.format('math/fit/fit1.py') + '" );', 'A Simple Fitting Example' ) +bar.AddButton( 'multifit', r'TPython::Exec( "' + to_run.format('math/fit/multifit.py') + '" );', 'Fitting in Subranges of Histograms' ) +bar.AddButton( 'h1draw', r'TPython::Exec( "' + to_run.format('hist/h1ReadAndDraw.py') + '" );', 'Drawing Options for 1D Histograms' ) +bar.AddButton( 'graph', r'TPython::Exec( "' + to_run.format('graphs/graph.py') + '" );', 'Example of a Simple Graph' ) +bar.AddButton( 'gerrors', r'TPython::Exec( "' + to_run.format('graphs/gerrors.py') + '" );', 'Example of a Graph with Error Bars' ) +bar.AddButton( 'tornado', r'TPython::Exec( "' + to_run.format('graphics/tornado.py') + '" );', 'Examples of 3-D PolyMarkers' ) +bar.AddButton( 'shapes', r'TPython::Exec( "' + to_run.format('geom/shapes.py') + '" );', 'The Geometry Shapes' ) +bar.AddButton( 'geometry', r'TPython::Exec( "' + to_run.format('geom/geometry.py') + '" );', 'Creation of the NA49 Geometry File' ) +bar.AddButton( 'na49view', r'TPython::Exec( "' + to_run.format('geom/na49view.py') + '" );', 'Two Views of the NA49 Detector Geometry' ) +bar.AddButton( 'ntuple1', r'TPython::Exec( "' + to_run.format('io/tree/ntuple1.py') + '" );', 'Ntuples and Selections' ) bar.AddSeparator() # not implemented -bar.AddButton( 'make ntuple', r'TPython::Exec( "' + to_run.format('mrt.py') + '" );', 'Convert a text file to an ntuple' ) +bar.AddButton( 'make ntuple', r'TPython::Exec( "' + to_run.format('io/tree/csv2tntuple.py') + '" );', 'Convert a text file to an ntuple' ) bar.Show() diff --git a/tutorials/pyroot/demoshelp.py b/tutorials/demoshelp.py similarity index 97% rename from tutorials/pyroot/demoshelp.py rename to tutorials/demoshelp.py index 2666fd26f4862..c67a150762049 100644 --- a/tutorials/pyroot/demoshelp.py +++ b/tutorials/demoshelp.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup Tutorials ## \notebook ## Display demo help. ## diff --git a/tutorials/pyroot/geometry.py b/tutorials/geom/geometry.py similarity index 80% rename from tutorials/pyroot/geometry.py rename to tutorials/geom/geometry.py index 7d14c4e3631d5..fddd71f6b17ec 100644 --- a/tutorials/pyroot/geometry.py +++ b/tutorials/geom/geometry.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_geom ## \notebook -nodraw ## Geometry ## @@ -10,7 +10,7 @@ import os import ROOT -macrodir = os.path.join(str(ROOT.gROOT.GetTutorialDir()), 'pyroot') +macrodir = os.path.join(str(ROOT.gROOT.GetTutorialDir()), 'geom') # the na49.C file was generated, so no python conversion is provided ROOT.gROOT.Macro( ROOT.gSystem.UnixPathName( os.path.join( macrodir, os.pardir, 'geom', 'na49.C' ) ) ) diff --git a/tutorials/pyroot/na49geomfile.py b/tutorials/geom/na49geomfile.py similarity index 79% rename from tutorials/pyroot/na49geomfile.py rename to tutorials/geom/na49geomfile.py index 47914df72cf69..9de11b0b7449a 100644 --- a/tutorials/pyroot/na49geomfile.py +++ b/tutorials/geom/na49geomfile.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_geom ## Before executing this macro, the file makegeometry.C must have been executed ## ## \macro_code @@ -9,7 +9,7 @@ import ROOT ROOT.gBenchmark.Start( 'geometry' ) -na = ROOT.TFile( 'py-na49.root', 'RECREATE' ) +na = ROOT.TFile( 'na49.root', 'RECREATE' ) n49 = ROOT.gROOT.FindObject( 'na49' ) n49.Write() na.Write() diff --git a/tutorials/pyroot/na49view.py b/tutorials/geom/na49view.py similarity index 94% rename from tutorials/pyroot/na49view.py rename to tutorials/geom/na49view.py index 90b2c0cfef16f..cabeb9c0ce70f 100644 --- a/tutorials/pyroot/na49view.py +++ b/tutorials/geom/na49view.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_geom ## This macro generates two views of the NA49 detector. ## ## To have a better and dynamic view of any of these pads, @@ -31,7 +31,7 @@ na49title.SetFillColor( 32 ) na49title.Draw() # -nageom = ROOT.TFile( 'py-na49.root' ) +nageom = ROOT.TFile( 'na49.root' ) n49 = ROOT.gROOT.FindObject( 'na49' ) n49.SetBomb( 1.2 ) n49.cd() # Set current geometry diff --git a/tutorials/pyroot/na49visible.py b/tutorials/geom/na49visible.py similarity index 98% rename from tutorials/pyroot/na49visible.py rename to tutorials/geom/na49visible.py index 8928b65d9dbf2..56f4c75e66d3b 100644 --- a/tutorials/pyroot/na49visible.py +++ b/tutorials/geom/na49visible.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_geom ## Set visibility attributes for the NA49 geometry ## Set Shape attributes ## diff --git a/tutorials/pyroot/shapes.py b/tutorials/geom/shapes.py similarity index 99% rename from tutorials/pyroot/shapes.py rename to tutorials/geom/shapes.py index d57e972f3444b..46a08c88f73d6 100644 --- a/tutorials/pyroot/shapes.py +++ b/tutorials/geom/shapes.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_geom ## \notebook ## Draw the geometry using the x3d viewver. ## Note that this viewver may also be invoked from the "View" menu in diff --git a/tutorials/pyroot/formula1.py b/tutorials/graphics/formula1.py similarity index 95% rename from tutorials/pyroot/formula1.py rename to tutorials/graphics/formula1.py index b13d85bc85b6a..170bafd591517 100644 --- a/tutorials/pyroot/formula1.py +++ b/tutorials/graphics/formula1.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphics ## \notebook -js ## TF1 example. ## diff --git a/tutorials/pyroot/tornado.py b/tutorials/graphics/tornado.py similarity index 97% rename from tutorials/pyroot/tornado.py rename to tutorials/graphics/tornado.py index 63e6cd93c66bc..4fa621c1037ef 100644 --- a/tutorials/pyroot/tornado.py +++ b/tutorials/graphics/tornado.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphics ## Tornado example. ## \notebook ## diff --git a/tutorials/pyroot/gerrors.py b/tutorials/graphs/gerrors.py similarity index 96% rename from tutorials/pyroot/gerrors.py rename to tutorials/graphs/gerrors.py index 05bc3fb32281e..c439be0f4eec7 100644 --- a/tutorials/pyroot/gerrors.py +++ b/tutorials/graphs/gerrors.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphs ## \notebook -js ## A Simple Graph with error bars ## diff --git a/tutorials/pyroot/graph.py b/tutorials/graphs/graph.py similarity index 97% rename from tutorials/pyroot/graph.py rename to tutorials/graphs/graph.py index 0d9bca8cc0a3e..8b74fb0b82cd0 100644 --- a/tutorials/pyroot/graph.py +++ b/tutorials/graphs/graph.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphs ## \notebook ## A Simple Graph Example ## diff --git a/tutorials/pyroot/surfaces.py b/tutorials/graphs/surfaces.py similarity index 97% rename from tutorials/pyroot/surfaces.py rename to tutorials/graphs/surfaces.py index 9e045fe319a1b..2b45652dfe904 100644 --- a/tutorials/pyroot/surfaces.py +++ b/tutorials/graphs/surfaces.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphs ## \notebook ## Surfaces example ## diff --git a/tutorials/pyroot/zdemo.py b/tutorials/graphs/zdemo.py similarity index 99% rename from tutorials/pyroot/zdemo.py rename to tutorials/graphs/zdemo.py index 603c7cb6d25bf..344fe865ec8f7 100644 --- a/tutorials/pyroot/zdemo.py +++ b/tutorials/graphs/zdemo.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_graphs ## \notebook ## This macro is an example of graphs in log scales with annotations. ## diff --git a/tutorials/pyroot/gui_ex.py b/tutorials/gui/gui_simple.py similarity index 96% rename from tutorials/pyroot/gui_ex.py rename to tutorials/gui/gui_simple.py index edb69b5930b2f..654e5159f9cbb 100644 --- a/tutorials/pyroot/gui_ex.py +++ b/tutorials/gui/gui_simple.py @@ -1,10 +1,11 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_gui ## A Simple GUI Example ## ## \macro_code ## ## \author Wim Lavrijsen +from __future__ import print_function import os, sys, ROOT diff --git a/tutorials/pyroot/numberEntry.py b/tutorials/gui/numberEntry.py similarity index 98% rename from tutorials/pyroot/numberEntry.py rename to tutorials/gui/numberEntry.py index 0932299eaa1c6..42b21101da857 100644 --- a/tutorials/pyroot/numberEntry.py +++ b/tutorials/gui/numberEntry.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_gui ## Example frame with one box where the user can increase or decrease a number ## and the shown value will be updated accordingly. ## diff --git a/tutorials/pyroot/DynamicSlice.py b/tutorials/hist/DynamicSlice.py similarity index 99% rename from tutorials/pyroot/DynamicSlice.py rename to tutorials/hist/DynamicSlice.py index 071df39fb290e..b6ebb7a68653e 100644 --- a/tutorials/pyroot/DynamicSlice.py +++ b/tutorials/hist/DynamicSlice.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_hist ## \notebook ## Example of function called when a mouse event occurs in a pad. ## When moving the mouse in the canvas, a second canvas shows the diff --git a/tutorials/pyroot/h1ReadAndDraw.py b/tutorials/hist/h1ReadAndDraw.py similarity index 98% rename from tutorials/pyroot/h1ReadAndDraw.py rename to tutorials/hist/h1ReadAndDraw.py index e753061a89e33..284008534d899 100644 --- a/tutorials/pyroot/h1ReadAndDraw.py +++ b/tutorials/hist/h1ReadAndDraw.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_hist ## \notebook -js ## A Simple histogram drawing example ## diff --git a/tutorials/pyroot/hsum.py b/tutorials/hist/hsum.py similarity index 98% rename from tutorials/pyroot/hsum.py rename to tutorials/hist/hsum.py index d8db7a8b86282..5e0eabbf35339 100644 --- a/tutorials/pyroot/hsum.py +++ b/tutorials/hist/hsum.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_hist ## \notebook -js ## Simple example illustrating how to use the C++ interpreter ## diff --git a/tutorials/histfactory/index.md b/tutorials/histfactory/index.md new file mode 100644 index 0000000000000..f2b2f669a19cf --- /dev/null +++ b/tutorials/histfactory/index.md @@ -0,0 +1,3 @@ +\defgroup tutorial_histfactory HistFactory Tutorials +\ingroup tutorial_roofit +\brief These tutorials illustrate the usage of the histfactory. diff --git a/tutorials/pyroot/hsimple.py b/tutorials/hsimple.py similarity index 99% rename from tutorials/pyroot/hsimple.py rename to tutorials/hsimple.py index eb3b7d5efcd03..98882f04283c4 100644 --- a/tutorials/pyroot/hsimple.py +++ b/tutorials/hsimple.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup Tutorials ## \notebook -js ## This program creates : ## - a one dimensional histogram diff --git a/tutorials/index.md b/tutorials/index.md index 3a4d7964bc0ab..a9ad5436455de 100644 --- a/tutorials/index.md +++ b/tutorials/index.md @@ -122,10 +122,6 @@ The `$ROOTSYS/tutorials` directory includes several sub-directories: \ingroup Tutorials \brief These examples aim to illustrate the multicore features of ROOT, such as thread awareness and safety, multithreading and multiprocessing. -\defgroup tutorial_pyroot PyRoot tutorials -\ingroup Tutorials -\brief Selected examples illustrating how to use ROOT's Python interface: PyROOT. - \defgroup tutorial_roostats RooStats Tutorials \ingroup Tutorials \brief These tutorials illustrate the main features of RooStats. diff --git a/tutorials/pyroot/pyroot006_tcontext_context_manager.py b/tutorials/io/tcontext_context_manager.py similarity index 83% rename from tutorials/pyroot/pyroot006_tcontext_context_manager.py rename to tutorials/io/tcontext_context_manager.py index 4f2239fb99efc..bbdb2082b31ec 100644 --- a/tutorials/pyroot/pyroot006_tcontext_context_manager.py +++ b/tutorials/io/tcontext_context_manager.py @@ -1,9 +1,9 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_io ## \notebook -nodraw ## This tutorial demonstrates the usage of the TContext class as a Python context ## manager. This functionality is related with how TFile works, so it is -## suggested to also take a look at the pyroot005 tutorial. +## suggested to also take a look at the tfile_context_manager.py tutorial. ## ## \macro_code ## \macro_output @@ -11,14 +11,16 @@ ## \date March 2022 ## \author Vincenzo Eduardo Padulano CERN/UPV import os +import sys import ROOT -from ROOT import TDirectory, TFile +from ROOT import TDirectory, TFile, gROOT # Sometimes it is useful to have multiple open files at once. In such cases, # the current directory will always be the file that was open last. -file_1 = TFile("pyroot006_file_1.root", "recreate") -file_2 = TFile("pyroot006_file_2.root", "recreate") +path = str(gROOT.GetTutorialDir()) + '/io/' +file_1 = TFile(path+"tcontext_1.root", "recreate") +file_2 = TFile(path+"tcontext_2.root", "recreate") print("Current directory: '{}'.\n".format(ROOT.gDirectory.GetName())) # Changing directory into another file can be safely done through a TContext # context manager. @@ -43,15 +45,17 @@ # this context, rather than to the global ROOT.gROOT # Remember that the TContext must be initialized before the TFile, otherwise the # current directory would already be set to the file opened for this context. -with TDirectory.TContext(), TFile("pyroot006_file_3.root", "recreate") as f: +with TDirectory.TContext(), TFile(path+"tcontext_3.root", "recreate") as f: print("Current directory: '{}'.\n".format(ROOT.gDirectory.GetName())) histo_2 = ROOT.TH1F("histo_2", "histo_2", 10, 0, 10) f.WriteObject(histo_2, "another_histogram") print("Current directory: '{}'.\n".format(ROOT.gDirectory.GetName())) + # Cleanup the files created for this tutorial file_1.Close(); file_2.Close(); + for i in range(1, 4): - os.remove("pyroot006_file_{}.root".format(i)) + os.remove(path+"tcontext_{}.root".format(i)) diff --git a/tutorials/pyroot/pyroot005_tfile_context_manager.py b/tutorials/io/tfile_context_manager.py similarity index 92% rename from tutorials/pyroot/pyroot005_tfile_context_manager.py rename to tutorials/io/tfile_context_manager.py index 5760a6bc8128b..b2b95fe625355 100644 --- a/tutorials/pyroot/pyroot005_tfile_context_manager.py +++ b/tutorials/io/tfile_context_manager.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_io ## \notebook -nodraw ## This tutorial demonstrates the usage of the TFile class as a Python context ## manager. @@ -12,7 +12,7 @@ import os import ROOT -from ROOT import TFile +from ROOT import TFile, gROOT # By default, objects of some ROOT types such as `TH1` and its derived types # are automatically attached to a ROOT.TDirectory when they are created. @@ -31,7 +31,9 @@ # open a TFile as a Python context manager. In the context, objects can be # created, modified and finally written to the file. At the end of the context, # the file will be automatically closed. -with TFile.Open("pyroot005_file_1.root", "recreate") as f: +path = str(gROOT.GetTutorialDir()) + '/io/' +filename = path+"tfile_1.root" +with TFile.Open(filename, "recreate") as f: histo_2 = ROOT.TH1F("histo_2", "histo_2", 10, 0, 10) # Inside the context, the current directory is the open file print("Current directory: '{}'.\n".format(ROOT.gDirectory.GetName())) @@ -54,11 +56,12 @@ # automatically closed. This means you should use this pattern as a quick way # to get information or modify objects from a certain file, without needing to # keep the histograms alive afterwards. -with TFile.Open("pyroot005_file_1.root", "read") as f: + +with TFile.Open(filename, "read") as f: # Retrieve histogram using the name given to f.WriteObject in the previous # with statement histo_2_fromfile = f["my_histogram"] print("Retrieved '{}' histogram from file '{}'.\n".format(histo_2_fromfile.GetName(), f.GetName())) # Cleanup the file created for this tutorial -os.remove("pyroot005_file_1.root") +os.remove(filename) diff --git a/tutorials/pyroot/aptuple.txt b/tutorials/io/tree/aptuple.txt similarity index 100% rename from tutorials/pyroot/aptuple.txt rename to tutorials/io/tree/aptuple.txt diff --git a/tutorials/pyroot/mrt.py b/tutorials/io/tree/csv2tntuple.py similarity index 83% rename from tutorials/pyroot/mrt.py rename to tutorials/io/tree/csv2tntuple.py index 17a73f6881f93..78eda18d5e0f3 100644 --- a/tutorials/pyroot/mrt.py +++ b/tutorials/io/tree/csv2tntuple.py @@ -1,9 +1,9 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_tree ## \notebook -nodraw ## Build ROOT Ntuple from other source. ## This program reads the `aptuple.txt' file row by row, then creates -## the Ntuple by adding row by row. +## the TNtuple by adding row by row. ## ## \macro_output ## \macro_code @@ -14,7 +14,7 @@ from ROOT import TFile, TNtuple, TROOT -ifn = os.path.join(str(TROOT.GetTutorialDir()), 'pyroot', 'aptuple.txt') +ifn = os.path.join(str(TROOT.GetTutorialDir()), 'io', 'tree', 'aptuple.txt') ofn = 'aptuple.root' print('opening file %s ...' % ifn) diff --git a/tutorials/pyroot/parse_CSV_file_with_TTree_ReadStream.py b/tutorials/io/tree/csv2tree_ReadStream.py similarity index 93% rename from tutorials/pyroot/parse_CSV_file_with_TTree_ReadStream.py rename to tutorials/io/tree/csv2tree_ReadStream.py index a228702d5cda7..0e53638a9912a 100644 --- a/tutorials/pyroot/parse_CSV_file_with_TTree_ReadStream.py +++ b/tutorials/io/tree/csv2tree_ReadStream.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_tree ## This function provides an example of how one might ## massage a csv data file to read into a ROOT TTree ## via TTree::ReadStream. This could be useful if the @@ -122,6 +122,9 @@ def parse_CSV_file_with_TTree_ReadStream(tree_name, afile): if __name__ == '__main__': if len(sys.argv) < 2: print("Usage: %s file_to_parse.dat" % sys.argv[0]) - sys.exit(1) - parse_CSV_file_with_TTree_ReadStream("example_tree", sys.argv[1]) + print("Using default data file example_data.dat") + filename = os.path.join(str(ROOT.TROOT.GetTutorialDir()), 'io', 'tree', 'example_data.dat') + parse_CSV_file_with_TTree_ReadStream("example_tree", filename) + else: + parse_CSV_file_with_TTree_ReadStream("example_tree", sys.argv[1]) diff --git a/tutorials/pyroot/example_data.dat b/tutorials/io/tree/example_data.dat similarity index 100% rename from tutorials/pyroot/example_data.dat rename to tutorials/io/tree/example_data.dat diff --git a/tutorials/pyroot/ntuple1.py b/tutorials/io/tree/ntuple1.py similarity index 99% rename from tutorials/pyroot/ntuple1.py rename to tutorials/io/tree/ntuple1.py index c23fe76969283..eabc8a3c4ec51 100644 --- a/tutorials/pyroot/ntuple1.py +++ b/tutorials/io/tree/ntuple1.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_tree ## \notebook ## Ntuple drawing example. ## diff --git a/tutorials/pyroot/staff.py b/tutorials/io/tree/staff.py similarity index 98% rename from tutorials/pyroot/staff.py rename to tutorials/io/tree/staff.py index a806109dc9e36..cdc38f282ec73 100644 --- a/tutorials/pyroot/staff.py +++ b/tutorials/io/tree/staff.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_tree ## \notebook -nodraw ## example of macro to read data from an ascii file and ## create a root file with a Tree. diff --git a/tutorials/pyroot/fit1.py b/tutorials/math/fit/fit1.py similarity index 91% rename from tutorials/pyroot/fit1.py rename to tutorials/math/fit/fit1.py index e9afa08697b58..4283f045a7e09 100644 --- a/tutorials/pyroot/fit1.py +++ b/tutorials/math/fit/fit1.py @@ -1,5 +1,5 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_fit ## \notebook ## Fit example. ## @@ -57,7 +57,7 @@ fitlabel = TPaveText( 0.6, 0.3, 0.9, 0.80, 'NDC' ) fitlabel.SetTextAlign( 12 ) fitlabel.SetFillColor( 42 ) -fitlabel.ReadFile(path.join(str(gROOT.GetTutorialDir()), 'pyroot', 'fit1_py.py')) +fitlabel.ReadFile(path.join(str(gROOT.GetTutorialDir()), 'math', 'fit', 'fit1_py.py')) fitlabel.Draw() c1.Update() gBenchmark.Show( 'fit1' ) diff --git a/tutorials/pyroot/fit1_py.py b/tutorials/math/fit/fit1_py.py similarity index 100% rename from tutorials/pyroot/fit1_py.py rename to tutorials/math/fit/fit1_py.py diff --git a/tutorials/pyroot/pyroot001_arrayInterface.py b/tutorials/math/vecops/vo008_numpyInterface.py similarity index 85% rename from tutorials/pyroot/pyroot001_arrayInterface.py rename to tutorials/math/vecops/vo008_numpyInterface.py index 5ebc926dc5097..10ca46c31eac4 100644 --- a/tutorials/pyroot/pyroot001_arrayInterface.py +++ b/tutorials/math/vecops/vo008_numpyInterface.py @@ -1,7 +1,7 @@ ## \file -## \ingroup tutorial_pyroot +## \ingroup tutorial_vecops ## \notebook -nodraw -## This tutorial illustrates the conversion of STL vectors and TVec to numpy +## This tutorial illustrates the conversion of RVec to numpy ## arrays without copying the data. ## The memory-adoption is achieved by the dictionary __array_interface__, which ## is added dynamically to the Python objects by PyROOT. @@ -21,8 +21,8 @@ exit() # Create a vector ROOT object and assign values -# Note that this works as well with a TVec -vec = ROOT.std.vector("float")(2) +# Note that this works as well with a ROOT.std.vector +vec = ROOT.RVecF(2) # or ROOT.std.vector("float")(2) vec[0] = 1 vec[1] = 2 print("Content of the ROOT vector object: {}".format([x for x in vec])) diff --git a/tutorials/pyroot/pyroot002_pythonizationDecorator.py b/tutorials/pyroot/pyroot002_pythonizationDecorator.py deleted file mode 100644 index caa805c8a6cdc..0000000000000 --- a/tutorials/pyroot/pyroot002_pythonizationDecorator.py +++ /dev/null @@ -1,200 +0,0 @@ -## \file -## \ingroup tutorial_pyroot -## \notebook -nodraw -## This tutorial shows how to use the `@pythonization` decorator to add extra -## behaviour to C++ user classes that are used from Python via PyROOT. -## -## \macro_code -## \macro_output -## -## \date November 2021 -## \author Enric Tejedor - -import ROOT -from ROOT import pythonization - -# Let's first define a new C++ class. In this tutorial, we will see how we can -# "pythonize" this class, i.e. how we can add some extra behaviour to it to -# make it more pythonic or easier to use from Python. -# -# Note: In this example, the class is defined dynamically for demonstration -# purposes, but it could also be a C++ class defined in some library or header. -# For more information about loading C++ user code to be used from Python with -# PyROOT, please see: -# https://root.cern.ch/manual/python/#loading-user-libraries-and-just-in-time-compilation-jitting -ROOT.gInterpreter.Declare(''' -class MyClass {}; -''') - -# Next, we define a pythonizor function: the function that will be responsible -# for injecting new behaviour in our C++ class `MyClass`. -# -# To convert a given Python function into a pythonizor, we need to decorate it -# with the @pythonization decorator. Such decorator allows us to define which -# which class we want to pythonize by providing its class name and its -# namespace (if the latter is not specified, it defaults to the global -# namespace, i.e. '::'). -# -# The decorated function - the pythonizor - must accept either one or two -# parameters: -# 1. The class to be pythonized (proxy object where new behaviour can be -# injected) -# 2. The fully-qualified name of that class (optional). -# -# Let's see all this with a simple example. Suppose I would like to define how -# `MyClass` objects are represented as a string in Python (i.e. what would be -# shown when I print that object). For that purpose, I can define the following -# pythonizor function. There are two important things to be noted here: -# - The @pythonization decorator has one argument that specifies our target -# class is `MyClass`. -# - The pythonizor function `pythonizor_of_myclass` provides and injects a new -# implementation for `__str__`, the mechanism that Python provides to define -# how to represent objects as strings. This new implementation -# always returns the string "This is a MyClass object". -@pythonization('MyClass') -def pythonizor_of_myclass(klass): - klass.__str__ = lambda o : 'This is a MyClass object' - -# Once we have defined our pythonizor function, let's see it in action. -# We will now use the `MyClass` class for the first time from Python: we will -# create a new instance of that class. At this moment, the pythonizor will -# execute and modify the class - pythonizors are always lazily run when a given -# class is used for the first time from a Python script. -my_object = ROOT.MyClass() - -# Since the pythonizor already executed, we should now see the new behaviour. -# For that purpose, let's print `my_object` (should show "This is a MyClass -# object"). -print(my_object) - -# The previous example is just a simple one, but there are many ways in which a -# class can be pythonized. Typical examples are the redefinition of dunder -# methods (e.g. `__iter__` and `__next__` to make your objects iterable from -# Python). If you need some inspiration, many ROOT classes are pythonized in -# the way we just saw; their pythonizations can be seen at: -# https://github.com/root-project/root/tree/master/bindings/pyroot/pythonizations/python/ROOT/pythonization - -# The @pythonization decorator offers a few more options when it comes to -# matching classes that you want to pythonize. We saw that we can match a -# single class, but we can also specify a list of classes to pythonize. -# -# The following code defines a couple of new classes: -ROOT.gInterpreter.Declare(''' -namespace NS { - class Class1 {}; - class Class2 {}; -} -''') - -# Note that these classes belong to the `NS` namespace. As mentioned above, the -# @pythonization decorator accepts a parameter with the namespace of the class -# or classes to be pythonized. Therefore, a pythonizor that matches both classes -# would look like this: -@pythonization(['Class1', 'Class2'], ns='NS') -def pythonize_two_classes(klass): - klass.new_attribute = 1 - -# Both classes will have the new attribute: -o1 = ROOT.NS.Class1() -o2 = ROOT.NS.Class2() -print("Printing new attribute") -for o in o1, o2: - print(o.new_attribute) - -# In addition, @pythonization also accepts prefixes of classes in a certain -# namespace in order to match multiple classes in that namespace. To signal that -# what we provide to @pythonization is a prefix, we need to set the `is_prefix` -# argument to `True` (default is `False`). -# -# A common case where matching prefixes is useful is when we have a templated -# class and we want to pythonize all possible instantiations of that template. -# For example, we can pythonize the `std::vector` (templated) class like so: -@pythonization('vector<', ns='std', is_prefix=True) -def vector_pythonizor(klass): - # first_elem returns the first element of the vector if it exists - klass.first_elem = lambda v : v[0] if v else None - -# Since we defined a prefix to do the match, the pythonization will be applied -# both if we instantiate e.g. a vector of integers and a vector of doubles. -v_int = ROOT.std.vector['int']([1,2,3]) -v_double = ROOT.std.vector['double']([4.,5.,6.]) -print("First element of integer vector: {}".format(v_int.first_elem())) -print("First element of double vector: {}".format(v_double.first_elem())) - -# Note that specifying a list of class name prefixes is also possible (similarly -# to what we saw with a list of class names). Again, `is_prefix=True` is -# required to signal that we are providing a list of prefixes. - -# These are some examples of combinations of prefixes and namespaces and the -# corresponding classes that they match: -# - '' : all classes in the global namespace. -# - '', ns='NS1::NS2' : all classes in the `NS1::NS2` namespace. -# - 'Prefix' : classes whose name starts with `Prefix` in the global namespace. -# - 'Prefix', ns='NS' : classes whose name starts with `Prefix` in the `NS` -# namespace. - -# Moreover, a pythonizor function can have a second optional parameter that -# contains the fully-qualified name of the class being pythonized. This can be -# useful e.g. if we would like to do some more complex filtering of classes in -# our pythonizor, for instance using regular expressions. -@pythonization('pair<', ns='std', is_prefix=True) -def pair_pythonizor(klass, name): - print('Pythonizing class ' + name) - -# The pythonizor above will be applied to any instantiation of `std::pair` - we -# can see this with the print we did inside the pythonizor. -# Note that we could use the `name` parameter to e.g. further filter which -# particular instantiations we would like to pythonize. -p1 = ROOT.std.pair['int','int'](1,2) # prints 'Pythonizing class std::pair' -p2 = ROOT.std.pair['int','double'](1,2.) # prints 'Pythonizing class std::pair' - -# Note that, to pythonize multiple classes in different namespaces, we can -# stack multiple @pythonization decorators. For example, if we define these -# classes: -ROOT.gInterpreter.Declare(''' -class FirstClass {}; -namespace NS { - class SecondClass {}; -} -''') - -# We can pythonize both of them with a single pythonizor function like so: -@pythonization('FirstClass') -@pythonization('SecondClass', ns='NS') -def pythonizor_for_first_and_second(klass, name): - print('Executed for class ' + name) - -# If we now access both classes, we should see that the pythonizor runs twice. -f = ROOT.FirstClass() -s = ROOT.NS.SecondClass() - -# So far we have seen how pythonizations can be registered for classes that -# have not been used yet. We have discussed how, in that case, the pythonizor -# functions are executed lazily when their target class/es are used for the -# first time in the application. -# However, it can also happen that our target class/es have already been -# accessed by the time we register a pythonization. In such a scenario, the -# pythonizor is applied immediately (at registration time) to the target -# class/es. - -# Let's see an example of what was just explained. We will define a new class -# and immediately create an object of that class. We can check how the object -# still does not have a new attribute `pythonized` that we are going to inject -# in the next step. -ROOT.gInterpreter.Declare(''' -class MyClass2 {}; -''') -o = ROOT.MyClass2() -try: - print(o.pythonized) -except AttributeError: - print("Object has not been pythonized yet!") - -# After that, we will register a pythonization for `MyClass2`. Since the class -# has already been used, the pythonization will happen right away. -@pythonization('MyClass2') -def pythonizor_for_myclass2(klass): - klass.pythonized = True - -# Now our object does have the `pythonized` attribute: -print(o.pythonized) # prints True diff --git a/tutorials/pyroot/pyroot003_prettyPrinting.py b/tutorials/pyroot/pyroot003_prettyPrinting.py deleted file mode 100644 index f07533222d570..0000000000000 --- a/tutorials/pyroot/pyroot003_prettyPrinting.py +++ /dev/null @@ -1,47 +0,0 @@ -## \file -## \ingroup tutorial_pyroot -## \notebook -nodraw -## This tutorial illustrates the pretty printing feature of PyROOT, which reveals -## the content of the object if a string representation is requested, e.g., by -## Python's print statement. The printing behaves similar to the ROOT prompt -## powered by the C++ interpreter cling. -## -## \macro_code -## \macro_output -## -## \date June 2018 -## \author Stefan Wunsch, Enric Tejedor - -import ROOT - -# Create an object with PyROOT -obj = ROOT.std.vector("int")(3) -for i in range(obj.size()): - obj[i] = i - -# Print the object, which reveals the content. Note that `print` calls the special -# method `__str__` of the object internally. -print(obj) - -# The output can be retrieved as string by any function that triggers the `__str__` -# special method of the object, e.g., `str` or `format`. -print(str(obj)) -print("{}".format(obj)) - -# Note that the interactive Python prompt does not call `__str__`, it calls -# `__repr__`, which implements a formal and unique string representation of -# the object. -print(repr(obj)) -obj - -# The print output behaves similar to the ROOT prompt, e.g., here for a ROOT histogram. -hist = ROOT.TH1F("name", "title", 10, 0, 1) -print(hist) - -# If cling cannot produce any nice representation for the class, we fall back to a -# "" format, which is what `__repr__` returns -ROOT.gInterpreter.Declare('class MyClass {};') -m = ROOT.MyClass() -print(m) -print(str(m) == repr(m)) - diff --git a/tutorials/pyroot/ratioplot.py b/tutorials/pyroot/ratioplot.py deleted file mode 100644 index 31cc3ba2fabbf..0000000000000 --- a/tutorials/pyroot/ratioplot.py +++ /dev/null @@ -1,114 +0,0 @@ -## \file -## \ingroup tutorial_pyroot -## \notebook -## Display two histograms and their ratio. -## -## This program illustrates how to plot two histograms and their -## ratio on the same canvas. Original macro by Olivier Couet. -## -## \macro_code -## -## \author Michael Moran - -from ROOT import TCanvas, TColor, TGaxis, TH1F, TPad -from ROOT import kBlack, kBlue, kRed - - -def createH1(): - h1 = TH1F("h1", ("Two gaussian plots and their ratio; x title; h1 and h2" - " histograms"), 100, -5, 5) - h1.SetLineColor(kBlue+1) - h1.SetLineWidth(2) - h1.FillRandom("gaus") - h1.GetYaxis().SetTitleSize(20) - h1.GetYaxis().SetTitleFont(43) - h1.GetYaxis().SetTitleOffset(1.55) - h1.SetStats(0) - return h1 - - -def createH2(): - h2 = TH1F("h2", "h2", 100, -5, 5) - h2.FillRandom("gaus") - h2.SetLineColor(kRed) - h2.SetLineWidth(2) - return h2 - - -def createRatio(h1, h2): - h3 = h1.Clone("h3") - h3.SetLineColor(kBlack) - h3.SetMarkerStyle(21) - h3.SetTitle("") - h3.SetMinimum(0.8) - h3.SetMaximum(1.35) - # Set up plot for markers and errors - h3.Sumw2() - h3.SetStats(0) - h3.Divide(h2) - - # Adjust y-axis settings - y = h3.GetYaxis() - y.SetTitle("ratio h1/h2 ") - y.SetNdivisions(505) - y.SetTitleSize(20) - y.SetTitleFont(43) - y.SetTitleOffset(1.55) - y.SetLabelFont(43) - y.SetLabelSize(15) - - # Adjust x-axis settings - x = h3.GetXaxis() - x.SetTitleSize(20) - x.SetTitleFont(43) - x.SetTitleOffset(4.0) - x.SetLabelFont(43) - x.SetLabelSize(15) - - return h3 - - -def createCanvasPads(): - c = TCanvas("c", "canvas", 800, 800) - # Upper histogram plot is pad1 - pad1 = TPad("pad1", "pad1", 0, 0.3, 1, 1.0) - pad1.SetBottomMargin(0) # joins upper and lower plot - pad1.SetGridx() - pad1.Draw() - # Lower ratio plot is pad2 - c.cd() # returns to main canvas before defining pad2 - pad2 = TPad("pad2", "pad2", 0, 0.05, 1, 0.3) - pad2.SetTopMargin(0) # joins upper and lower plot - pad2.SetBottomMargin(0.2) - pad2.SetGridx() - pad2.Draw() - - return c, pad1, pad2 - - -def ratioplot(): - # create required parts - h1 = createH1() - h2 = createH2() - h3 = createRatio(h1, h2) - c, pad1, pad2 = createCanvasPads() - - # draw everything - pad1.cd() - h1.Draw() - h2.Draw("same") - # to avoid clipping the bottom zero, redraw a small axis - h1.GetYaxis().SetLabelSize(0.0) - axis = TGaxis(-5, 20, -5, 220, 20, 220, 510, "") - axis.SetLabelFont(43) - axis.SetLabelSize(15) - axis.Draw() - pad2.cd() - h3.Draw("ep") - -# To hold window open when running from command line -# text = raw_input() - - -if __name__ == "__main__": - ratioplot()