Ignore:
Files:
7 added
5 deleted
42 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/canvasmain.py

    r11557 r11598  
    399399                    ) 
    400400 
     401        self.open_and_freeze_action = \ 
     402            QAction(self.tr("Open and Freeze"), self, 
     403                    objectName="action-open-and-freeze", 
     404                    toolTip=self.tr("Open a new scheme and freeze signal " 
     405                                    "propagation."), 
     406                    triggered=self.open_and_freeze_scheme 
     407                    ) 
     408 
    401409        self.save_action = \ 
    402410            QAction(self.tr("Save"), self, 
     
    576584        file_menu.addAction(self.new_action) 
    577585        file_menu.addAction(self.open_action) 
     586        file_menu.addAction(self.open_and_freeze_action) 
    578587        file_menu.addAction(self.reload_last_action) 
    579588 
     
    881890        else: 
    882891            return QDialog.Rejected 
     892 
     893    def open_and_freeze_scheme(self): 
     894        """ 
     895        Open a new scheme and freeze signal propagation. Return 
     896        QDialog.Rejected if the user canceled the operation and 
     897        QDialog.Accepted otherwise. 
     898 
     899        """ 
     900        frozen = self.freeze_action.isChecked() 
     901        if not frozen: 
     902            self.freeze_action.trigger() 
     903 
     904        state = self.open_scheme() 
     905        if state == QDialog.Rejected: 
     906            # If the action was rejected restore the original frozen state 
     907            if not frozen: 
     908                self.freeze_action.trigger() 
     909        return state 
    883910 
    884911    def open_scheme_file(self, filename): 
  • Orange/OrangeCanvas/main.py

    r11535 r11596  
    181181                log.info("%r style sheet not found.", stylesheet) 
    182182 
    183     if stylesheet_string is not None: 
    184         app.setStyleSheet(stylesheet_string) 
    185  
    186183    # Add the default canvas_icons search path 
    187184    dirpath = os.path.abspath(os.path.dirname(OrangeCanvas.__file__)) 
     
    189186 
    190187    canvas_window = CanvasMainWindow() 
     188 
     189    if stylesheet_string is not None: 
     190        canvas_window.setStyleSheet(stylesheet_string) 
    191191 
    192192    if not options.force_discovery: 
  • Orange/OrangeWidgets/OWBaseWidget.py

    r11548 r11592  
    44# A General Orange Widget, from which all the Orange Widgets are derived 
    55# 
     6import warnings 
     7 
    68from Orange.utils import environ 
    79from Orange.orng.orngEnviron import directoryNames as old_directory_names 
     
    204206        self.__wasShown = False 
    205207        self.__progressBarValue = -1 
     208        self.__progressState = 0 
    206209 
    207210    # uncomment this when you need to see which events occured 
     
    729732        if self.progressBarHandler: 
    730733            self.progressBarHandler(self, 0) 
    731         self.processingStateChanged.emit(1) 
     734 
     735        if self.__progressState != 1: 
     736            self.__progressState = 1 
     737            self.processingStateChanged.emit(1) 
    732738 
    733739    def progressBarSet(self, value, processEventsFlags=QEventLoop.AllEvents): 
     
    740746        old = self.__progressBarValue 
    741747        if value > 0: 
     748            if self.__progressState != 1: 
     749                warnings.warn("progressBarSet() called without a " 
     750                              "preceding progressBarInit()", 
     751                              stacklevel=2) 
     752                self.__progressState = 1 
     753                self.processingStateChanged.emit(1) 
     754 
    742755            self.__progressBarValue = value 
    743756            usedTime = max(1, time.time() - self.startTime) 
     
    780793        if self.progressBarHandler: 
    781794            self.progressBarHandler(self, 101) 
    782         self.processingStateChanged.emit(0) 
     795 
     796        if self.__progressState != 0: 
     797            self.__progressState = 0 
     798            self.processingStateChanged.emit(0) 
    783799 
    784800    # handler must be a function, that receives 2 arguments. First is the widget instance, the second is the value between -1 and 101 
  • Orange/data/discretization.py

    r11576 r11586  
    11import Orange 
    22 
    3 from Orange.core import\ 
    4     EquiNDiscretization as EqualFreq,\ 
    5     EquiDistDiscretization as EqualWidth,\ 
    6     EntropyDiscretization as Entropy,\ 
    7     BiModalDiscretization as BiModal,\ 
    8     Preprocessor_discretize 
    9  
     3from Orange.core import Preprocessor_discretize 
    104 
    115class DiscretizeTable(object): 
     
    3125    """ 
    3226    def __new__(cls, data=None, features=None, discretize_class=False, 
    33                 method=EqualFreq(n=3), clean=True): 
     27                method=Orange.feature.discretization.EqualFreq(n=3), clean=True): 
    3428        if data is None: 
    3529            self = object.__new__(cls) 
     
    4135 
    4236    def __init__(self, features=None, discretize_class=False, 
    43                  method=EqualFreq(n=3), clean=True): 
     37                 method=Orange.feature.discretization.EqualFreq(n=3), clean=True): 
    4438        self.features = features 
    4539        self.discretize_class = discretize_class 
  • Orange/fixes/fix_changed_names.py

    r11004 r11585  
    8484           "orange.EntropyDiscretization": "Orange.feature.discretization.Entropy", 
    8585           "orange.EquiDistDiscretization": "Orange.feature.discretization.EqualWidth", 
    86            "orange.EquiNDiscretization": "Orange.data.discretization.EqualFreq", 
    87            "orange.BiModalDiscretization": "Orange.data.discretization.BiModal", 
     86           "orange.EquiNDiscretization": "Orange.feature.discretization.EqualFreq", 
     87           "orange.BiModalDiscretization": "Orange.feature.discretization.BiModal", 
    8888 
    8989           "orngFSS.attMeasure": "Orange.feature.scoring.score_all", 
  • Orange/orng/orngPCA.py

    r9676 r11590  
    99from numpy import sqrt, abs, dot, transpose 
    1010from numpy.linalg import eig, inv 
    11  
    12 mathlib_import = True 
    13 try: 
    14     import matplotlib.pyplot as plt 
    15 except: 
    16     import warnings 
    17     warnings.warn("Importing of matplotlib has failed.\nPlotting functions will be unavailable.") 
    18     mathlib_import = False 
    1911 
    2012#color table for biplot 
     
    354346            If None, plot will be shown 
    355347        """ 
    356  
    357         if not mathlib_import: 
    358             raise orange.KernelException, "Matplotlib was not imported!" 
    359  
    360         #plt.clf() -> opens two windows 
     348        import pylab as plt 
    361349        fig = plt.figure() 
    362350        ax = fig.add_subplot(111) 
     
    392380            If None, plot will be shown 
    393381        """ 
    394  
    395         if not mathlib_import: 
    396             raise orange.KernelException, "Matplotlib was not imported!" 
     382        import pylab as plt 
    397383 
    398384        if self._dataMatrix == None: 
  • Orange/testing/regression/tests_20/exclude-from-regression.txt

    r10290 r11588  
    2323reference_matrix.py 
    2424__init__.py 
     25reference_c45.py 
  • Orange/testing/testing.py

    r11088 r11580  
    614614    if getattr(__IPYTHON__.shell, "call_pdb", None): # Is pdb enabled 
    615615        enable_pdb() 
    616 except NameError: 
     616except: 
    617617    pass 
    618618 
  • Orange/testing/unit/tests/__init__.py

    r9679 r11582  
     1import os 
     2import unittest 
     3if not hasattr(unittest.TestLoader, 'discover'): 
     4    import unittest2 as unittest 
     5 
     6 
     7def suite(): 
     8    test_dir = os.path.dirname(__file__) 
     9    return unittest.TestLoader().discover(test_dir, ) 
     10 
     11test_suite = suite() 
     12 
     13 
     14if __name__ == '__main__': 
     15    unittest.main(defaultTest='suite') 
  • Orange/testing/unit/tests/test_regression.py

    r10655 r11591  
    77import subprocess 
    88 
     9import Orange 
    910from Orange.utils import environ 
    1011from Orange.testing import testing 
     
    1213 
    1314class TestRegression(unittest.TestCase): 
    14  
    1515    PLATFORM = sys.platform 
    1616    PYVERSION = sys.version[:3] 
    1717    STATES = ["OK", "timedout", "changed", "random", "error", "crash"] 
     18    maxDiff = None 
     19 
     20    def __init__(self, methodName='runTest'): 
     21        super(TestRegression, self).__init__(methodName) 
     22        self.orange_dir = os.path.dirname(Orange.__file__) 
     23        self.orange_dir = os.path.join(self.orange_dir, '..') 
     24        self.orange_dir = os.path.realpath(self.orange_dir) 
    1825 
    1926    def setUp(self): 
    20         pass 
     27        sys.path.append(self.orange_dir) 
     28 
     29    def tearDown(self): 
     30        del sys.path[-1] 
    2131 
    2232    def test_regression_on(self, roottest, indir, outdir, name): 
     
    3848        stdout, stderr = p.communicate() 
    3949        rv = stdout.strip().lower() 
     50        if rv == 'error': 
     51            self.assertEqual(stderr.split('\n'), []) 
     52        elif rv == 'changed': 
     53            expected_results = self.get_expected_results(outdir, name) 
     54            actual_results = self.get_actual_results(outdir, name) 
     55            self.assertEqual(actual_results, expected_results) 
     56 
     57 
    4058        self.assertEqual(rv, "ok", "Regression test %s: %s" % (rv, name) \ 
    4159                        if stderr == "" else \ 
    4260                        "Regression test %s: %s\n\n%s" % (rv, name, stderr)) 
    4361        os.chdir(tmpdir) 
     62 
     63    def get_expected_results(self, outputdir, name): 
     64        expected_results = "%s/%s.%s.%s.txt" % (outputdir, name, sys.platform, sys.version[:3]) 
     65        if not os.path.exists(expected_results): 
     66            expected_results = "%s/%s.%s.txt" % (outputdir, name, sys.platform) 
     67            if not os.path.exists(expected_results): 
     68                expected_results = "%s/%s.txt" % (outputdir, name) 
     69 
     70        with open(expected_results, 'r') as results: 
     71            return results.read().split('\n') 
     72 
     73    def get_actual_results(self, outputdir, name): 
     74        for state in TestRegression.STATES: 
     75            actual_results = "%s/%s.%s.%s.%s.txt" % ( 
     76                outputdir, name, TestRegression.PLATFORM, 
     77                TestRegression.PYVERSION, state) 
     78 
     79            if os.path.exists(actual_results): 
     80                with open(actual_results, 'r') as results: 
     81                    return results.read().split('\n') 
     82 
    4483 
    4584root = os.path.normpath(os.path.join(environ.install_dir, "..")) 
  • Orange/utils/addons.py

    r11094 r11597  
    6464with closing(open_addons()) as addons: 
    6565    addons_corrupted = len(addons)==0 
     66 
     67del addons 
    6668 
    6769addon_refresh_callback = [] 
  • README.txt

    r10845 r11589  
    1919in that file for instructions on how to do that). 
    2020 
     21Running tests 
     22------------- 
     23After Orange is installed, you can check if everything is working OK by running the included tests:: 
     24 
     25    python setup.py test 
     26 
     27This command runs all the unit tests and documentation examples. Some of the latter have additional dependencies you can satisfy by installing matplotlib, PIL and scipy. 
     28 
    2129Starting Orange Canvas 
    2230---------------------- 
  • docs/development/rst/c.rst

    r11577 r11587  
    33################################ 
    44 
    5 This page is a draft of documentation for writing Orange extensions in C++. It has been written quite a while ago and never really polished, but we publish it due to requests and questions about the topic. 
    6  
    7 This page starts with a simple example of a C function that gets the data from Orange's example table and computes Delaunay triangulation for a chosen pair of (continuous) attributes. The result is returned as ``orange.Graph``. Then follows a more complex example that shows how to derive a new class from one of Orange's classes and make it accessible from Python. Finally, there's a more concise description of Orange's interface to Python and the tools we developed for making our lives easier. 
    8  
    9 *************************************** 
    10 A simple example: Exporting function(s) 
    11 *************************************** 
    12  
    13 If you plan to develop your own extensions for Orange, you'll need the Orange sources. Besides headers, you will certainly need the C files for stealing parts of the code from them. So download them if you haven't already done so. The example shown in this section is from directory orange/orangeom, files triangulate.cpp and orangeom.cpp. 
    14  
    15 We also assume that you have at least a vague idea about the Python API. If you don't, read the Python documentation first. Don't be afraid, API is fairly simple and very nicely organized. 
    16  
    17 We shall write a function that gets examples, indices/names/descriptors of two attributes and the number edge types. The two attributes need to be discrete; the function treats the values of these two attributes for each example as a point in a plane and computes the Delaunay triangulation, which is returned as ``orange.Graph``. 
    18  
    19 The function should be declared like this:: 
    20  
    21     PyObject *triangulate(PyObject *, PyObject *args, PyObject *) 
    22         PYARGS(METH_VARARGS, "(examples[, attr1, attr2, nEdgeTypes]) -> Graph") 
    23  
    24 This all should be written in a single line. Function arguments and result are like for any Python function (as you have learned while reading Python's documentation), while the macro PYARGS marks the function for export to Python. The macro is dummy and serves only as a marker. The first argument tells Python about the types of arguments (again, you've seen the constant METH_VARARGS in Python's docs), and the second string describes the functions arguments and/or what the function does. 
    25  
    26 We shall close the function in ``PyTRY`` - ``PyCATCH`` block. This catches any C++ exceptions that Orange might throw and turns them into Python error messages. You should do this with any your function that calls orange's functions to be sure that you catch anything that's thrown from in there. :: 
    27  
    28     { 
    29         PyTRY 
    30         ... 
    31         PyCATCH 
    32     } 
    33  
    34 Now for the real code. First we need to get the arguments and check their types. :: 
    35  
    36     PExampleGenerator egen; 
    37     int nEdgeTypes = 1; 
    38     PyObject *pyvar1 = NULL, *pyvar2 = NULL; 
    39     int attr1 = 0, attr2 = 1; 
    40     if (!PyArg_ParseTuple(args, "O&|OOi:triangulate", pt_ExampleGenerator, &egen, &pyvar1, &pyvar2, &nEdgeTypes) 
    41         || pyvar1 && !varNumFromVarDom(pyvar1, egen->domain, attr1) 
    42         || pyvar2 && !varNumFromVarDom(pyvar2, egen->domain, attr2)) 
    43       return NULL; 
    44  
    45     if ((egen->domain->attributes->at(attr1)->varType != TValue::FLOATVAR) 
    46             || (egen->domain->attributes->at(attr2)->varType != TValue::FLOATVAR)) 
    47         PYERROR(PyExc_TypeError, "triangulate expects continuous attributes", NULL); 
    48  
    49 For the arguments of ``PyArg_ParseTuple`` see the corresponding Python documentation. Here we accept from one to four arguments. The first is a table of examples, which gets converted to ``PExampleGenerator`` by a converter function ``pt_ExampleGenerator``. The next two are read as Python objects and can be, as far as ``PyArg_ParseTuple`` is concerned, of any type. The last argument is an integer. 
    50  
    51 We use Orange's function ``varNumFromVarDom`` to convert what the user passed as an attribute to an attribute index. (This function is not documented anywhere, but you can discover such functions by looking at the orange's functions that do similar things as the function you plan to write.) If the argument is already an integer, it is returned as such; otherwise the example table's domain (``egen->domain``) is used to convert a name or an attribute descriptor to an integer index. 
    52  
    53 We then check the attribute's type and complain if they are not continuous. Macro ``PYERROR``, defined in Orange, is used to set the error type (``PyExc_TypeError``) and message, and to send the corresponding result (in Python ``NULL`` always signifies an error). 
    54  
    55 For the actual triangulation we shall use Wml, which expects the arguments in a structure ``Vector2<float>``. :: 
    56  
    57     const int nofex = egen->numberOfExamples(); 
    58  
    59     Vector2<float> *points = new Vector2<float>[nofex]; 
    60     Vector2<float> *pi = points; 
    61  
    62     PEITERATE(ei, egen) { 
    63         if ((*ei)[attr1].isSpecial() || (*ei)[attr2].isSpecial()) 
    64             PYERROR(PyExc_AttributeError, "triangulate cannod handle unknown values", NULL); 
    65  
    66         *(pi++) = Vector2<float>((*ei)[attr1].floatV, (*ei)[attr2].floatV); 
    67     } 
    68  
    69     int nTriangles; 
    70     int *triangles, *adjacent; 
    71     Delaunay2a<float> delaunay(nofex, points, nTriangles, triangles, adjacent); 
    72     delete adjacent; 
    73  
    74 We have used a macro ``PEITERATE(ei, egen)`` for iteration across the example table, where ``egen`` is an example generator and ``ei`` is an iterator (declared by the macro). If we wanted, we could use an equivalent ``for`` loop: ``for(TExampleIterator ei(egen->begin()); ei; ++ei)``. Of the two results computed by ``Delaunay2a&lt;float&gt;``, we immediately discard ``adjacent``, while from ``triangles`` we shall construct the graph. :: 
    75  
    76     TGraph *graph = new TGraphAsList(nofex, nEdgeTypes, 0); 
    77     PGraph wgraph = graph; 
    78     try { 
    79         for(int *ti = triangles, *te = ti+nTriangles*3; ti!=te; ti+=3) { 
    80             for(int se = 3; se--; ) { 
    81                 float *gedge = graph->getOrCreateEdge(ti[se], ti[(se+1) % 3]); 
    82                 for(int et = nEdgeTypes; et--; *gedge++ = 1.0); 
    83             } 
    84         } 
    85     } 
    86     catch (...) { 
    87         delete triangles; 
    88         throw; 
    89     } 
    90     delete triangles; 
    91  
    92 The ``graph`` is immediately wrapped into an instance of ``PGraph``. If anything fails, for instance, if an exception occurs in the code that follows, ``graph`` will be deallocated automatically so we don't need to (and even mustn't!) care about it. However, we have to be careful about the ``triangles``, we need another ``try``-``catch`` to deallocate it in case of errors. Then follows copying the data from ``triangles`` into ``graph`` (we won't explain it here, look at Wml's documentation if you are that curious). 
    93  
    94 ``TGraph`` is Orange's class and ``PGraph`` is a wrapper around it. Python can use neither of them, the only type of result that the function can return is a Python object, so we conclude by:: 
    95  
    96     PyObject *res = WrapOrange(wgraph); 
    97     return res; 
    98  
    99 (Don't worry about double wrappers. ``WrapOrange``, which wraps Orange objects into ``PyObject *`` technically does the opposite - it partially unwraps ``PGraph`` which already includes a ``PyObject *``. See the Orange's garbage collector, garbage.hpp, if you need to know.) 
    100  
    101 There's another detail in the actual function ``triangulate`` which is rather dirty and quite specific and rare, so we won't explain it here. If you're looking at triangulate.cpp and wonder what it means, just ignore it. It's not important. 
    102  
    103 We have omitted a few #includes above the function. We need a few wml headers. Besides that, all extensions should include orange_api.hpp that defines several macros and similar. In our case, we also needed to include graph.hpp and examplegen.hpp which define the classes used in our function. 
    104  
    105 What remains is to export this function to Python. We need to construct a new module, it's name will be ``orangeom`` and it will export the function triangulate. We've already mentioned that this has something to do with ``PYARGS`` macro. It has two arguments, the first, ``METH_VARARGS`` giving the number and type of arguments, while the second is the documentation string. Assuming that ``triangulate.cpp`` is in directory source/orangeom, so source/orange is its sibling directory, we need to run ../pyxtract/pyxtract.py like this:: 
    106  
    107     python ../pyxtract/pyxtract.py -m -n orangeom -l ../orange/px/stamp triangulate.cpp 
    108  
    109 Do yourself a favour and put this line into a batch file. See the ``_pyxtract.bat`` files in various Orange's directories. 
    110  
    111 Option ``-m`` tells pyxtract to ``m``ake the file, ``-n orangeom`` gives the name of the module to be produced and ``-l ../orange/px/stamp`` is similar to gcc's ``-l`` - it tells pyxtract to include a "library" with all Orange's objects (don't worry, it's not code, there are just things that pyxtract has written down for himself). Then follow the names of the files with any exported functions and classes. In our case, this is a single file, triangulate.cpp. 
    112  
    113 What do we get from pyxtract? A few files we don't need and one more we don't care about. The only file of real interest for us is initialization.px. You can see the file for yourself, but omitting a few unimportant points, it looks like this. :: 
    114  
    115     PyMethodDef orangeomFunctions[] = { 
    116         {"triangulate", (binaryfunc)triangulate, METH_VARARGS, "(examples[, attr1, attr2, nEdgeTypes]) -> Graph"}, 
    117         {NULL, NULL} 
    118     }; 
    119  
    120     PyObject *orangeomModule; 
    121  
    122     extern "C" ORANGEOM_API void initorangeom() 
    123     { 
    124         if (!initorangeomExceptions()) 
    125             return; 
    126  
    127         gcorangeomUnsafeStaticInitialization(); 
    128         orangeomModule = Py_InitModule("orangeom", orangeomFunctions); 
    129     } 
    130  
    131 ``initorangeom`` is a function that will be called by Python when the module is initialized. Basically, it calls ``initorangeomExceptions``, ``gcorangeomUnsafeInitialization`` and then ``Py_InitModule`` whom it tells the module name and gives it the list of function that the module exports - in our case, ``orangeomFunctions`` has only a pointer to ``triangulate`` we wrote above. 
    132  
    133 Functions ``initorangeomExceptions``, ``gcorangeomUnsafeInitialization`` initialize the exceptions your module will throw to Python and initializes the stuff that cannot be initialized before the class types are ready. Both functions need to be provided by our module, but since we don't have any work for them, we'll just define them empty. 
    134  
    135 So, we need to write a file that will include initialization.px. It should be like this. :: 
    136  
    137     #include "orange_api.hpp" 
    138  
    139     #ifdef _MSC_VER 
    140         #define ORANGEOM_API __declspec(dllexport) 
    141     #else 
    142         #define ORANGEOM_API 
    143     #endif 
    144  
    145  
    146     bool initExceptions() 
    147     { return true; } 
    148  
    149     void gcUnsafeStaticInitialization() 
    150     {} 
    151  
    152     #include "px/initialization.px" 
    153  
    154 We simplified the definition of ``ORANGEOM_API`` for this guide. If you ever wrote a DLL yourself, you probably know what the complete definition would look like, but for our module this will suffice. You can see the complete definition in ``orangeom.cpp`` if you want to. Then follow the two functions that we need to provide only because they are called by ``initorangeom``. Finally, we include initialization.px which takes care of the rest. 
    155  
    156 Setting the compiler options? Eh, just copy them from some project that is delivered together with Orange. If you want to do it yourself, this is the complete recipe (unless we've forgotten about something :) 
    157  
    158 * Add an environment variable ``PYTHON=c:\python23``, or wherever your 
    159 Python is installed. This will simplify the options and also help 
    160 you upgrade to a newer version. (If you don't want to do this, 
    161 just replace the below reference ``$(PYTHON)`` with ``c:\python23``.) 
    162  
    163 * Open the Orange workspace (sources/orange.dsw) and add your stuff as new projects. 
    164 Add new project into workspace". You need a "Win32 Dynamic-link 
    165 Library"; create it as an empty or simple project. 
    166 This document will suppose you've put it into a subdirectory 
    167 of ``orange/source`` (eg ``orange/source/myproject``) 
    168  
    169 * Edit the project settings. Make sure to edit the settings for 
    170 both Release and Debug version - or for Release, in the unlikely case that you won't 
    171 need to debug. 
    172   * In C/C++, Preprocessor add include directories ``../include,../orange,px,ppp,$(PYTHON)/include``. 
    173       If VC later complains that it cannot find Python.h, locate the 
    174       file yourself and fix the last include directory accordingly. 
    175    
    176  
    177   * In C/C++, C++ Language, check Enable Run-Time Type Information. 
    178  
    179   * In C/C++, Code generation, under Use run-time library choose 
    180       Multithread DLL for Release and Debug Multithread DLL for Debug 
    181       version. 
    182  
    183   * In Link, Input, add ``$(PYTHON)\libs`` and ``../../lib`` to 
    184       Additional Library Path. (The first is the path to Python.lib, 
    185       and the second to orange.lib; locate them manually if you need to.) 
    186  
    187   * In Link, General, change the Output file name to ../../mymodule.pyd for the Release Build and to ../../mymodule_d.pyd for debug. You can use .dll instead of .pyd. 
    188  
    189   * In Post-build step, add "copy Release\orangeom.lib ..\..\lib\orangeom.lib" for the Release version, and "copy Debug\orangeom_d.lib ..\..\lib\orangeom_d.lib" for the Debug. 
    190  
    191   * In Debug Build, go to tab Debug, General, set the Executable for debug session to "c:\python23\python_d.exe". 
    192  
    193 ################################################################## 
    194 General mechanism and tools used in Orange's C to Python interface 
    195 ################################################################## 
    196  
    197 This page is not meant to be a comprehensive and self-sufficient guide to exporting C++ classes into Python using the Orange's machinery. To learn how to export your functions and classes to Python, you should open some Orange's files (lib_*.cpp and cls_*.cpp) and search for examples there, while this page will hopefully help you to understand them. The easiest way to write your interfaces will be to copy and modify the existing Orange's code. (This is what we do all the time. :) 
    198  
    199 If you are writing extension modules for Python, you by no means *have to* use Orange's scripts (pyxtract, pyprops) compiling the interface. Compile your interface with any tool you want, e.g. Swig. The only possible complications would arise if you are deriving and exporting new C++ classes from Orange's classes (to those that know something on the topic: Orange uses a slightly extended version of ``PyTypeObject``, and the derived classes can't return to the original). If need be, ask and we may try to provide some more insight and help to overcome them. 
    200  
    201 ################################ 
    202 Orange's C++ to Python interface 
    203 ################################ 
    204  
    205 Instead of general 3rd party tools (Swig, Sip, PyBoost...) for interfacing between C++ and Python, Orange uses it's own set of tools. (We are working towards making them general, ie also useful for other applications.) These tools (two Python scripts, actually, pyprops and pyxtract) require more manual programming than other tools, but on the other hand, the result is a tighter and nicer coupling of Orange with Python. 
    206  
    207 In short, to expose a C++ object to Python, we mark the attributes to be exported by a comment line starting with ``//P``. Besides, we need to either declare which general constructor to use or program a special one (this constructor will be in place of what would in Python be defined as the function __new__), and program the interfaces to those C++ member functions that we want exported. In order for pyxtract to recognize them, the function name should be composed of the class name and method name, separated by an underscore, and followed by a certain keyword. When we simply give the access to unaltered C++ functionality, the interface functions will only have a few-lines. When we want to make the "Python" version of the function more friendly, eg. allow various types of arguments or fitting the default arguments according to the given, these functions will be longer, but the pay-off is evident. We argue that a few-line function is not more of inconvenience than having to write export declarations (as is the case with Sip at least, I guess). 
    208  
    209 To define a non-member function, we write the function itself according to the instructions in Python's manual (see the first chapter of "Extending and Embedding the Python Interpreter") and then mark it with a specific keyword. Pyxtract will recognize the keyword and add it to the list of exported functions. 
    210  
    211 Orange's core C++ objects are essentially unaware of Python above them. However, to facilitate easier interface with Python, each Orange class contains a static pointer to a list of its ``properties'' (attributes, in Python terminology). Accessing the object's attributes from Python goes through that list. These lists, however, are general and would be equally useful if we were to interface Orange to some other language (eg Perl) or technology (ActiveX, Corba). 
    212  
    213 The only exception to the Orange's independency of Python is garbage collection: Orange uses Python's garbage collection for the sake of efficiency and simplicity. Each Orange's pointer (except for the short-term ones) is wrapped into a wrapper of type ``PyObject *``. Dependency of Orange on Python is not strong - if we wanted to get rid of it, we'd only need to write our own garbage collection (or steal the Python's). ``PyObject *`` is the basic Python's type which stores some garbage collection related stuff, a pointer to the class type (``PyTypeObject *``) and the class specific data. The specific data is, in Orange's case, a pointer to the Orange object. Class type is a structure that contains the class name, pointers to function that implement the class special methods (such as indexing, printing, memory allocation, mathematical operations) and class members. 
    214  
    215 We won't go deeper into explaining ``PyTypeObject`` since this is done in Python documentation. What you need to know is that for each Orange class that is accessible from Python, there is a corresponding ``PyTypeObject`` that defines its methods. For instance, the elements of ``ExampleTable`` (examples) can be accessed through indexing because we defined a C function that gets an index (and the table, of course) and returns the corresponding example, and we've put a pointer to this method into the ``ExampleTable``'s ``PyTypeObject`` (actually, we didn't do it manually, this is what pyxtract is responsible for). This is equivalent to overloading the operator [] in C++. Here's the function (with error detection removed for the sake of clarity). :: 
     5This page gives an introduction to extending Orange in C++ with emphasis on 
     6how to define interfaces to Python. Besides reading this page, we recommend 
     7studying some of existing extension modules like orangeom, and the Orange's 
     8interface itself. 
     9 
     10We shall first present a general picture and then focus on specific parts of the 
     11interface. 
     12 
     13Instead of general tools for creating interfaces between C++ and Python 
     14(Swig, Sip, PyBoost...), Orange uses its own specific set of tools. 
     15 
     16To expose a C++ object to Python, we need to mark them as exportable, select a 
     17general constructor template to use or program a specific one, we have to mark 
     18the attributes to be exported, and provide the interfaces for C++ member 
     19functions. When we give the access to mostly C++ code as it is, the interface 
     20functions have only a few lines. When we want to make the exported function more 
     21friendly, eg. allow various types of arguments or fitting the default arguments 
     22according to the given ones, these functions are longer. 
     23 
     24To define a non-member function, we write the function itself as described in 
     25the Python's manual (see the first chapter of "Extending and Embedding the 
     26Python Interpreter") and then mark it with a specific keyword. 
     27Pyxtract will recognize the keyword and add it to the list of exported functions. 
     28 
     29To define a special method, one needs to provide a function with the appropriate 
     30name constructed from the class name and the special method's name, which is the 
     31same as in Python's PyTypeObjects. 
     32 
     33For instance, the elements of ``ExampleTable`` (examples) can be accessed 
     34through indexing because we defined a C function that gets an index (and the 
     35table, of course) and returns the corresponding example. Here is the function 
     36(with error detection removed for the sake of clarity). :: 
    21637 
    21738    PyObject *ExampleTable_getitem_sq(PyObject *self, int idx) 
     
    22142    } 
    22243 
    223 Also, ``ExampleTable`` has a method ``sort([list-of-attributes])``. This is implemented through a C function that gets a list of attributes and calls the C++ class' method ``TExampleTable::sort(const vector&lt;int&gt; order)``. To illustrate, this is a slightly simplified function (we've removed some flexibility regarding the parameters and the exception handling). :: 
     44Also, ``ExampleTable`` has a non-special method ``sort([list-of-attributes])``. 
     45This is implemented through a C function that gets a list of attributes and 
     46calls the C++ class' method 
     47``TExampleTable::sort(const vector<int> order)``. To illustrate, this is a 
     48slightly simplified function (we've removed some flexibility regarding the 
     49parameters and the exception handling). :: 
    22450 
    22551    PyObject *ExampleTable_sort(PyObject *self, PyObject *args) PYARGS(METH_VARARGS, "() -> None") 
     
    23561        varListFromDomain(PyTuple_GET_ITEM(args, 0), table->domain, attributes, true, true); 
    23662        vector<int> order; 
    237         for(TVarList::reverse_iterator vi(attributes.rbegin()), ve(attributes.rend()); vi!=ve; vi++) 
     63        for(TVarList::reverse_iterator vi(attributes.rbegin()), ve(attributes.rend()); vi!=ve; vi++) { 
    23864            order.push_back(table->domain->getVarNum(*vi)); 
    239  
     65        } 
    24066        table->sort(order); 
    24167        RETURN_NONE; 
    24268    } 
    24369 
    244 Basically, what the function does is it casts the ``PyObject *`` into the corresponding C++ object ("unwrapping"), reads the arguments, calls the C++ functions and returns the result (``None``, in this case). 
    245  
    246 There seem to be a lot of manual work involved in interfacing. Indeed there is, but this is exactly why Orange is so user friendly. The manual control of argument conversion gives a programmer the opportunity to program a function which accepts many different forms of arguments. The above function, for instances, can accept the list where the attributes can be specified by indices, names or descriptors, all corresponding to the ``ExampleTable`` which is being sorted. Inheritance of methods, on the other hand, ensures that only the methods that are truly specific for a class need to be coded. 
    247  
    248  
    249 The part of the interface that is programmed automatically is taken care of by two scripts. ``pyprops.py`` parses all Orange's header files and extracts all the class built-in properties. The result are lists of properties (attributes); nothing else needs to be done for the ``ExampleTable``'s attribute ``domain`` to be visible in Python, except for putting a ``// P`` after its declaration. 
    250  
    251 The second script is ``pyxtract.py``. It goes through the C++ files that contain the interface functions, such as those above. It recognizes the functions that implement special or member methods and constructs the corresponding ``PyTypeObject``s. It therefore relieves us from the most boring part of work, but permits us to program things like more intelligent arguments or error handling. 
     70The function casts the ``PyObject *`` into the 
     71corresponding C++ object, reads the arguments, calls the C++ 
     72functions and returns the result (``None``, in this case). 
     73 
     74Interfacing with Python requires a lot of manual work, but this gives a 
     75programmer the opportunity to provide a function which accepts many different 
     76forms of arguments. The above function, for instance, accepts a list in 
     77which attributes are specified by indices, names or descriptors, all 
     78corresponding to the ``ExampleTable`` which is being sorted. Inheritance of 
     79methods, on the other hand, ensures that only the methods that are truly 
     80specific for a class need to be coded. 
     81 
     82The part of the interface that is built automatically is taken care of by 
     83two scripts. ``pyprops`` parses all Orange's header files and extracts all 
     84the class built-in properties. The second is ``pyxtract``, which goes 
     85through the C++ files that contain the interface functions such as those above. 
     86It recognizes the functions that implement special or member methods and 
     87constructs the corresponding ``PyTypeObject``s. 
    25288 
    25389******* 
     
    25591******* 
    25692 
    257 Pyprops scans each hpp file for classes we want to export to Python (or, in general, some other scripting language). Properties can be ``bool``, ``int``, ``float``, ``string``, ``TValue`` or a wrapped Orange type. 
    258  
    259 Pyprops' parser is essentially a trivial finite automaton. Class definition needs to look as follows. :: 
     93Pyprops scans each hpp file for classes we want to export to Python). Properties 
     94can be ``bool``, ``int``, ``float``, ``string``, ``TValue`` or a wrapped Orange 
     95type. 
     96 
     97Class definition needs to look as follows. :: 
    26098 
    26199    class [ORANGE_API] <classname>; [: public <parentclass> ] 
    262100 
    263 This should be in a single line. To mark the class for export, this should be followed by ``__REGISTER_CLASS`` or ``__REGISTER_ABSTRACT_CLASS`` before any properties or components are defined. The difference between the two, as far as pyprops is concerned, is that abstract classes don't define the ``clone`` method. 
     101This should be in a single line. To mark the class for export, this should be 
     102followed by ``__REGISTER_CLASS`` or ``__REGISTER_ABSTRACT_CLASS`` before any 
     103properties or components are defined. The difference between the two, as far as 
     104pyprops is concerned, is that abstract classes do not define the ``clone`` 
     105method. 
    264106 
    265107To export a property, it should be defined like this. :: 
     
    267109    <type> <name> //P[R|O] [>|+<alias>] <description> 
    268110 
    269 Pyprops doesn't check the type and won't object if you use other types than those listed above. Linker will complain about missing symbols, though. ``//P`` signals that we want to export the property. If followed by R or O, the property is read-only or obsolete. The property can also have an alias name; > renames it and + adds an alias. Description is not used at the time, but it is nevertheless a good practice to provide it. 
     111Pyprops doesn't check the type and won't object if you use other types than 
     112those listed above. The error will be discovered later, during linking. ``//P`` 
     113signals that we want to export the property. If followed by ``R`` or ``O``, the 
     114property is read-only or obsolete. The property can also have an alias name; 
     115``>`` renames it and ``+`` adds an alias. 
    270116 
    271117Each property needs to be declared in a separate line, e.g. :: 
     
    274120    int y; //P; 
    275121 
    276 If we don't want to export a certain property, we don't need to. Just omit the ``//P``. An exception to this are wrapped Orange objects: for instance, if a class has a (wrapped) pointer to the domain, ``PDomain`` and it doesn't export it, pyxtract should still now about them because of the cyclic garbage collection. You should mark them by ``//C`` so that they are put into the list of objects that need to be counted. If you fail to do so, you'll have a memory leak. Luckily, this is a very rare situation; there are only two such components in relief.hpp. 
    277  
    278 If a class directly or indirectly holds references to any wrapped objects that are neither properties nor components, it will need to declare ``traverse`` and ``clear`` to include them in the garbage collection. Python documentation will tell you what these functions need to do, and you can look at several instances where we needed them in Orange. 
    279  
    280 Pyprops creates a ppp file for each hpp. The ppp file first ``#include``s the corresponding hpp file and then declares the necessary definition for each exported file. A list of properties store their names, descriptions, typeid's (RTTI), a class description for the properties' type, the properties' offset and the flags denoting read-only and obsolete properties. 
    281  
    282 Then comes a list of components' offsets, followed by a definition of classes static field ``st_classDescription`` and a virtual function ``classDescription`` that returns a pointer to it. Finally, if the class is not abstract, a virtual function ``clone`` is defined that returns a ``new`` instance of this class initialized, through a copy constructor, with an existing one. 
    283  
    284 ppp file contains definitions, so it has to be compiled only once. The most convenient way to do it is to include it in the corresponding cpp file. For instance, while many Orange's cpp files include domain.hpp, only domain.cpp includes domain.ppp instead. 
     122If we don't want to export a certain property, we omit the ``//P`` mark. An 
     123exception to this are wrapped Orange objects: for instance, if a class has a 
     124(wrapped) pointer to the domain, ``PDomain`` and it doesn't export it, pyxtract 
     125should still know about them because for the purpose of garbage collection. You 
     126should mark them by ``//C`` so that they are put into the list of objects that 
     127need to be counted. Failing to do so would cause a memory leak. 
     128 
     129If a class directly or indirectly holds references to any wrapped objects that 
     130are neither properties nor components, it needs to declare ``traverse`` and 
     131``clear`` as described in Python documentation. 
     132 
     133Pyprops creates a ppp file for each hpp, which includes the extracted 
     134information in form of C++ structures that compile into the interface. 
     135The ppp file needs to be included in the corresponding cpp file. For 
     136instance, domain.ppp is included in domain.cpp. 
    285137 
    286138******** 
     
    288140******** 
    289141 
    290 Pyxtract's job is to detect the functions that define special methods (such as printing, conversion, sequence and arithmetic related operations...) and member functions. Based on what it finds for each specific class, it constructs the corresponding ``PyTypeObject``s. For the functions to be recognized, they must follow a specific syntax. 
    291  
    292 There are two basic mechanisms used. Special functions are recognized by their definition (they need to return ``PyObject *``, ``void`` or ``int`` and their name must be of form &lt;classname&gt;_&lt;functionname&gt;). Member functions, inheritance relations, constants etc. are marked by macros such as ``PYARGS`` in the above definition of ``ExampleTable_sort``. These macros usually don't do anything, so C++ compiler ignores them - they are just markups for pyxtract. 
     142Pyxtract's job is to detect the functions that define special methods (such as 
     143printing, conversion, sequence and arithmetic related operations...) and member 
     144functions. Based on what it finds for each specific class, it constructs the 
     145corresponding ``PyTypeObject``s. For the functions to be recognized, they must 
     146follow a specific syntax. 
     147 
     148There are two basic mechanisms for marking the functions to export. Special 
     149functions are recognized by their definition (they need to return 
     150``PyObject *``, ``void`` or ``int`` and their name must be of form 
     151<classname>_<functionname>). Member functions, 
     152inheritance relations, constants etc. are marked by macros such as ``PYARGS`` 
     153in the above definition of ``ExampleTable_sort``. Most of these macros don't do 
     154anything except for marking stuff for pyxtract. 
    293155 
    294156Class declaration 
    295157================= 
    296158 
    297 Each class needs to be declared: pyxtract must know from which parent class the class it's derived. If it's a base class, pyxtract need to know the data structure for the instances of this class. As for all Python objects the structure must be "derived" from ``PyObject`` (the quotation mark are here because Python is written in C, so the subclasses are not derived in the C++ sense). Most objects are derived from Orange; the only classes that are not are ``orange.Example``, ``orange.Value`` and ``orange.DomainDepot`` (I forgot why the depot, but there had to be a strong reason). 
    298  
    299 Pyxtract should also know how the class is constructed - it can have a specific constructor, one of the general constructors or no constructor at all. 
    300  
    301  
    302 The class is declared in one of the following ways. 
     159Each class needs to be declared as exportable. If it's a base class, pyxtract 
     160needs to know the data structure for the instances of this class. As for all 
     161Python objects the structure must be "derived" from ``PyObject`` (Python is 
     162written in C, so the subclasses are not derived in the C++ sense but extend the 
     163C structure instead). Most objects are derived from Orange; the only exceptions 
     164are ``orange.Example``, ``orange.Value`` and ``orange.DomainDepot``. 
     165 
     166Pyxtract should also know how the class is constructed - it can have a specific 
     167constructor, one of the general constructors or no constructor at all. 
     168 
     169The class is declared in one of the following ways (here are some examples from 
     170actual Orange code). 
    303171 
    304172``BASED_ON(EFMDataDescription, Orange)`` 
     
    306174 
    307175``ABSTRACT(ClassifierFD, Classifier)`` 
    308     This defines an abstract class, which will never be constructed in the C++ code and not even pretend to be seen in Python. At the moment, the only difference between this ``BASED_ON`` and ``ABSTRACT`` is that the former can have pickle interface, while the latter don't need one.  
    309  
    310 Abstract C++ classes are not necessarily defined as ``ABSTRACT`` in the Python interface. For example, ``TClassifier`` is an abstract C++ class, but you can seemingly construct an instance of ``Classifier`` in Python. What happens is that there is an additional C++ class ``TClassifierPython``, which poses as Python's class ``Classifier``. So the Python class ``Classifier`` is not defined as ``ABSTRACT`` or ``BASED_ON`` but using the ``Classifier_new`` function, as described below. 
     176    This defines an abstract class, which will never be constructed in the C++ code. The only difference between this ``BASED_ON`` and ``ABSTRACT`` is that the former can have pickle interface, while the latter don't need one. 
     177 
     178Abstract C++ classes are not necessarily defined as ``ABSTRACT`` in the Python 
     179interface. For example, ``TClassifier`` is an abstract C++ class, but you can 
     180seemingly construct an instance of ``Classifier`` in Python. What happens is 
     181that there is an additional C++ class ``TClassifierPython``, which poses as 
     182Python's class ``Classifier``. So the Python class ``Classifier`` is not defined 
     183as ``ABSTRACT`` or ``BASED_ON`` but using the ``Classifier_new`` function, as 
     184described below. 
    311185 
    312186 
    313187``C_NAMED(EnumVariable, Variable, "([name=, values=, autoValues=, distributed=, getValueFrom=])")`` 
    314     ``EnumVariable`` is derived from ``Variable``. Pyxtract will also create a constructor which will as an optional argument accept the object's name. The third argument is a string that describes the constructor, eg. gives a list of arguments. IDEs for Python, such as PythonWin, will show this string in a balloon help while the programmer is typing. 
     188    ``EnumVariable`` is derived from ``Variable``. Pyxtract will also create a constructor which will accept the object's name as an optional argument. The third argument is a string that describes the constructor, eg. gives a list of arguments. IDEs for Python, such as PythonWin, will show this string in a balloon help while the programmer is typing. 
    315189 
    316190``C_UNNAMED(RandomGenerator, Orange, "() -> RandomGenerator")`` 
     
    318192 
    319193``C_CALL(BayesLearner, Learner, "([examples], [weight=, estimate=] -/-> Classifier")`` 
    320     ``BayesLearner`` is derived from ``Learner``. It will have a peculiar constructor. It will, as usual, first construct an instance of ``BayesLearner``. If no arguments are given (except for, possibly, keyword arguments), it will return the constructed instance. Otherwise, it will call the ``Learner``'s call operator and return its result instead of ``BayesLearner``.`` 
     194    ``BayesLearner`` is derived from ``Learner``. It will have a peculiar constructor. It will, as usual, first construct an instance of ``BayesLearner``. If no arguments are given (except for, possibly, keyword arguments), it will return the constructed instance. Otherwise, it will call the ``Learner``'s call operator and return its result instead of ``BayesLearner``. 
    321195 
    322196``C_CALL3(MakeRandomIndices2, MakeRandomIndices2, MakeRandomIndices, "[n | gen [, p0]], [p0=, stratified=, randseed=] -/-> [int]")`` 
     
    329203    This is for the base classes. ``Orange`` has no parent class. The C++ structure that stores it is ``TPyOrange``; ``TPyOrange`` is essentially ``PyObject`` (again, the structure always has to be based on ``PyObject``) but with several additional fields, among them a pointer to an instance of ``TOrange`` (the C++ base class for all Orange's classes). ``orange_dict`` is a name of ``TPyOrange``'s field that points to a Python dictionary; when you have an instance ``bayesClassifier`` and you type, in Python, ``bayesClassifier.someMyData=15``, this gets stored in ``orange_dict``. The actual mechanism behind this is rather complicated and you most probably won't need to use it. If you happen to need to define a class with ``DATASTRUCTURE``, you can simply omit the last argument and give a 0 instead. 
    330204 
    331 ``PyObject *ClassifierByLookupTable1_new(PyTypeObject *type, PyObject *args, PyObject *) BASED_ON(ClassifierByLookupTable, "(class-descriptor, descriptor)")`` 
    332     ``ClassifierByLookupTable1_new`` has a specific constructor. The general ones above couldn't be used since this constructor needs to have some arguments or for some other reason (eg. the user doesn't need to specify the arguments, but the C++ constructor for the corresponding C++ class requires them, so this interface function will provide the defaults). The constructor function needs to be defined like above (ie. &lt;classname&gt;_new), followed by a ``BASED_ON`` macro giving the parent class and the comment. The declaration must be written in a single line. Just for the illustration, the (simplified, no error handling) constructor looks like this :: 
    333  
    334     PyObject *ClassifierByLookupTable1_new(PyTypeObject *type, PyObject *args, PyObject *) BASED_ON(ClassifierByLookupTable, "(class-descriptor, descriptor)") 
    335     { 
    336         PyTRY 
    337             PVariable vcl, vvl; 
    338             PyObject *pyvlist = NULL, *pydlist = NULL; 
    339             if (!PyArg_ParseTuple(args, "O&O&|OO", cc_Variable, &vcl, cc_Variable, &vvl, &pyvlist, &pydlist)) 
    340                 PYERROR(PyExc_TypeError, 
    341                     "invalid parameter; two variables and, optionally, ValueList and DistributionList expected", 
    342                      NULL); 
    343  
    344             TClassifierByLookupTable1 *cblt = mlnew TClassifierByLookupTable1(vcl, vvl); 
    345             return initializeTables(pyvlist, pydlist, cblt) ? WrapNewOrange(cblt, type) : NULL; 
    346         PyCATCH 
    347     } 
    348  
    349 Function parses the arguments by calling ``PyArg_ParseTuple``, constructs an instance of ``ClassifierByLookupTable1``, initializes it and returns either the constructed object or NULL which signifies an error. ``PyTRY`` and ``PyCATCH`` catch the possible Orange's exception and turn them into Python exceptions. 
    350  
    351 Even if the class is defined by ``DATASTRUCTURE``, you can still specify a different constructor, most probably the last form of it (the ``_new`` function). In this case, specify a keyword ``ROOT`` as a parent and pyxtract will understand that this is the base class. 
    352  
    353 Object construction in Python is divided between two methods. The constructors we discussed above construct the essential part of the object - they allocate the necessary memory and initialize the fields far enough that the object is valid to enter the garbage collection. The second part is handled by the ``init`` method. It is, however, not forbidden to organize the things so that ``new`` does all the job. This is also the case in Orange. The only task left for ``init`` is to set any attributes that user gave as the keyword arguments to the constructor. 
    354  
    355 For instance, Python's statement ``orange.EnumVariable("a", values=["a", "b", "c"])`` is executed so that ``new`` constructs the variable and gives it the name, while ``init`` sets the ``values`` field. You don't need to do anything about it. 
    356  
    357 The ``new`` operator, however, sometimes also accepts keyword arguments. For instance, when constructing an ``ExampleTable`` by reading the data from file, you can specify a domain (using keyword argument ``domain``), a list of attributes to reuse if possible (``use``), you can tell it not to reuse the stored domain or not to store the newly constructed domain (``dontCheckStored``, ``dontStore``). After the ``ExampleTable`` is constructed, ``init`` is called to set the attributes. To tell it to ignore the keyword arguments that the constructor might (or had) used, we write the following. :: 
     205Even if the class is defined by ``DATASTRUCTURE``, you can still specify a 
     206different constructor, most probably the last form of it (the ``_new`` 
     207function). In this case, specify a keyword ``ROOT`` as a parent and pyxtract 
     208will understand that this is the base class. 
     209 
     210Object construction in Python is divided between two methods. The constructors 
     211we discussed above construct the essential part of the object - they allocate 
     212the necessary memory and initialize the fields far enough that the object is 
     213valid to enter the garbage collection. The second part is handled by the 
     214``init`` method. It is, however, not forbidden to organize the things so that 
     215``new`` does all the job. This is also the case in Orange. The only task left 
     216for ``init`` is to set any attributes that user gave as the keyword arguments to 
     217the constructor. 
     218 
     219For instance, Python's statement 
     220``orange.EnumVariable("a", values=["a", "b", "c"])`` is executed so that ``new`` 
     221constructs the variable and gives it the name, while ``init`` sets the 
     222``values`` field. 
     223 
     224The ``new`` operator can also accept keyword arguments. For 
     225instance, when constructing an ``ExampleTable`` by reading the data from a file, 
     226you can specify a domain (using keyword argument ``domain``), a list of 
     227attributes to reuse if possible (``use``), you can tell it not to reuse the 
     228stored domain or not to store the newly constructed domain (``dontCheckStored``, 
     229``dontStore``). After the ``ExampleTable`` is constructed, ``init`` is called to 
     230set the attributes. To tell it to ignore the keyword arguments that the 
     231constructor might (or had) used, we write the following. :: 
    358232 
    359233    CONSTRUCTOR_KEYWORDS(ExampleTable, "domain use useMetas dontCheckStored dontStore filterMetas") 
    360234 
    361 ``init`` will ignore all the keywords from the list. 
    362  
    363 Talking about attributes, here's another macro. You know you can assign arbitrary attributes to Orange classes. Let ``ba`` be an orange object, say ``orange.BayesLearner``. If you assign new attributes as usual directly, eg. ``ba.myAttribute = 12``, you will get a warning (you should use the object's method ``setattr(name, value)`` to avoid it). Some objects have some attributes that cannot be implemented in C++ code, yet they are usual and useful. For instance, ``Graph`` can use attributes ``objects``, ``forceMapping`` and ``returnIndices``, which can only be set from Python (if you take a look at the documentation on ``Graph`` you will see why these cannot be implemented in C++). Yet, since user are allowed to set these attributes and will do so often, we don't want to give warnings. We achieve this by :: 
     235There's another macro related to attributes. Let ``ba`` be an orange object, say 
     236an instance of ``orange.BayesLearner``. If you assign new attributes as usual 
     237directly, eg. ``ba.myAttribute = 12``, you will get a warning (you should use 
     238the object's method ``setattr(name, value)`` to avoid it). Some objects have 
     239some attributes that cannot be implemented in C++ code, yet they are usual and 
     240useful. For instance, ``Graph`` can use attributes ``objects``, ``forceMapping`` 
     241and ``returnIndices``, which can only be set from Python (if you take a look at 
     242the documentation on ``Graph`` you will see why these cannot be implemented in 
     243C++). Yet, since user are allowed to set these attributes and will do so often, 
     244we don't want to give warnings. We achieve this by :: 
    364245 
    365246    RECOGNIZED_ATTRIBUTES(Graph, "objects forceMapping returnIndices") 
    366  
    367 How do these things function? Well, it is not hard to imagine: pyxtract catches all such exceptions and stores the corresponding lists for each particular class. The ``init`` constructor then checks the list prior to setting attributes. Also the method for setting attributes that issues warnings for unknown attributes checks the list prior to complaining. 
    368247 
    369248 
     
    371250=============== 
    372251 
    373 Special methods act as the class built-in methods. They define what the type can do: if it, for instance, supports multiplication, it should define the operator that gets the object itself and another object and return the product (or throw an exception). If it allows for indexing, it defines an operator that gets the object itself and the index and returns the element. These operators are low-level; most can be called from Python scripts but they are also internally by Python. For instance, if ``table`` is an ``ExampleTable``, then ``for e in table:`` or ``reduce(f, table)`` will both work by calling the indexing operator for each table's element. 
    374  
    375 We shall here avoid the further discussion of this since the topic is adequately described in Python manuals (see "Extending and Embedding the Python Interpreter", chapter 2, "Defining New Types"). 
    376  
    377 To define a method for Orange class, you need to define a function named, ``&lt;classname&gt;_&lt;methodname&gt;``; the function should return either ``PyObject *``, ``int`` or ``void``. The function's head has to be written in a single line. Regarding the arguments and the result, it should conform to Python's specifications. Pyxtract will detect the methods and set the pointers in ``PyTypeObject`` correspondingly. 
    378  
    379 Here's a list of methods: the left column represents a method name that triggers pyxtract (these names generally correspond to special method names of Python classes as a programmer in Python sees them) and the second next is the name of the field in ``PyTypeObject`` or subjugated structures. See Python documentation for description of functions' arguments and results. Not all methods can be directly defined; for those that can't, it is because we either use an alternative method (eg. ``setattro`` instead of ``setattr``) or pyxtract gets or computes the data for this field in some other way. If you really miss something ... wasn't the idea of open source that you are allowed to modify the code (e.g. of pyxtract) yourself? 
     252Special methods act as the class built-in methods. They define what the type can 
     253do: if it, for instance, supports multiplication, it should define the operator 
     254that gets the object itself and another object and return the product (or throw 
     255an exception). If it allows for indexing, it defines an operator that gets the 
     256object itself and the index, and returns the element. These operators are 
     257low-level; most can be called from Python scripts but they are also internally 
     258by Python. For instance, if ``table`` is an ``ExampleTable``, then 
     259``for e in table:`` or ``reduce(f, table)`` will both work by calling the 
     260indexing operator for each table's element. 
     261For more details, consider the Python manual, chapter "Extending and 
     262Embedding the Python Interpreter" section "Defining New Types". 
     263 
     264To define a method for Orange class, you need to define a function named, 
     265``<classname>_<methodname>``; the function should return either 
     266``PyObject *``, ``int`` or ``void``. The function's head has to be written in a 
     267single line. Regarding the arguments and the result, it should conform to 
     268Python's specifications. Pyxtract will detect the methods and set the pointers 
     269in ``PyTypeObject`` correspondingly. 
     270 
     271Here is a list of methods: the left column represents a method name that 
     272triggers pyxtract (these names generally correspond to special method names of 
     273Python classes as a programmer in Python sees them) and the second is the 
     274name of the field in ``PyTypeObject`` or subjugated structures. See Python 
     275documentation for description of functions' arguments and results. Not all 
     276methods can be directly defined; for those that can't, it is because we either 
     277use an alternative method (eg. ``setattro`` instead of ``setattr``) or pyxtract 
     278gets or computes the data for this field in some other way. 
    380279 
    381280General methods 
     
    390289|              |                       | has been taken care of. If you have a brand new object,   | 
    391290|              |                       | copy the code of one of Orange's deallocators.            | 
    392 +--------------+-----------------------+-----------------------------------------------------------+ 
    393 | ``.``        | ``tp_print``          | This function can't be redefined as it seem to crash      | 
    394 |              |                       | Python (due to difference in compilers?!)                 | 
    395291+--------------+-----------------------+-----------------------------------------------------------+ 
    396292| ``.``        | ``tp_getattr``        | Can't be redefined since we use ``tp_getattro`` instead.  | 
     
    534430+-------------+----------------------+ 
    535431 
    536 For example, here's what gets called when you want to know the length of an example table. :: 
     432For example, here is what gets called when you want to know the length of an 
     433example table. :: 
    537434 
    538435    int ExampleTable_len_sq(PyObject *self) 
     
    543440    } 
    544441 
    545 ``PyTRY`` and ``PyCATCH`` take care of C++ exceptions. ``SELF_AS`` is a macro for casting, ie unwrapping the points (this is an alternative to ``CAST_TO`` you've seen earlier). 
     442``PyTRY`` and ``PyCATCH`` take care of C++ exceptions. ``SELF_AS`` is a macro 
     443for casting, ie unwrapping the points (this is an alternative to ``CAST_TO``). 
    546444 
    547445 
     
    549447==================================== 
    550448 
    551 Exporting of most of C++ class fields is already taken care by the lists that are compiled by pyprops. There are only a few cases in the entire Orange where we needed to manually write specific handlers for setting and getting the attributes. This needs to be done if setting needs some special processing or when simulating an attribute that does not exist in the underlying C++ class. 
    552  
    553 An example for this is class ``HierarchicalCluster``. It contains results of a general, not necessarily binary clustering, so each node in the tree has a list ``branches`` with all the node's children. Yet, as the usual clustering is binary, it would be nice if the node would also support attributes ``left`` and ``right``. They are not present in C++, but we can write a function that check the number of branches; if there are none, it returns ``None``, if there are more than two, it complains, while otherwise it returns the first branch. :: 
     449Exporting of most of C++ class fields is already taken care by the lists that 
     450are compiled by pyprops. There are only a few cases in the entire Orange where 
     451we needed to manually write specific handlers for setting and getting the 
     452attributes. This needs to be done if setting needs some special processing or 
     453when simulating an attribute that does not exist in the underlying C++ class. 
     454 
     455An example for this is class ``HierarchicalCluster``. It contains results of a 
     456general, not necessarily binary clustering, so each node in the tree has a list 
     457``branches`` with all the node's children. Yet, as the usual clustering is 
     458binary, it would be nice if the node would also support attributes ``left`` and 
     459``right``. They are not present in C++, but we can write a function that check 
     460the number of branches; if there are none, it returns ``None``, if there are 
     461more than two, it complains, while otherwise it returns the first branch. :: 
    554462 
    555463    PyObject *HierarchicalCluster_get_left(PyObject *self) 
     
    570478    } 
    571479 
    572 As you can see from the example, the function needs to accept a ``PyObject *`` (the object it``self``) and return a ``PyObject *`` (the attribute value). The function name needs to be ``&lt;classname&gt;_get_&lt;attributename&gt;``. Setting an attribute is similar; function name should be ``&lt;classname&gt;_set_&lt;attributename&gt;``, it should accept two Python objects (the object and the attribute value) and return an ``int``, where 0 signifies success and -1 a failure. 
    573  
    574 If you define only one of the two handlers, you'll get a read-only or write-only attribute. 
     480As you can see from the example, the function needs to accept a ``PyObject *`` 
     481(the object it``self``) and return a ``PyObject *`` (the attribute value). The 
     482function name needs to be ``<classname>_get_<attributename>``. 
     483Setting an attribute is similar; function name should be 
     484``<classname>_set_<attributename>``, it should accept two Python 
     485objects (the object and the attribute value) and return an ``int``, where 0 
     486signifies success and -1 a failure. 
     487 
     488If you define only one of the two handlers, you'll get a read-only or write-only 
     489attribute. 
    575490 
    576491 
     
    578493================ 
    579494 
    580 You've already seen an example of a member function - the ``ExampleTable``'s method ``sort``. The general template is ``PyObject *&lt;classname&gt;_&lt;methodname&gt;(&lt;arguments&gt;) PYARGS(&lt;arguments-keyword&gt;, &lt;documentation-string&gt;)``. In the case of the ``ExampleTable``'s ``sort``, this looks like this. :: 
     495We have already shown an example of a member function - the ``ExampleTable``'s 
     496method ``sort``. The general template is 
     497``PyObject *<classname>_<methodname>(<arguments>) PYARGS(<arguments-keyword>, <documentation-string>)``. 
     498In the case of the ``ExampleTable``'s ``sort``, this looks like this. :: 
    581499 
    582500    PyObject *ExampleTable_sort(PyObject *self, PyObject *args) PYARGS(METH_VARARGS, "() -> None") 
    583501 
    584 Argument type can be any of the usual Python constants stating the number and the kind of arguments, such as ``METH_VARARGS`` or ``METH_O`` - this constant gets copied to the corresponding list (browse Python documentation for ``PyMethodDef``). 
    585  
    586 If you want more examples, just search Orange's files for the keyword ``PYARGS``. 
     502Argument type can be any of the usual Python constants stating the number and 
     503the kind of arguments, such as ``METH_VARARGS`` or ``METH_O`` - this constant 
     504gets copied to the corresponding list (browse Python documentation for 
     505``PyMethodDef``). 
     506 
    587507 
    588508Class constants 
    589509=============== 
    590510 
    591 Orange classes, as seen from Python, can also have constants, such as ``orange.Classifier.GetBoth``. Classifier's ``GetBoth`` is visible as a member of the class, the derived classes and all their instances (eg. ``BayesClassifier.GetBoth`` and ``bayes.GetBoth``). 
    592  
    593 There are several ways to define such constants. If they are simple integers or floats, you can use ``PYCLASSCONSTANT_INT`` or ``PYCLASSCONSTANT_FLOAT``, like in :: 
     511Orange classes, as seen from Python, can also have constants, such as 
     512``orange.Classifier.GetBoth``. Classifier's ``GetBoth`` is visible as a member 
     513of the class, the derived classes and all their instances (eg. 
     514``BayesClassifier.GetBoth`` and ``bayes.GetBoth``). 
     515 
     516There are several ways to define such constants. If they are simple integers or 
     517floats, you can use ``PYCLASSCONSTANT_INT`` or ``PYCLASSCONSTANT_FLOAT``, like 
     518in :: 
    594519 
    595520    PYCLASSCONSTANT_INT(Classifier, GetBoth, 2) 
     
    599524    PYCLASSCONSTANT_INT(C45TreeNode, Leaf, TC45TreeNode::Leaf) 
    600525 
    601 Pyxtract will convert the given constant to a Python object (using ``PyInt_FromLong`` or ``PyFloat_FromDouble>``). 
    602  
    603 When the constant is an object of some other type, use ``PYCLASSCONSTANT``. In this form (not used in Orange so far), the third argument can be either an instance of ``PyObject *`` or a function call. In either case, the object or function must be known at the point where the pyxtract generated file is included. 
     526Pyxtract will convert the given constant to a Python object (using 
     527``PyInt_FromLong`` or ``PyFloat_FromDouble>``). 
     528 
     529When the constant is an object of some other type, use ``PYCLASSCONSTANT``. In 
     530this form (not used in Orange so far), the third argument can be either an 
     531instance of ``PyObject *`` or a function call. In either case, the object or 
     532function must be known at the point where the pyxtract generated file is 
     533included. 
    604534 
    605535 
     
    607537======== 
    608538 
    609 Pickling is taken care of automatically if the class provides a Python constructor which can construct the object without arguments (it may *accept* arguments, but should be able to do without them. If there is no such constructor, the class should provide a ``__reduce__`` method or it should explicitly declare that it cannot be pickled. If it doesn't pyxtract will issue a warning that the class will not be picklable. 
     539Pickling is taken care of automatically if the class provides a Python 
     540constructor that can construct the object without arguments (it may *accept* 
     541arguments, but should be able to do without them. If there is no such 
     542constructor, the class should provide a ``__reduce__`` method or it should 
     543explicitly declare that it cannot be pickled. If it doesn't pyxtract will issue 
     544a warning that the class will not be picklable. 
    610545 
    611546Here are the rules: 
    612547 
    613548* Classes that provide a ``__reduce__`` method (details follow below) are pickled through that method. 
     549 
    614550* Class ``Orange``, the base class, already provides a ``__reduce__`` method, which is only useful if the constructor accepts empty arguments. So, if the constructor is declared as ``C_NAMED``, ``C_UNNAMED``, ``C_CALL`` or ``C_CALL3``, the class is the class will be picklable. See the warning below. 
     551 
    615552* If the constructor is defined by ``_new`` method, and the ``BASED_ON`` definition is followed be ``ALLOWS_EMPTY``, this signifies that it accepts empty arguments, so it will be picklable just as in the above point. For example, the constructor for the class ``DefaultClassifier`` is defined like this :: 
    616553 
    617554    PyObject *DefaultClassifier_new(PyTypeObject *tpe, PyObject *args) 
    618555        BASED_ON(Classifier, "([defaultVal])") ALLOWS_EMPTY 
    619   and is picklable through code ``Orange.__reduce__``. But again, see the warning below. 
     556 
     557and is picklable through code ``Orange.__reduce__``. But again, see the warning 
     558below. 
    620559 
    621560* If the constructor is defined as ``ABSTRACT``, there cannot be any instances of this class, so pyxtract will give no warning that it is not picklable. 
     
    623562 
    624563    NO_PICKLE(TabDelimExampleGenerator) 
    625   Such classes won't be picklable even if they define the appropriate constructors. This effectively defined a ``__reduce__`` method which yields an exception; if you manually provide a ``__reduce__`` method for such a class, pyxtract will detect that the method is multiply defined. 
    626 * If there are no suitable constructors, no ``__reduce__`` method and no ``ABSTRACT`` or ``NO_PICKLE`` flag, pyxtract will warn you about that. 
    627  
    628 When the constructor is used, as in points 2 and 3, pickling will only work if all fields of the C++ class can be set "manually" from Python, are set through the constructor, or are set when assigning other fields (search the source code for the ``afterSet`` method). In other words, if there are fields that are not marked as ``//P`` for pyprops, you will most probably need to manually define a ``__reduce__`` method, as in point 1. 
    629  
    630 The details of what the ``__reduce__`` method must do are described in the Python documentation. In our circumstances it can be implemented in two ways which differ in what function is used for unpickling: it can either use the class' constructor or we can define a special method for unpickling. 
    631  
    632 The former usually happens when the class has a read-only property (``//PR``) which is set by the constructor. For instance, ``AssociationRule`` has read-only fields ``left`` and ``right``, which are needs to be given to the constructor. This is the ``__reduce__`` method for the class. :: 
     564 
     565  Such classes won't be picklable even if they define the appropriate 
     566  constructors. This effectively defined a ``__reduce__`` method which yields an 
     567  exception; if you manually provide a ``__reduce__`` method for such a class, 
     568  pyxtract will detect that the method is multiply defined. 
     569 
     570* If there are no suitable constructors, no ``__reduce__`` method and no 
     571  ``ABSTRACT`` or ``NO_PICKLE`` flag, pyxtract gives a warning about that. 
     572 
     573When the constructor is used, as in points 2 and 3, pickling will only work if 
     574all fields of the C++ class can be set "manually" from Python, are set through 
     575the constructor, or are set when assigning other fields. In other words, if 
     576there are fields that are not 
     577marked as ``//P`` for pyprops, you will most probably need to manually define 
     578a ``__reduce__`` method, as in point 1. 
     579 
     580The details of what the ``__reduce__`` method must do are described in the 
     581Python documentation. In our circumstances, it can be implemented in two ways 
     582which differ in what function is used for unpickling: it can either use the 
     583class' constructor or we can define a special method for unpickling. 
     584 
     585The former usually happens when the class has a read-only property (``//PR``), 
     586which is set by the constructor. For instance, ``AssociationRule`` has read-only 
     587fields ``left`` and ``right``, which are needs to be given to the constructor. 
     588This is the ``__reduce__`` method for the class. :: 
    633589 
    634590    PyObject *AssociationRule__reduce__(PyObject *self) 
     
    643599    } 
    644600 
    645 As you can learn from the Python documentation, the ``__reduce__`` should return the tuple in which the first element is the function that will do the unpickling, and the second argument are the arguments for that function. Our unpickling function is simply the classes' type (calling a type corresponds to calling a constructor) and the arguments for the constructor are the left- and right-hand side of the rule. The third element of the tuple is classes' dictionary. 
    646  
    647 When unpickling is more complicated, usually when the class has no constructor and contains fields of type ``float *`` or similar, we need a special unpickling function. The function needs to be directly in the modules' namespace (it cannot be a static method of a class), so we named them ``__pickleLoader&lt;classname&gt;``. Search for examples of such functions in the source code; note that the instance's true class need to be pickled, too. Also, check how we use ``TCharBuffer`` throughout the code to store and pickle binary data as Python strings. 
    648  
    649 Be careful when manually writing the unpickler: if a C++ class derived from that class inherits its ``__reduce__``, the corresponding unpickler will construct an instance of a wrong class (unless the unpickler functions through Python's constructor, ``ob_type->tp_new``). Hence, classes derived from a class which defines an unpickler have to define their own ``__reduce__``, too. 
     601As described in the Python documentation, the ``__reduce__`` should return a 
     602tuple in which the first element is the function that will do the unpickling, 
     603and the second argument are the arguments for that function. Our unpickling 
     604function is simply the classes' type (calling a type corresponds to calling a 
     605constructor) and the arguments for the constructor are the left- and right-hand 
     606side of the rule. The third element of the tuple is classes' dictionary. 
     607 
     608When unpickling is more complicated - usually when the class has no constructor 
     609and contains fields of type ``float *`` or similar - we need a special 
     610unpickling function. The function needs to be directly in the modules' namespace 
     611(it cannot be a static method of a class), so we named them 
     612``__pickleLoader<classname>``. Search for examples of such functions in 
     613the source code; note that the instance's true class need to be pickled, too. 
     614Also, check how we use ``TCharBuffer`` throughout the code to store and pickle 
     615binary data as Python strings. 
     616 
     617Be careful when manually writing the unpickler: if a C++ class derived from that 
     618class inherits its ``__reduce__``, the corresponding unpickler will construct an 
     619instance of a wrong class (unless the unpickler functions through Python's 
     620constructor, ``ob_type->tp_new``). Hence, classes derived from a class which 
     621defines an unpickler have to define their own ``__reduce__``, too. 
    650622 
    651623Non-member functions and constants 
    652624================================== 
    653625 
    654 Most Orange's functions are members of classes. About the only often used exception to this is ``orange.newmetaid`` which returns a new ID for a meta attribute. These functions are defined in the same way as member function except that the function name doesn't have the class name (and the underscore - that's how pyxtract distinguishes between the two). Here's the ``newmetaid`` function. :: 
     626Non-member functions are defined in the same way as member functions except 
     627that their names do not start with the class name. Here is how the ``newmetaid`` 
     628is implemented :: 
    655629 
    656630    PyObject *newmetaid(PyObject *, PyObject *) PYARGS(0,"() -> int") 
     
    661635    } 
    662636 
    663 Orange also defines some non-member constants. These are defined in a similar fashion as the class constants. ``PYCONSTANT_INT(&lt;constant-name&gt;, &lt;integer&gt;)`` defines an integer constant and ``PYCONSTANT_FLOAT`` would be used for a continuous one. ``PYCONSTANT`` is used for objects of other types, as the below example that defines an (obsolete) constant ``MeasureAttribute_splitGain`` shows. :: 
     637Orange also defines some non-member constants. These are defined in a similar 
     638fashion as the class constants. 
     639``PYCONSTANT_INT(<constant-name>, <integer>)`` defines an integer 
     640constant and ``PYCONSTANT_FLOAT`` would be used for a continuous one. 
     641``PYCONSTANT`` is used for objects of other types, as the below example that 
     642defines an (obsolete) constant ``MeasureAttribute_splitGain`` shows. :: 
    664643 
    665644    PYCONSTANT(MeasureAttribute_splitGain, (PyObject *)&PyOrMeasureAttribute_gainRatio_Type) 
    666645 
    667 Class constants from the previous chapter are put in a pyxtract generated file that is included at the end of the file in which the constant definitions and the corresponding classes are. Global constant modules are included in an other file, far away from their actual definitions. For this reason, ``PYCONSTANT`` cannot reference any functions (the above example is an exception - all class types are declared in this same file and are thus available at the moment the above code is used). Therefore, if the constant is defined by a function call, you need to use another keyword, ``PYCONSTANTFUNC``:: 
     646Class constants from the previous section are put in a pyxtract generated file 
     647that is included at the end of the file in which the constant definitions and 
     648the corresponding classes are. Global constant modules are included in another 
     649file, far away from their actual definitions. For this reason, ``PYCONSTANT`` 
     650cannot refer to any functions (the above example is an exception - all class 
     651types are declared in this same file and are thus available at the moment the 
     652above code is used). Therefore, if the constant is defined by a function call, 
     653you need to use another keyword, ``PYCONSTANTFUNC``:: 
    668654 
    669655    PYCONSTANTFUNC(globalRandom, stdRandomGenerator) 
    670656 
    671 Pyxtract will generate a code which will, prior to calling ``stdRandomGenerator``, declare it as a function with no arguments that returns ``PyObject *``. Of course, you will have to define the function somewhere in your code, like this:: 
     657Pyxtract will generate a code which will, prior to calling 
     658``stdRandomGenerator``, declare it as a function with no arguments that returns 
     659``PyObject *``. Of course, you will have to define the function somewhere in 
     660your code, like this:: 
    672661 
    673662    PyObject *stdRandomGenerator() 
     
    676665    } 
    677666 
    678 Another example are ``VarTypes``. You've probably already used ``orange.VarTypes.Discrete`` and ``orange.VarTypes.Continuous`` to check an attribute's type. ``VarTypes`` is a tiny module inside Orange that contains nothing but five constants, representing various attribute types. From pyxtract perspective, ``VarTypes`` is a constant. Here's the complete definition. :: 
     667Another example are ``VarTypes``. ``VarTypes`` is a tiny module inside Orange 
     668that contains nothing but five constants, representing various attribute types. 
     669From pyxtract perspective, ``VarTypes`` is a constant. This is the complete 
     670definition. :: 
    679671 
    680672    PyObject *VarTypes() 
     
    691683    PYCONSTANTFUNC(VarTypes, VarTypes) 
    692684 
    693 If you want to understand the constants completely, check the Orange's pyxtract generated file initialization.px. 
     685If you want to understand the constants completely, check the Orange's pyxtract 
     686generated file initialization.px. 
    694687 
    695688How does it all fit together 
    696689============================ 
    697690 
    698 This part of the text's main purpose is to remind the pyxtract author of the structure of the files pyxtract creates. (I'm annoyed when I don't know how my programs work. And I happen to be annoyed quite frequently. :-) If you think you can profit from reading it, you are welcome. 
     691We will finish the section with a description of the files generated by the two 
     692scripts. Understanding these may be needed for debugging purposes. 
    699693 
    700694File specific px files 
    701695---------------------- 
    702696 
    703 For each compiled cpp file, pyxtract creates a px file with the same name. The file starts with externs declaring the base classes for the classes whose types are defined later on. 
    704  
    705 Then follow class type definitions. 
    706  
    707 * Method definitions (``PyMethodDef``). Nothing exotic here, just a table with the member functions that is pointed to by ``tp_methods`` of the ``PyTypeObject``. 
    708  
    709 * GetSet definitions (``PyGetSetDef``). Similar to methods, a list to be pointed to by ``tp_getset``, which includes the attributes for which special handlers were written. 
     697For each compiled cpp file, pyxtract creates a px file with the same name. The 
     698file starts with externs declaring the base classes for the classes whose types 
     699are defined later on. Then follow class type definitions: 
     700 
     701* Method definitions (``PyMethodDef``). Nothing exotic here, just a table with 
     702  the member functions that is pointed to by ``tp_methods`` of the 
     703  ``PyTypeObject``. 
     704 
     705* GetSet definitions (``PyGetSetDef``). Similar to methods, a list to be pointed 
     706  to by ``tp_getset``, which includes the attributes for which special handlers 
     707  were written. 
    710708 
    711709* Definitions of doc strings for call operator and constructor. 
    712710 
    713 * Constants. If the class has any constants, there will be a function named ``void &lt;class-name&gt;_addConstants()``. The function will create a class dictionary in the type's ``tp_dict``, if there is none yet. Then it will store the constants in it. The functions is called at the module initialization, file initialization.px. 
    714  
    715 * Constructors. If the class uses generic constructors (ie, if it's defined by ``C_UNNAMED``, ``C_NAMED``, ``C_CALL`` or ``C_CALL3``), they will need to call a default object constructor, like the below one for ``FloatVariable``. (This supposes the object is derived from ``TOrange``! We will need to get rid of this we want pyxtract to be more general. Maybe an additional argument in ``DATASTRUCTURE``?) :: 
     711* Constants. If the class has any constants, there will be a function named 
     712  ``void <class-name>_addConstants()``. The function will create a class 
     713  dictionary in the type's ``tp_dict``, if there is none yet. Then it will store 
     714  the constants in it. The functions is called at the module initialization, 
     715  file initialization.px. 
     716 
     717* Constructors. If the class uses generic constructors (ie, if it's defined by 
     718  ``C_UNNAMED``, ``C_NAMED``, ``C_CALL`` or ``C_CALL3``), they will need to call 
     719  a default object constructor, like the below one for ``FloatVariable``. 
     720  (This supposes the object is derived from ``TOrange``! We will need to get rid 
     721  of this we want pyxtract to be more general. Maybe an additional argument in 
     722  ``DATASTRUCTURE``?) :: 
    716723 
    717724    POrange FloatVariable_default_constructor(PyTypeObject *type) 
     
    719726        return POrange(mlnew TFloatVariable(), type); 
    720727    } 
    721   If the class is abstract, pyxtract defines a constructor that will call ``PyOrType_GenericAbstract``. ``PyOrType_GenericAbstract`` checks the type that the caller wishes to construct; if it is a type derived from this type, it permits it, otherwise it complains that the class is abstract. 
     728 
     729  If the class is abstract, pyxtract defines a constructor that will call 
     730  ``PyOrType_GenericAbstract``. ``PyOrType_GenericAbstract`` checks the type 
     731  that the caller wishes to construct; if it is a type derived from this type, 
     732  it permits it, otherwise it complains that the class is abstract. 
    722733 
    723734* Aliases. A list of renamed attributes. 
    724735 
    725 * ``PyTypeObject`` and the numeric, sequence and mapping protocols. ``PyTypeObject`` is named ``PyOr&lt;classname&gt;_Type_inh``. 
    726  
    727 * Definition of conversion functions. This is done by macro ``DEFINE_cc(&lt;classname&gt;)`` which defines ``int ccn_&lt;classname&gt;(PyObject *obj, void *ptr)`` - functions that can be used in ``PyArg_ParseTuple`` for converting an argument (given as ``PyObject *`` to an instance of ``&lt;classname&gt;``. Nothing needs to be programmed for the conversion, it is just a cast: ``*(GCPtr< T##type > *)(ptr) = PyOrange_As##type(obj);``). The difference between ``cc`` and ``ccn`` is that the latter accepts null pointers. 
    728  
    729 * ``TOrangeType``. Although ``PyTypeObject`` is a regular Python object, it unfortunately isn't possible to derive new objects from it. Obviously the developers of Python didn't think anyone would need it, and this part of Python's code is messy enough even without it. Orange nevertheless uses a type ``TOrangeType`` that begins with ``PyTypeObject`` (essentially inheriting it). The new definition also includes the RTTI used for wrapping (this way Orange nows which C++ class corresponds to which Python class), a pointer to the default constructor (used by generic constructors), a pointer to list of constructor keywords (``CONSTRUCTOR_KEYWORDS``, keyword arguments that should be ignored in a later call to ``init``) and recognized attributes (``RECOGNIZED_ATTRIBUTES``, attributes that don't yield warnings when set), a list of aliases, and pointers to ``cc_`` and ``ccn_`` functions. The latter are not used by Orange, since it can call the converters directly. They are here because ``TOrangeType`` is exported in a DLL while ``cc_`` and ``ccn_`` are not (for the sake of limiting the number of exported symbols). 
     736* ``PyTypeObject`` and the numeric, sequence and mapping protocols. 
     737  ``PyTypeObject`` is named ``PyOr<classname>_Type_inh``. 
     738 
     739* Definition of conversion functions. This is done by macro 
     740  ``DEFINE_cc(<classname>)`` which defines 
     741  ``int ccn_<classname>(PyObject *obj, void *ptr)`` - functions that can 
     742  be used in ``PyArg_ParseTuple`` for converting an argument (given as 
     743  ``PyObject *`` to an instance of ``<classname>``. Nothing needs to be 
     744  programmed for the conversion, it is just a 
     745  cast: ``*(GCPtr< T##type > *)(ptr) = PyOrange_As##type(obj);``). The 
     746  difference between ``cc`` and ``ccn`` is that the latter accepts null 
     747  pointers. 
     748 
     749* ``TOrangeType`` that (essentially) inherits ``PyTypeObject``. The new 
     750  definition also includes the RTTI used for wrapping (this way Orange knows 
     751  which C++ class corresponds to which Python class), a pointer to the default 
     752  constructor (used by generic constructors), a pointer to list of constructor 
     753  keywords (``CONSTRUCTOR_KEYWORDS``, keyword arguments that should be ignored 
     754  in a later call to ``init``) and recognized attributes 
     755  (``RECOGNIZED_ATTRIBUTES``, attributes that don't yield warnings when set), a 
     756  list of aliases, and pointers to ``cc_`` and ``ccn_`` functions. The latter 
     757  are not used by Orange, since it can call the converters directly. They are 
     758  here because ``TOrangeType`` is exported in a DLL while ``cc_`` and ``ccn_`` 
     759  are not (for the sake of limiting the number of exported symbols). 
    730760 
    731761 
     
    735765Initialization.px defines the global module stuff. 
    736766 
    737 First, here is a list of all ``TOrangeTypes``. The list is used for checking whether some Python object is of Orange's type or derived from one, for finding a Python class corresponding to a C++ class (based on C++'s RTTI). Orange also exports the list as ``orange._orangeClasses``; this is a ``PyCObject`` so it can only be used by other Python extensions written in C. 
    738  
    739 Then come declarations of all non-member functions, followed by a ``PyMethodDef`` structure with them. 
    740  
    741 Finally, here are declarations of functions that return manually constructed constants (eg ``VarTypes``) and declarations of functions that add class constants (eg ``Classifier_addConstants``). The latter functions were generated by pyxtract and reside in the individual px files. Then follows a function that calls all the constant related functions declared above. This function also adds all class types to the Orange module. Why not in a loop over ``orangeClasses``? 
     767First, here is a list of all ``TOrangeTypes``. The list is used for checking 
     768whether some Python object is of Orange's type or derived from one, for finding 
     769a Python class corresponding to a C++ class (based on C++'s RTTI). Orange also 
     770exports the list as ``orange._orangeClasses``; this is a ``PyCObject`` so it can 
     771only be used by other Python extensions written in C. 
     772 
     773Then come declarations of all non-member functions, followed by a 
     774``PyMethodDef`` structure with them. 
     775 
     776Finally, here are declarations of functions that return manually constructed 
     777constants (eg ``VarTypes``) and declarations of functions that add class 
     778constants (eg ``Classifier_addConstants``). The latter functions were generated 
     779by pyxtract and reside in the individual px files. Then follows a function that 
     780calls all the constant related functions declared above. This function also adds 
     781all class types to the Orange module. 
    742782 
    743783The main module now only needs to call ``addConstants``. 
     
    754794    #define PyOrange_AsDomain(op) (GCPtr< TDomain >(PyOrange_AS_Orange(op))) 
    755795 
    756 ***************** 
    757 Where to include? 
    758 ***************** 
    759  
    760 As already mentioned, ppp files should be included (at the beginning) of the corresponding cpp files, instead of the hpp file. For instance, domain.ppp is included in domain.cpp. Each ppp should be compiled only once, all other files needing the definition of ``TDomain`` should still include domain.hpp as usual. 
    761  
    762 File-specific px files are included in the corresponding cpp files. lib_kernel.px is included at the end of lib_kernel.cpp, from which it was generated. initialization.px should preferably be included in the file that initializes the module (function ``initorange`` needs to call ``addConstants``, which is declared in initialization.px. These px files contain definitions and must be compiled only once. externs.px contains declarations and can be included wherever needed. 
    763  
    764 Some steps in these instructions are only for Visual Studio 6.0. If you use a newer version of Visual Studio or if you use Linux, adapt them. 
    765  
    766 Create a new, blank workspace. If your orange sources are in d:\ai\orange\source (as are mine :), specify this directory as a "Location". Add a new project of type "Win 32 Dynamic-Link Library"; change the location back to d:\ai\orange\source. Make it an empty DLL project. 
    767  
    768 Whatever names you give your module, make sure that the .cpp and .hpp files you create as you go on are in orange\source\something (replace "something" with something), since the further instructions will suppose it. 
     796************************** 
     797What and where to include? 
     798************************** 
     799 
     800As already mentioned, ppp files should be included (at the beginning) of the 
     801corresponding cpp files, instead of the hpp file. For instance, domain.ppp is 
     802included in domain.cpp. Each ppp should be compiled only once, all other files 
     803needing the definition of ``TDomain`` should still include domain.hpp as usual. 
     804 
     805File-specific px files are included in the corresponding cpp files. 
     806lib_kernel.px is included at the end of lib_kernel.cpp, from which it was 
     807generated. initialization.px should preferably be included in the file that 
     808initializes the module (function ``initorange`` needs to call ``addConstants``, 
     809which is declared in initialization.px. These px files contain definitions and 
     810must be compiled only once. externs.px contains declarations and can be included 
     811wherever needed. 
     812 
     813For Microsoft Visual Studio, create a new, blank workspace. Specify the 
     814directory with orange sources as "Location". Add a new project of type "Win 32 
     815Dynamic-Link Library"; change the 
     816location back to d:\ai\orange\source. Make it an empty DLL project. 
     817 
     818Whatever names you give your module, make sure that the .cpp and .hpp files you 
     819create as you go on are in orange\source\something (replace "something" with 
     820something), since the further instructions will suppose it. 
  • docs/extend-widgets/rst/OWAttributeSampler.py

    r11085 r11593  
    55<priority>1020</priority> 
    66""" 
     7import Orange 
    78 
    89from OWWidget import * 
     
    1112class OWAttributeSampler(OWWidget): 
    1213    settingsList = [] 
    13     contextHandlers = {"": DomainContextHandler("", [ 
    14             ContextField("classAttribute", DomainContextHandler.Required), 
    15             ContextField("attributeList", DomainContextHandler.List + DomainContextHandler.SelectedRequired, 
    16                          selected="selectedAttributes")])} 
     14 
     15    # ~start context handler~ 
     16    contextHandlers = { 
     17        "": DomainContextHandler( 
     18            "", 
     19            [ContextField("classAttribute", DomainContextHandler.Required), 
     20             ContextField("attributeList", 
     21                          DomainContextHandler.List + 
     22                          DomainContextHandler.SelectedRequired, 
     23                          selected="selectedAttributes")])} 
     24    # ~end context handler~ 
    1725 
    1826    def __init__(self, parent=None, signalManager=None): 
    1927        OWWidget.__init__(self, parent, signalManager, 'AttributeSampler') 
    2028 
    21         self.inputs = [("Examples", ExampleTable, self.dataset)] 
    22         self.outputs = [("Examples", ExampleTable)] 
     29        self.inputs = [("Examples", Orange.data.Table, self.dataset)] 
     30        self.outputs = [("Examples", Orange.data.Table)] 
    2331 
    2432        self.icons = self.createAttributeIconDict() 
     
    2937        self.loadSettings() 
    3038 
    31         OWGUI.listBox(self.controlArea, self, "selectedAttributes", "attributeList", box="Selected attributes", selectionMode = QListWidget.ExtendedSelection) 
     39        OWGUI.listBox(self.controlArea, self, "selectedAttributes", 
     40                      "attributeList", 
     41                      box="Selected attributes", 
     42                      selectionMode=QListWidget.ExtendedSelection) 
     43 
    3244        OWGUI.separator(self.controlArea) 
    33         self.classAttrCombo = OWGUI.comboBox(self.controlArea, self, "classAttribute", box="Class attribute") 
     45        self.classAttrCombo = OWGUI.comboBox( 
     46            self.controlArea, self, "classAttribute", 
     47            box="Class attribute") 
     48 
    3449        OWGUI.separator(self.controlArea) 
    35         OWGUI.button(self.controlArea, self, "Commit", callback = self.outputData) 
     50        OWGUI.button(self.controlArea, self, "Commit", 
     51                     callback=self.outputData) 
    3652 
    3753        self.resize(150,400) 
     
    4359        self.classAttrCombo.clear() 
    4460        if data: 
    45             self.attributeList = [(attr.name, attr.varType) for attr in data.domain] 
     61            self.attributeList = [(attr.name, attr.varType) 
     62                                  for attr in data.domain] 
    4663            self.selectedAttributes = [] 
    4764            for attrName, attrType in self.attributeList: 
     
    6380            self.send("Examples", None) 
    6481        else: 
    65             newDomain = orange.Domain([self.data.domain[i] for i in self.selectedAttributes], self.data.domain[self.classAttribute]) 
    66             newData = orange.ExampleTable(newDomain, self.data) 
     82            newDomain = Orange.data.Domain( 
     83                [self.data.domain[i] for i in self.selectedAttributes], 
     84                self.data.domain[self.classAttribute]) 
     85 
     86            newData = Orange.data.Table(newDomain, self.data) 
    6787            self.send("Examples", newData) 
    6888 
    69  
    70 ############################################################################## 
    71 # Test the widget, run from prompt 
    7289 
    7390if __name__=="__main__": 
     
    7693    ow.show() 
    7794 
    78     data = orange.ExampleTable('iris.tab') 
     95    data = Orange.data.Table('iris.tab') 
    7996    ow.dataset(data) 
    8097 
  • docs/extend-widgets/rst/OWDataSamplerB.py

    r11085 r11593  
    22<name>Data Sampler (B)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerB.png</icon> 
     4<icon>icons/DataSamplerB.svg</icon> 
    55<priority>20</priority> 
    66""" 
     7import Orange 
    78from OWWidget import * 
    89import OWGUI 
     
    1112    settingsList = ['proportion', 'commitOnChange'] 
    1213    def __init__(self, parent=None, signalManager=None): 
    13         OWWidget.__init__(self, parent, signalManager, 'SampleDataB') 
     14        OWWidget.__init__(self, parent, signalManager) 
    1415 
    15         self.inputs = [("Data", ExampleTable, self.data)] 
    16         self.outputs = [("Sampled Data", ExampleTable)] 
     16        self.inputs = [("Data", Orange.data.Table, self.data)] 
     17        self.outputs = [("Sampled Data", Orange.data.Table)] 
    1718 
    1819        self.proportion = 50 
     
    4950 
    5051    def selection(self): 
    51         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     52        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    5253        ind = indices(self.dataset) 
    5354        self.sample = self.dataset.select(ind, 0) 
     
    6162            self.commit() 
    6263 
    63 ############################################################################## 
    64 # Test the widget, run from prompt 
    6564 
    6665if __name__=="__main__": 
     
    6867    ow = OWDataSamplerB() 
    6968    ow.show() 
    70     dataset = orange.ExampleTable('iris.tab') 
     69    dataset = Orange.data.Table('iris.tab') 
    7170    ow.data(dataset) 
    7271    appl.exec_() 
  • docs/extend-widgets/rst/OWDataSamplerC.py

    r11085 r11593  
    22<name>Data Sampler (C)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerC.png</icon> 
     4<icon>icons/DataSamplerC.svg</icon> 
    55<priority>30</priority> 
    66""" 
     7import Orange 
     8 
    79from OWWidget import * 
    810import OWGUI 
     
    1315        OWWidget.__init__(self, parent, signalManager, 'SampleDataC') 
    1416         
    15         self.inputs = [("Data", ExampleTable, self.data)] 
    16         self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
     17        self.inputs = [("Data", Orange.data.Table, self.data)] 
     18        self.outputs = [("Sampled Data", Orange.data.Table), 
     19                        ("Other Data", Orange.data.Table)] 
    1720 
    1821        self.proportion = 50 
     
    4952 
    5053    def selection(self): 
    51         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     54        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    5255        ind = indices(self.dataset) 
    5356        self.sample = self.dataset.select(ind, 0) 
     
    6366            self.commit() 
    6467 
    65 ############################################################################## 
    66 # Test the widget, run from prompt 
    6768 
    6869if __name__=="__main__": 
     
    7071    ow = OWDataSamplerC() 
    7172    ow.show() 
    72     dataset = orange.ExampleTable('iris.tab') 
     73    dataset = Orange.data.Table('iris.tab') 
    7374    ow.data(dataset) 
    7475    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurveA.py

    r11085 r11593  
    22<name>Learning Curve (A)</name> 
    33<description>Takes a data set and a set of learners and shows a learning curve in a table</description> 
    4 <icon>icons/LearningCurveA.png</icon> 
     4<icon>icons/LearningCurve.svg</icon> 
    55<priority>1000</priority> 
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    9 import OWGUI, orngTest, orngStat 
     11import OWGUI 
    1012 
    1113class OWLearningCurveA(OWWidget): 
    1214    settingsList = ["folds", "steps", "scoringF", "commitOnChange"] 
    13      
     15 
    1416    def __init__(self, parent=None, signalManager=None): 
    1517        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1618 
    17         self.inputs = [("Data", ExampleTable, self.dataset), 
    18                        ("Learner", orange.Learner, self.learner, Multiple)] 
    19          
     19        self.inputs = [("Data", Orange.data.Table, self.dataset), 
     20                       ("Learner", Orange.core.Learner, self.learner, 
     21                        Multiple)] 
     22 
    2023        self.folds = 5     # cross validation folds 
    2124        self.steps = 10    # points in the learning curve 
     
    2326        self.commitOnChange = 1 # compute curve on any change of parameters 
    2427        self.loadSettings() 
    25         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    26         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     28        self.setCurvePoints() # sets self.curvePoints, self.steps equidistant points from 1/self.steps to 1 
     29        self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     30                        ("AUC", Orange.evaluation.scoring.AUC), 
     31                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     32                        ("Information Score", Orange.evaluation.scoring.IS), 
     33                        ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     34                        ("Specificity", Orange.evaluation.scoring.Specificity)] 
    2735        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    2836        self.data = None   # data on which to construct the learning curve 
     
    3644 
    3745        OWGUI.separator(self.controlArea) 
     46 
    3847        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    3948        scoringNames = [x[0] for x in self.scoring] 
    40         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
     49        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
     50                       callback=self.computeScores) 
    4151 
    4252        OWGUI.separator(self.controlArea) 
     53 
    4354        box = OWGUI.widgetBox(self.controlArea, "Options") 
    44         OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
     55        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     56                   label='Cross validation folds:  ', 
    4557                   callback=lambda: self.computeCurve(self.commitOnChange)) 
    46         OWGUI.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points:  ', 
    47                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
    48  
     58        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
     59                   label='Learning curve points:  ', 
     60                   callback=[self.setCurvePoints, 
     61                             lambda: self.computeCurve(self.commitOnChange)]) 
    4962        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    50         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
     63        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     64                                      callback=self.computeCurve, disabled=1) 
     65 
     66        OWGUI.rubber(self.controlArea) 
    5167 
    5268        # table widget 
    53         self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
    54                  
     69        self.table = OWGUI.table(self.mainArea, 
     70                                 selectionMode=QTableWidget.NoSelection) 
     71 
    5572        self.resize(500,200) 
    5673 
    5774    ##############################################################################     
    58     # slots: handle input signals         
     75    # slots: handle input signals 
    5976 
    6077    def dataset(self, data): 
     
    6885            self.curves = [] 
    6986            self.scores = [] 
    70         self.commitBtn.setEnabled(self.data<>None) 
     87        self.commitBtn.setEnabled(self.data is not None) 
    7188 
    7289    def learner(self, learner, id=None): 
     
    107124            self.infob.setText("No learners.") 
    108125        self.commitBtn.setEnabled(len(self.learners)) 
    109 ##        if len(self.scores): 
     126 
    110127        if self.data: 
    111128            self.setTable() 
     
    130147    def getLearningCurve(self, learners):    
    131148        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    132         curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
     149        curve = Orange.evaluation.testing.learning_curve_n( 
     150            learners, self.data, folds=self.folds, 
     151            proportions=self.curvePoints, callback=pb.advance) 
    133152        pb.finish() 
    134153        return curve 
    135154 
    136155    def setCurvePoints(self): 
    137         self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
     156        self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
    138157 
    139158    def setTable(self): 
     
    154173            self.table.setColumnWidth(i, 80) 
    155174 
    156 ############################################################################## 
    157 # Test the widget, run from prompt 
    158175 
    159176if __name__=="__main__": 
     
    162179    ow.show() 
    163180     
    164     l1 = orange.BayesLearner() 
     181    l1 = Orange.classification.bayes.NaiveLearner() 
    165182    l1.name = 'Naive Bayes' 
    166183    ow.learner(l1, 1) 
    167184 
    168     data = orange.ExampleTable('iris.tab') 
     185    data = Orange.data.Table('iris.tab') 
    169186    ow.dataset(data) 
    170187 
    171     l2 = orange.BayesLearner() 
     188    l2 = Orange.classification.bayes.NaiveLearner() 
    172189    l2.name = 'Naive Bayes (m=10)' 
    173     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    174     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
     190    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     191    l2.conditionalEstimatorConstructor = \ 
     192        Orange.statistics.estimate.ConditionalByRows( 
     193            estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
    175194    ow.learner(l2, 2) 
    176195 
    177     import orngTree 
    178     l4 = orngTree.TreeLearner(minSubset=2) 
     196    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    179197    l4.name = "Decision Tree" 
    180198    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveB.py

    r11085 r11593  
    22<name>Learning Curve (B)</name> 
    33<description>Takes a data set and a set of learners and shows a learning curve in a table</description> 
    4 <icon>icons/LearningCurveB.png</icon> 
     4<icon>icons/LearningCurve.svg</icon> 
    55<priority>1010</priority> 
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    9 import OWGUI, orngTest, orngStat 
     11import OWGUI 
     12 
    1013 
    1114class OWLearningCurveB(OWWidget): 
     
    1518        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1619 
    17         self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
    18                        ("Test Data", ExampleTable, self.testset), 
    19                        ("Learner", orange.Learner, self.learner, Multiple)] 
     20        self.inputs = [("Train Data", Orange.data.Table, self.trainset, Default), 
     21                       ("Test Data", Orange.data.Table, self.testset), 
     22                       ("Learner", Orange.classification.Learner, 
     23                        self.learner, Multiple)] 
    2024         
    2125        self.folds = 5     # cross validation folds 
     
    2428        self.commitOnChange = 1 # compute curve on any change of parameters 
    2529        self.loadSettings() 
    26         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    27         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     30        self.setCurvePoints() # sets self.curvePoints, self.steps equidistan points from 1/self.steps to 1 
     31        self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     32                        ("AUC", Orange.evaluation.scoring.AUC), 
     33                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     34                        ("Information Score", Orange.evaluation.scoring.IS), 
     35                        ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     36                        ("Specificity", Orange.evaluation.scoring.Specificity)] 
    2837        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    2938        self.data = None   # data on which to construct the learning curve 
     
    3847 
    3948        OWGUI.separator(self.controlArea) 
     49 
    4050        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    4151        scoringNames = [x[0] for x in self.scoring] 
    42         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
     52        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
     53                       callback=self.computeScores) 
    4354 
    4455        OWGUI.separator(self.controlArea) 
     56 
    4557        box = OWGUI.widgetBox(self.controlArea, "Options") 
    46         OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
     58        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     59                   label='Cross validation folds:  ', 
    4760                   callback=lambda: self.computeCurve(self.commitOnChange)) 
    48         OWGUI.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points:  ', 
    49                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
     61 
     62        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
     63                   label='Learning curve points:  ', 
     64                   callback=[self.setCurvePoints, 
     65                             lambda: self.computeCurve(self.commitOnChange)]) 
    5066 
    5167        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    52         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
     68        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     69                                      callback=self.computeCurve, disabled=1) 
    5370 
    5471        # table widget 
    55         self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
     72        self.table = OWGUI.table(self.mainArea, 
     73                                 selectionMode=QTableWidget.NoSelection) 
    5674                 
    5775        self.resize(500,200) 
    5876 
    59     ##############################################################################     
    60     # slots: handle input signals         
     77    ############################################################################## 
     78    # slots: handle input signals 
    6179 
    6280    def trainset(self, data): 
     
    7088            self.curves = [] 
    7189            self.scores = [] 
    72         self.commitBtn.setEnabled(self.data<>None) 
     90        self.commitBtn.setEnabled(self.data is not None) 
    7391 
    7492    def testset(self, testdata): 
     
    140158        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    141159        if not self.testdata: 
    142             curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
    143         else: 
    144             curve = orngTest.learningCurveWithTestData(learners, 
    145               self.data, self.testdata, times=self.folds, proportions=self.curvePoints, callback=pb.advance)             
     160            curve = Orange.evaluation.testing.learning_curve_n( 
     161                learners, self.data, folds=self.folds, 
     162                proportions=self.curvePoints, 
     163                callback=pb.advance) 
     164        else: 
     165            curve = Orange.evaluation.testing.learning_curve_with_test_data( 
     166                learners, self.data, self.testdata, times=self.folds, 
     167                proportions=self.curvePoints, 
     168#                callback=pb.advance 
     169                ) 
    146170        pb.finish() 
    147171        return curve 
    148172 
    149173    def setCurvePoints(self): 
    150         self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
     174        self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
    151175 
    152176    def setTable(self): 
     
    167191            self.table.setColumnWidth(i, 80) 
    168192 
    169 ############################################################################## 
    170 # Test the widget, run from prompt 
    171193 
    172194if __name__=="__main__": 
     
    175197    ow.show() 
    176198     
    177     l1 = orange.BayesLearner() 
     199    l1 = Orange.classification.bayes.NaiveLearner() 
    178200    l1.name = 'Naive Bayes' 
    179201    ow.learner(l1, 1) 
    180202 
    181     data = orange.ExampleTable('iris.tab') 
    182     indices = orange.MakeRandomIndices2(data, p0 = 0.7) 
     203    data = Orange.data.Table('iris.tab') 
     204    indices = Orange.data.sample.SubsetIndices2(data, p0 = 0.7) 
    183205    train = data.select(indices, 0) 
    184206    test = data.select(indices, 1) 
     
    187209    ow.testset(test) 
    188210 
    189     l2 = orange.BayesLearner() 
     211    l2 = Orange.classification.bayes.NaiveLearner() 
    190212    l2.name = 'Naive Bayes (m=10)' 
    191     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    192     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
     213    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     214    l2.conditionalEstimatorConstructor = \ 
     215        Orange.statistics.estimate.ConditionalByRows( 
     216            estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
    193217    ow.learner(l2, 2) 
    194218 
    195     import orngTree 
    196     l4 = orngTree.TreeLearner(minSubset=2) 
     219    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    197220    l4.name = "Decision Tree" 
    198221    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveC.py

    r11085 r11593  
    66""" 
    77 
     8import Orange 
     9 
    810from OWWidget import * 
    911from OWColorPalette import ColorPixmap 
    10 import OWGUI, orngTest, orngStat 
    1112from OWGraph import * 
     13 
     14import OWGUI 
    1215 
    1316import warnings 
     
    2023        OWWidget.__init__(self, parent, signalManager, 'LearningCurveC') 
    2124 
    22         self.inputs = [("Data", ExampleTable, self.dataset), 
    23                        ("Learner", orange.Learner, self.learner, Multiple)] 
     25        self.inputs = [("Data", Orange.data.Table, self.dataset), 
     26                       ("Learner", Orange.classification.Learner, 
     27                        self.learner, Multiple)] 
    2428 
    2529        self.folds = 5     # cross validation folds 
     
    3034        self.graphDrawLines = 1 # draw lines between points in the graph 
    3135        self.graphShowGrid = 1  # show gridlines in the graph 
    32         self.selectedLearners = []  
     36        self.selectedLearners = [] 
     37 
    3338        self.loadSettings() 
    3439 
    35         warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
    36  
    37         self.setCurvePoints() # sets self.curvePoints, self.steps equidistantpoints from 1/self.steps to 1 
    38         self.scoring = [("Classification Accuracy", orngStat.CA), ("AUC", orngStat.AUC), ("BrierScore", orngStat.BrierScore), ("Information Score", orngStat.IS), ("Sensitivity", orngStat.sens), ("Specificity", orngStat.spec)] 
     40        warnings.filterwarnings("ignore", ".*builtin attribute.*", Orange.core.AttributeWarning) 
     41 
     42        self.setCurvePoints() # sets self.curvePoints, self.steps equidistant points from 1/self.steps to 1 
     43        self.scoring = [("Classification Accuracy", 
     44                         Orange.evaluation.scoring.CA), 
     45                        ("AUC", Orange.evaluation.scoring.AUC), 
     46                        ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     47                        ("Information Score", Orange.evaluation.scoring.IS), 
     48                        ("Sensitivity", Orange.evaluation.scoring.sens), 
     49                        ("Specificity", Orange.evaluation.scoring.spec)] 
    3950        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    4051        self.data = None   # data on which to construct the learning curve 
     
    4960        ## class selection (classQLB) 
    5061        OWGUI.separator(self.controlArea) 
     62 
     63        # ~SPHINX start color cb~ 
    5164        self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    52         self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", selectionMode=QListWidget.MultiSelection, callback=self.learnerSelectionChanged) 
     65        self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
     66                                 selectionMode=QListWidget.MultiSelection, 
     67                                 callback=self.learnerSelectionChanged) 
    5368         
    5469        self.llb.setMinimumHeight(50) 
    5570        self.blockSelectionChanges = 0 
     71        # ~SPHINX end color cb~ 
    5672 
    5773        OWGUI.separator(self.controlArea) 
     74 
    5875        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    5976        scoringNames = [x[0] for x in self.scoring] 
     
    6279 
    6380        OWGUI.separator(self.controlArea) 
     81 
    6482        box = OWGUI.widgetBox(self.controlArea, "Options") 
    6583        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     
    6886        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
    6987                   label='Learning curve points:  ', 
    70                    callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
     88                   callback=[self.setCurvePoints, 
     89                             lambda: self.computeCurve(self.commitOnChange)]) 
    7190 
    7291        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    73         self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    74  
     92        self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
     93                                      callback=self.computeCurve, disabled=1) 
     94 
     95        # ~SPHINX start main area tabs~ 
    7596        # start of content (right) area 
    7697        tabs = OWGUI.tabWidget(self.mainArea) 
    7798 
    78         # graph widget 
     99        # graph tab 
    79100        tab = OWGUI.createTabPage(tabs, "Graph") 
    80101        self.graph = OWGraph(tab) 
     
    84105        self.setGraphGrid() 
    85106 
    86         # table widget 
     107        # table tab 
    87108        tab = OWGUI.createTabPage(tabs, "Table") 
    88109        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
     110        # ~SPHINX end main area tabs~ 
    89111 
    90112        self.resize(550,200) 
     
    187209    def getLearningCurve(self, learners): 
    188210        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    189         curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
     211        curve = Orange.evaluation.testing.learning_curve_n( 
     212            learners, self.data, folds=self.folds, 
     213            proportions=self.curvePoints, 
     214            callback=pb.advance) 
     215 
    190216        pb.finish() 
    191217        return curve 
     
    250276        else: 
    251277            curve.setStyle(QwtPlotCurve.NoCurve) 
    252         curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
    253           QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
    254           QSize(self.graphPointSize, self.graphPointSize))) 
     278 
     279        curve.setSymbol( 
     280            QwtSymbol(QwtSymbol.Ellipse, 
     281                      QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
     282                      QSize(self.graphPointSize, self.graphPointSize))) 
     283 
    255284        curve.setPen(QPen(learner.color, 5)) 
    256285 
    257286    def drawLearningCurve(self, learner): 
    258         if not self.data: return 
    259         curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
     287        if not self.data: 
     288            return 
     289        curve = self.graph.addCurve( 
     290            learner.name, 
     291            xData=self.curvePoints, 
     292            yData=learner.score, 
     293            autoScale=True) 
    260294         
    261295        learner.curve = curve 
     
    268302            self.drawLearningCurve(l[1]) 
    269303 
    270 ############################################################################## 
    271 # Test the widget, run from prompt 
    272304 
    273305if __name__=="__main__": 
     
    276308    ow.show() 
    277309 
    278     l1 = orange.BayesLearner() 
     310    l1 = Orange.classification.bayes.NaiveLearner() 
    279311    l1.name = 'Naive Bayes' 
    280312    ow.learner(l1, 1) 
    281313 
    282     data = orange.ExampleTable('iris.tab') 
     314    data = Orange.data.Table('iris.tab') 
    283315    ow.dataset(data) 
    284316 
    285     l2 = orange.BayesLearner() 
     317    l2 = Orange.classification.bayes.NaiveLearner() 
    286318    l2.name = 'Naive Bayes (m=10)' 
    287     l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
    288     l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
    289  
    290     l3 = orange.kNNLearner(name="k-NN") 
     319    l2.estimatorConstructor = Orange.statistics.estimate.M(m=10) 
     320    l2.conditionalEstimatorConstructor = Orange.statistics.estimate.ConditionalByRows(estimatorConstructor = Orange.statistics.estimate.M(m=10)) 
     321 
     322    l3 = Orange.classification.knn.kNNLearner(name="k-NN") 
    291323    ow.learner(l3, 3) 
    292324 
    293     import orngTree 
    294     l4 = orngTree.TreeLearner(minSubset=2) 
     325    l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
    295326    l4.name = "Decision Tree" 
    296327    ow.learner(l4, 4) 
     
    299330#    ow.learner(None, 2) 
    300331#    ow.learner(None, 4) 
    301      
    302  
    303332 
    304333    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurve_plot.py

    r11085 r11593  
    7373        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    7474 
     75        # ~start main area tabs~ 
    7576        # start of content (right) area 
    7677        tabs = OWGUI.tabWidget(self.mainArea) 
    7778 
    78         # graph widget 
     79        # graph tab 
    7980        tab = OWGUI.createTabPage(tabs, "Graph") 
    8081        self.graph = OWPlot(tab) 
     
    8485        self.setGraphGrid() 
    8586 
    86         # table widget 
     87        # table tab 
    8788        tab = OWGUI.createTabPage(tabs, "Table") 
    8889        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
     90        # ~end main area tabs~ 
    8991 
    9092        self.resize(550,200) 
     
    256258 
    257259    def drawLearningCurve(self, learner): 
    258         if not self.data: return 
    259         curve = self.graph.add_curve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
     260        if not self.data: 
     261            return 
     262        curve = self.graph.add_curve( 
     263            learner.name, 
     264            xData=self.curvePoints, 
     265            yData=learner.score, 
     266            autoScale=True) 
    260267         
    261268        learner.curve = curve 
  • docs/extend-widgets/rst/basics.rst

    r11439 r11593  
    1414some really nice interaction may be over 1000 lines long. 
    1515 
    16 When we have started to write this tutorial, we have been working 
    17 on widgets for quite a while. There are now (now being in the very 
    18 time this page has been crafted) about 50 widgets available, and we 
    19 have pretty much defined how widgets and their interfaces should look 
    20 like. We have also made some libraries that help set up GUI with only 
    21 a few lines of code, and some mechanisms that one may found useful and 
    22 user friendly, like progress bars and alike. 
    23  
    2416On this page, we will start with some simple essentials, and then 
    2517show how to build a simple widget that will be ready to run within 
     
    3325category has an associated priority. Opening Orange Canvas, a visual 
    3426programming environment that comes with Orange, widgets are listed in 
    35 toolbox on the top of the window: 
     27a toolbox on the left: 
    3628 
    3729.. image:: widgettoolbox.png 
    3830 
    39 By default, Orange is installed in site-packages directory of 
    40 Python libraries. Widgets are all put in the subdirectories of 
    41 OrangeWidget directory; these subdirectories define widget 
    42 categories. For instance, under windows and default settings, a 
    43 directory that stores all the widgets displayed in the Evaluate pane is 
    44 *C:\\Python23\\Lib\\site-packages\\Orange\\OrangeWidgets\\Evaluate*. Figure 
    45 above shows that at the time of writing of this text there were five 
    46 widgets for evaluation of classifiers, and this is how my Evaluate 
    47 directory looked like: 
    48  
    49 .. image:: explorer.png 
    50  
    51 Notice that there are a number of files in Evaluate directory, so 
    52 how does Orange Canvas distinguish those that define widgets? Well, 
    53 widgets are Python script files that start with a header. Here is a 
    54 header for OWTestLearners.py:: 
    55  
     31The widgets and categories to which they belong are discovered at Orange 
     32Canvas startup leveraging setuptools/distribute and it's `entry points 
     33<http://pythonhosted.org/distribute/setuptools.html#dynamic-discovery-of-services-and-plugins>`_ 
     34protocol. In particular Orange Canvas looks for widgets using a 
     35`orange.widgets` entry point. 
     36 
     37 
     38First we will examine an existing widget in Orange. The Test Learners 
     39widget which is implemented in `OWTestLearners.py 
     40<http://orange.biolab.si/trac/browser/orange/Orange/OrangeWidgets/Evaluate/OWTestLearners.py>`_. 
     41 
     42Here is its header:: 
     43 
     44    """ 
    5645    <name>Test Learners</name> 
    5746    <description>Estimates the predictive performance of learners on a data set.</description> 
    58     <icon>icons/TestLearners.png</icon> 
     47    <icon>icons/TestLearners1.svg</icon> 
    5948    <priority>200</priority> 
    60  
    61 OWTestLearners is a Python script, so the header information we 
     49    """ 
     50 
     51OWTestLearners is a Python module, so the header information we 
    6252show about lies within the comment block, with triple quote opening 
    6353and closing the comment. Header defines the name of the widget, its 
     
    6555icon, and a number expressing the priority of the widget. The name of 
    6656the widget as given in the header will be the one that will be used 
    67 throughout in Orange Canvas. As for naming, the actual file name of 
    68 the widget is not important. The description of the widget is shown 
    69 once mouse rests on an toolbar icon representing the widget. And for 
     57throughout in Orange Canvas. The description of the widget is shown 
     58once mouse rests on an toolbox icon representing the widget. And for 
    7059the priority: this determines the order in which widgets appear in the 
    71 toolbox. The one shown above for Evaluate groups has widget named Test 
    72 Learners with priority 200, Classifications with 300, ROC Analysis 
    73 with 1010, Lift Curve with 1020 and Calibration Plot with 1030. Notice 
    74 that every time the priority number crosses a multiplier of a 1000, 
    75 there is a gap in the toolbox between the widgets; in this way, a 
    76 subgroups of the widgets within the same group can be imposed. 
     60toolbox within a category. 
    7761 
    7862Widgets communicate. They use typed channels, and exchange 
     
    8468    self.outputs = [("Evaluation Results", orngTest.ExperimentResults)] 
    8569 
    86 Above two lines are for Test Learners widget, so hovering with your 
    87 mouse over its icon in the widget toolbox would yield: 
    88  
    89 .. image:: mouseoverwidgetintoolbox.png 
    9070 
    9171We will go over the syntax of channel definitions later, but for 
     
    9373 
    9474   - Widgets are defined in a Python files. 
    95    - For Orange and Orange canvas to find them, they reside in subdirectories 
    96      in OrangeWidgets directory of Orange installation. The name of the 
    97      subdirectory matters, as this is the name of the widget category. Widgets 
    98      in the same directory will be grouped in the same pane of widget toolbox 
    99      in Orange Canvas. 
    100    - A file describing a widget starts with a header. This, given in sort of 
    101      XMLish style, tells about the name, short description, location of an 
    102      icon and priority of the widget. 
     75   - Widgets are registered through entry points and are discovered at 
     76     runtime. 
     77   - A python module implementing a widget starts with a header. This, given 
     78     in sort of XMLish style, tells about the name, short description, 
     79     location of an icon and priority of the widget. 
    10380   - The sole role of priority is to specify the placement (order) of widgets 
    10481     in the Orange Canvas toolbox. 
     
    10885     is seen from the outside. 
    10986 
    110 Oh, by the way. Orange caches widget descriptions to achieve a faster 
    111 startup, but this cache is automatically refreshed at startup if any change 
    112 is detected in widgets' files. 
     87.. note:: 
     88   Orange caches widget descriptions to achieve a faster startup, 
     89   but this cache is automatically refreshed at startup if any change 
     90   is detected in widgets' file. 
    11391 
    11492*********** 
     
    11997have some fun and write a widget. We will start with a very simple 
    12098one, that will receive a data set on the input and will output a data 
    121 set with 10% of the data instances. Not to mess with other widgets, we 
    122 will create a Test directory within OrangeWidgets directory, and write 
    123 the widget in a file called `OWDataSamplerA.py`: OW for Orange Widget, 
    124 DataSampler since this is what widget will be doing, and A since we 
    125 prototype a number of this widgets in our tutorial. 
    126  
    127 The script defining the OWDataSamplerA widget starts with a follwing header:: 
     99set with 10% of the data instances. We will call this widget 
     100`OWDataSamplerA.py` (OW for Orange Widget, DataSampler since this is what 
     101widget will be doing, and A since we prototype a number of this widgets 
     102in our tutorial). 
     103 
     104But first we must create a simple `python project`_ layout called *Demo*, 
     105that we will use in the rest of this tutorial. 
     106 
     107.. _`python project`: http://docs.python.org/2/distutils/examples.html#pure-python-distribution-by-package 
     108 
     109The layout should be:: 
     110 
     111   Demo/ 
     112         setup.py 
     113         orangedemo/ 
     114                     __init__.py 
     115                     OWDataSamplerA.py 
     116 
     117and the :download:`setup.py` should contain 
     118 
     119.. literalinclude:: setup.py 
     120 
     121Note that we declare our *orangedemo* package as containing widgets 
     122from an ad hoc defined category *Demo*. 
     123 
     124Following the previous example of OWTestLearners, our module defining 
     125the OWDataSamplerA widget starts with a following header:: 
    128126 
    129127    <name>Data Sampler</name> 
    130128    <description>Randomly selects a subset of instances from the data set</description> 
    131     <icon>icons/DataSamplerA.png</icon> 
     129    <icon>icons/DataSamplerA.svg</icon> 
    132130    <priority>10</priority> 
    133131 
     
    138136 
    139137Orange Widgets are all derived from the class OWWidget. The name of 
    140 the class should be match the file name, so the lines following the 
     138the class should match the file name, so the lines following the 
    141139header in our Data Sampler widget should look something like:: 
    142140 
     141    import Orange 
    143142    from OWWidget import * 
    144143    import OWGUI 
     
    147146 
    148147        def __init__(self, parent=None, signalManager=None): 
    149             OWWidget.__init__(self, parent, signalManager, 'SampleDataA') 
    150  
    151             self.inputs = [("Data", ExampleTable, self.data)] 
    152             self.outputs = [("Sampled Data", ExampleTable)] 
     148            OWWidget.__init__(self, parent, signalManager) 
     149 
     150            self.inputs = [("Data", Orange.data.Table, self.data)] 
     151            self.outputs = [("Sampled Data", Orange.data.Table)] 
    153152 
    154153            # GUI 
     
    158157            self.resize(100,50) 
    159158 
    160 In initialization, the widget calls the :obj:`__init__` function 
    161 of a base class, passing the name 'SampleData' which will, 
    162 essentially, be used for nothing else than a stem of a file for saving 
    163 the parameters of the widgets (we will regress on these somehow 
    164 latter in tutorial). Widget then defines inputs and outputs. For 
    165 input, widget defines a "Data" channel, accepting tokens of the type 
    166 orange.ExampleTable and specifying that :obj:`data` function will 
     159In initialization, the widget calls the :func:`__init__` method 
     160of a base class. Widget then defines inputs and outputs. For input, 
     161this is a *Data* channel, accepting tokens of the type 
     162:class:`Orange.data.Table` and specifying that :func:`data` method will 
    167163be used to handle them. For now, we will use a single output channel 
    168164called "Sampled Data", which will be of the same type 
    169 (orange.ExampleTable). 
    170  
    171 Notice that the types of the channels are 
    172 specified by a class name; you can use any classes here, but if your 
    173 widgets need to talk with other widgets in Orange, you will need to 
    174 check which classes are used there. Luckily, and as one of the main 
    175 design principles, there are just a few channel types that current 
    176 Orange widgets are using. 
     165(Orange.data.Table). 
     166 
     167Notice that the types of the channels are specified by a class; 
     168you can use any class here, but if your widgets need to talk with 
     169other widgets in Orange, you will need to check which classes are 
     170used there. Luckily, and as one of the main design principles, 
     171there are just a few channel types that current Orange widgets are 
     172using. 
    177173 
    178174The next four lines specify the GUI of our widget. This will be 
     
    196192 
    197193In order to complete our widget, we now need to define how will it 
    198 handle the input data. This is done in a function called 
    199 :obj:`data` (remember, we did introduce this name in the 
    200 specification of the input channel):: 
     194handle the input data. This is done in a method called :func:`data` 
     195(remember, we did introduce this name in the specification of the 
     196input channel):: 
    201197 
    202198    def data(self, dataset): 
     
    213209            self.send("Sampled Data", None) 
    214210 
    215 The function is defined within a class definition, so its first 
    216 argument has to be :obj:`self`. The second argument called 
    217 :obj:`dataset` is the token sent through the input channel which 
    218 our function needs to handle. 
     211The :obj:`dataset` argument is the token sent through the input 
     212channel which our method needs to handle. 
    219213 
    220214To handle the non-empty token, the widget updates the interface 
     
    225219"Sampled Data". 
    226220 
    227 Notice that the token can be empty (``dataset is None``), 
    228 resulting from either the sending widget to which we have connected 
    229 intentionally emptying the channel, or when the link between the two 
    230 widgets is removed. In any case, it is important that we always write 
    231 token handlers that appropriately handle the empty tokens. In our 
    232 implementation, we took care of empty input data set by appropriately 
    233 setting the GUI of a widget and sending an empty token to the 
    234 output channel. 
    235  
    236 .. 
    237    Although our widget is now ready to test, for a final touch, let's 
    238    design an icon for our widget. As specified in the widget header, we 
    239    will call it :download:`DataSamplerA.png <DataSamplerA.png>` and will 
    240    put it in icons subdirectory of OrangeWidgets directory (together with 
    241    all other icons of other widgets). 
     221Notice that the token can be empty (``None``), resulting from either 
     222the sending widget to which we have connected intentionally emptying 
     223the channel, or when the link between the two widgets is removed. 
     224In any case, it is important that we always write token handlers 
     225that appropriately handle the empty tokens. In our implementation, 
     226we took care of empty input data set by appropriately setting the 
     227GUI of a widget and sending an empty token to the output channel. 
     228 
     229 
     230Although our widget is now ready to test, for a final touch, let's 
     231design an icon for our widget. As specified in the widget header, we 
     232will call it :download:`DataSamplerA.svg <DataSamplerA.svg>` and will 
     233put it in `icons` subdirectory of `orangedemo` directory. 
     234 
     235With this we cen now go ahead and install the orangedemo package. We 
     236will do this by running :code:`python setup.py develop` command from 
     237the `Demo` directory. 
     238 
     239.. note:: 
     240   Depending on your python installation you might need 
     241   administrator/superuser privileges. 
    242242 
    243243For a test, we now open Orange Canvas. There should be a new pane in a 
    244 widget toolbox called Test (this is the name of the directory we have 
    245 used to put in our widget). If we click on this pane, it displays an 
    246 icon of our widget. Try to hoover on it to see if the header and 
    247 channel info was processed correctly: 
     244widget toolbox called Demo. If we click on this pane, it displays an 
     245icon of our widget. Try to hover on it to see if the header and channel 
     246info was processed correctly: 
    248247 
    249248.. image:: samplewidgetontoolbox.png 
    250249 
    251250Now for the real test. We put the File widget on the schema (from 
    252 Data pane), read iris.tab data set. We also put our Data Sampler widget on the pane and 
    253 open it (double click on the icon, or right-click and choose 
    254 Open): 
     251Data pane) and load the iris.tab data set. We also put our Data 
     252Sampler widget on the scheme and open it (double click on the icon, 
     253or right-click and choose Open): 
    255254 
    256255.. image:: datasamplerAempty.png 
    257256 
    258 Drag this window off the window with the widget schema of Orange 
    259 Canvas, and connect File and Data Sampler widget (click on an ouput 
    260 connector - green box - of the File widget, and drag the line to the 
    261 input connector of the Data Sampler). If everything is ok, as soon as 
    262 you release the mouse the connection is established and, the token 
    263 that was waiting on the output of the file widget was sent to the Data 
    264 Sampler widget, which in turn updated its window: 
     257Now connect the File and Data Sampler widget (click on an output 
     258connector of the File widget, and drag the line to the input connector 
     259of the Data Sampler). If everything is ok, as soon as you release the 
     260mouse, the connection is established and, the token that was waiting 
     261on the output of the file widget was sent to the Data Sampler widget, 
     262which in turn updated its window: 
    265263 
    266264.. image:: datasamplerAupdated.png 
     
    292290        ow = OWDataSamplerA() 
    293291        ow.show() 
    294         dataset = orange.ExampleTable('iris.tab') 
     292        dataset = Orange.data.Table('iris.tab') 
    295293        ow.data(dataset) 
    296294        appl.exec_() 
    297295 
    298296These are essentially some calls to Qt routines that run GUI for our 
    299 widgets. At the core, however, notice that instead of sending the 
    300 token to the input channel, we directly called the routine for token 
    301 handling (:obj:`data`). 
    302  
    303 To test your widget in more complex environment, that for instance 
    304 requires to set a complex schema in which your widget collaborates, 
    305 use Orange Canvas to set the schema and then either 1) save the schema 
    306 to be opened every time you run Orange Canvas, or 2) save this schema 
    307 (File menu) as an application within a single file you will need to 
    308 run each time you will test your widget. 
     297widgets. Notice that we call the :func:`data` method directly. 
  • docs/extend-widgets/rst/channels.rst

    r11439 r11593  
    1818******************** 
    1919 
    20 First, I do not like the name, but can't make up anything better. In 
    21 essence, the basic idea about "multi-input" channels is that they can 
     20In essence, the basic idea about "multi-input" channels is that they can 
    2221be used to connect them with several output channels. That is, if a 
    2322widget supports such a channel, several widgets can feed their input 
     
    2524 
    2625Say we want to build a widget that takes a data set and test 
    27 various predictive modelling techniques on it. A widget has to have an 
     26various predictive modeling techniques on it. A widget has to have an 
    2827input data channel, and this we know how to deal with from our 
    2928:doc:`previous <settings>` lesson. But, somehow differently, we 
     
    5049widget are defined by:: 
    5150 
    52     self.inputs = [("Data", ExampleTable, self.dataset), 
    53                    ("Learner", orange.Learner, self.learner, Multiple + Default)] 
     51    self.inputs = [("Data", Orange.data.Table, self.dataset), 
     52                   ("Learner", Orange.classification.Learner, 
     53                    self.learner, Multiple + Default)] 
    5454 
    5555Notice that everything is pretty much the same as it was with 
     
    6363channel for its type (more on default channels later). 
    6464 
    65 .. note:: :obj:`Default` flag here is used for illustration. Since *Learner* 
    66           channel is the only channel for a :class:`orange.Learner` type 
    67           it is also the default. 
     65.. note:: 
     66   :obj:`Default` flag here is used for illustration. Since *"Learner"* 
     67   channel is the only channel for a :class:`Orange.classification.Learner` 
     68   type it is also the default. 
    6869 
    6970How does the widget know from which widget did the token come from? 
     
    7172sending the token, and having a multi-input channel only tells Orange to 
    7273send a token together with sending widget id, the two arguments with 
    73 which the receiving function is called. For our :obj:`Learner` 
    74 channel the receiving function is :obj:`learner`, and this looks 
     74which the receiving function is called. For our *"Learner"* 
     75channel the receiving function is :func:`learner`, and this looks 
    7576like the following:: 
    7677 
     
    131132(:obj:`None`). Remember that sending an empty learner 
    132133essentially means that the link with the sending widget was removed, 
    133 hance we need to remove such learner from our list. If a non-empty 
     134hence we need to remove such learner from our list. If a non-empty 
    134135learner was sent, then it is either a new learner (say, from a widget 
    135136we have just linked to our learning curve widget), or an update 
     
    149150all that is needed is the augmenting the list:: 
    150151 
    151     self.scoring = [("Classification Accuracy", orngStat.CA), 
    152                     ("AUC", orngStat.AUC), 
    153                     ("BrierScore", orngStat.BrierScore), 
    154                     ("Information Score", orngStat.IS), 
    155                     ("Sensitivity", orngStat.sens), 
    156                     ("Specificity", orngStat.spec)] 
     152    self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), 
     153                    ("AUC", Orange.evaluation.scoring.AUC), 
     154                    ("BrierScore", Orange.evaluation.scoring.Brier_score), 
     155                    ("Information Score", Orange.evaluation.scoring.IS), 
     156                    ("Sensitivity", Orange.evaluation.scoring.Sensitivity), 
     157                    ("Specificity", Orange.evaluation.scoring.Specificity)] 
    157158 
    158159which is defined in the initialization part of the widget. The 
     
    178179is:: 
    179180 
    180     self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
     181    self.outputs = [("Sampled Data", Orange.data.Table), 
     182                    ("Other Data", Orange.data.Table)] 
    181183 
    182184We used this in the third incarnation of :download:`data sampler widget <OWDataSamplerC.py>`, 
    183 with essentially the only other change in the code in the :obj:`selection` and 
    184 :obj:`commit` functions:: 
     185with essentially the only other change in the code in the :func:`selection` and 
     186:func:`commit` functions:: 
    185187 
    186188    def selection(self): 
    187         indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
     189        indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
    188190        ind = indices(self.dataset) 
    189191        self.sample = self.dataset.select(ind, 0) 
     
    197199If a widget that has multiple channels of the same type is 
    198200connected to a widget that accepts such tokens, Orange Canvas opens a 
    199 window asking the user to confirm which channels to connect. The 
    200 channel mentioned in :obj:`self.outputs` is connected by 
    201 default. Hence, if we have just connected Data Sampler 
    202 (C) widget to a Data Table widget in a schema below: 
     201window asking the user to confirm which channels to connect. Hence, 
     202if we have just connected *Data Sampler (C)* widget to a Data Table 
     203widget in a schema below: 
    203204 
    204205.. image:: datasampler-totable.png 
     
    223224training data set channel will be the default one. 
    224225 
    225 When enlisting the input channel of the same type, the non-default 
     226When enlisting the input channel of the same type, the default 
    226227channels have a special flag in the channel specification list. So for 
    227228our new :download:`learning curve <OWLearningCurveB.py>` widget, the 
    228229channel specification is:: 
    229230 
    230     self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
    231                    ("Test Data", ExampleTable, self.testset), 
    232                    ("Learner", orange.Learner, self.learner, Multiple)] 
     231    self.inputs = [("Train Data", Orange.data.Table, self.trainset, Default), 
     232                   ("Test Data", Orange.data.Table, self.testset), 
     233                   ("Learner", Orange.classification.Learner, self.learner, Multiple)] 
    233234 
    234235That is, the :obj:`Train Data` channel is a single-token 
     
    240241.. image:: file-to-learningcurveb.png 
    241242 
    242 That is, no window with a query on which channels 
    243 to connect to will open. To find out which channels got connected, 
    244 double click on the green link between the two widgets: 
    245  
    246 .. image:: file-to-learningcurveb-channels.png 
     243That is, no window with a query on which channels to connect to will 
     244open, as the default *"Train Data"* was selected. 
  • docs/extend-widgets/rst/contextsettings.rst

    r11408 r11593  
    2121selecting a subset of attributes and the class attributes (note that a 
    2222better widget for this task is already included in your Orange 
    23 instalation). 
     23installation). 
    2424 
    2525.. image:: attributesampler.png 
     
    2929somehow store the user's selection. 
    3030 
    31 Here's the widget's :obj:`__init__` function. 
    32  
    33 Part of :download:`OWAttributeSampler.py <OWAttributeSampler.py>`:: 
    34  
    35     def __init__(self, parent=None, signalManager=None): 
    36         OWWidget.__init__(self, parent, signalManager, 'AttributeSampler') 
    37  
    38         self.inputs = [("Examples", ExampleTable, self.dataset)] 
    39         self.outputs = [("Examples", ExampleTable)] 
    40  
    41         self.icons = self.createAttributeIconDict() 
    42  
    43         self.attributeList = [] 
    44         self.selectedAttributes = [] 
    45         self.classAttribute = None 
    46         self.loadSettings() 
    47  
    48         OWGUI.listBox(self.controlArea, self, "selectedAttributes", "attributeList", box="Selected attributes", selectionMode = QListWidget.ExtendedSelection) 
    49         OWGUI.separator(self.controlArea) 
    50         self.classAttrCombo = OWGUI.comboBox(self.controlArea, self, "classAttribute", box="Class attribute") 
    51         OWGUI.separator(self.controlArea) 
    52         OWGUI.button(self.controlArea, self, "Commit", callback = self.outputData) 
    53  
    54         self.resize(150,400) 
     31Here's the widget's :func:`__init__` function. 
     32 
     33Part of :download:`OWAttributeSampler.py <OWAttributeSampler.py>` 
     34 
     35.. literalinclude:: OWAttributeSampler.py 
     36   :pyobject: OWAttributeSampler.__init__ 
    5537 
    5638Note that we are strictly using controls from OWGUI. As for the 
     
    6446 
    6547When the widget gets the data, a function :obj:`dataset` is 
    66 called. 
    67  
    68 Part of :download:`OWAttributeSampler.py`:: 
     48called:: 
    6949 
    7050    def dataset(self, data): 
     
    8565 
    8666 
    87     def outputData(self): 
    88         if not self.data: 
    89             self.send("Examples", None) 
    90         else: 
    91             newDomain = orange.Domain([self.data.domain[i] for i in self.selectedAttributes], self.data.domain[self.classAttribute]) 
    92             newData = orange.ExampleTable(newDomain, self.data) 
    93             self.send("Examples", newData) 
     67.. literalinclude:: OWAttributeSampler.py 
     68   :pyobject: OWAttributeSampler.outputData 
     69 
    9470 
    9571Nothing special here (yet). We fill the list box, deselect all 
     
    11086exist in the actual domain at all. 
    11187 
    112 To make the setting dependent on the context, we put :: 
    113  
    114     contextHandlers = {"": DomainContextHandler("", [ 
    115             ContextField("classAttribute", DomainContextHandler.Required), 
    116             ContextField("attributeList", DomainContextHandler.List + 
    117                                           DomainContextHandler.SelectedRequired, 
    118                          selected="selectedAttributes")])} 
     88To make the setting dependent on the context, we put 
     89 
     90.. literalinclude:: OWAttributeSampler.py 
     91   :start-after: # ~start context handler~ 
     92   :end-before: # ~end context handler~ 
    11993 
    12094at the same place where we usually declare :obj:`settingsList`. 
     
    156130(:obj:`DomainContextHandler.Optional`); sometimes certain 
    157131attribute doesn't really matter, so if it is present in the domain, 
    158 it's gonna be used, otherwise not. And for the list, we could say 
     132it's going to be used, otherwise not. And for the list, we could say 
    159133:obj:`DomainContextHandler.List + DomainContextHandler.Required` 
    160134in which case all the attributes on the list would be required for the 
    161135domain to match. 
    162136 
    163 The default flag is :obj:`DomainContextHandler.Required`, and there are other shortcuts for declaring the context, too. The above code could be simplified as :: 
    164  
    165     contextHandlers = {"": DomainContextHandler("", [ 
    166             "classAttribute", 
    167             ContextField("attributeList", DomainContextHandler.SelectedRequiredList, 
    168                          selected="selectedAttributes")])} 
     137The default flag is :obj:`DomainContextHandler.Required`, and there 
     138are other shortcuts for declaring the context, too. The above code could 
     139be simplified as :: 
     140 
     141    contextHandlers = { 
     142        "": DomainContextHandler( 
     143            "", 
     144            ["classAttribute", 
     145             ContextField("attributeList", 
     146                          DomainContextHandler.SelectedRequiredList, 
     147                          selected="selectedAttributes")])} 
    169148 
    170149Why the dictionary and the empty string as the key? A widget can 
     
    186165function :obj:`dataset` 
    187166 
    188 Part of :download:`OWAttributeSampler.py`:: 
    189  
    190     def dataset(self, data): 
    191         self.closeContext() 
    192      
    193         self.classAttrCombo.clear() 
    194         if data: 
    195             self.attributeList = [(attr.name, attr.varType) for attr in data.domain] 
    196             self.selectedAttributes = [] 
    197             for attrName, attrType in self.attributeList: 
    198                 self.classAttrCombo.addItem(self.icons[attrType], attrName) 
    199             self.classAttribute = 0 
    200         else: 
    201             self.attributeList = [] 
    202             self.selectedAttributes = [] 
    203             self.classAttrCombo.addItem("") 
    204      
    205         self.openContext("", data) 
    206      
    207         self.data = data 
    208         self.outputData() 
    209  
    210 We added only two lines. First, before you change any controls in the widget, you need to call :obj:`self.closeContext` (the function has an optional argument, the context name, but since we use the default name, an empty string, we can omit it). This reads the data from the widget into the stored context. Then the function proceeds as before: the controls (the list box and combo box) are filled in as if there were no context handling (this is important, so once again: widget should be set up as if there were not context dependent settings). When the controls are put in a consistent state, we call :obj:`self.openContext`. The first argument is the context name and the second is the object from which the handler reads the context. In case of :obj:`DomainContextHandler` this can be either a domain or the data. :obj:`openContext` will make the context handler search through the stored context for the one that (best) matches the data, and if one is find the widget's state is set accordingly (that is, the list boxes are filled, attributes in it are selected etc.). If no context is found, a new context is established and the data from widget is copied to the context. 
     167.. literalinclude:: OWAttributeSampler.py 
     168   :pyobject: OWAttributeSampler.dataset 
     169 
     170We added only two lines. First, before you change any controls in 
     171the widget, you need to call :obj:`self.closeContext` (the function 
     172has an optional argument, the context name, but since we use the 
     173default name, an empty string, we can omit it). This reads the 
     174data from the widget into the stored context. Then the function 
     175proceeds as before: the controls (the list box and combo box) 
     176are filled in as if there were no context handling (this is 
     177important, so once again: widget should be set up as if there 
     178were not context dependent settings). When the controls are put 
     179in a consistent state, we call :obj:`self.openContext`. The first 
     180argument is the context name and the second is the object from 
     181which the handler reads the context. In case of 
     182:obj:`DomainContextHandler` this can be either a domain or the 
     183data. :obj:`openContext` will make the context handler search 
     184through the stored context for the one that (best) matches the 
     185data, and if one is find the widget's state is set accordingly 
     186(that is, the list boxes are filled, attributes in it are selected 
     187etc.). If no context is found, a new context is established and the 
     188data from widget is copied to the context. 
    211189 
    212190What can be stored as a context dependent setting? Anything, even 
  • docs/extend-widgets/rst/graphing.rst

    r11439 r11593  
    44 
    55The most fun widgets are of course those that include graphics. For 
    6 this we either use control called canvas, which is Qt's general 
    7 control for doing any graphics of choice (widgets for tree and heat map 
    8 visualizations, for instance, use this), or use a special control for 
    9 drawing data plots as provided in Qwt library and PyQwt 
     6this we either use Qt's :class:`QGraphicsScene` (widgets for tree and 
     7heat map visualizations, for instance, use this), or use a special 
     8control for drawing data plots as provided in Qwt library and :mod:`PyQwt` 
    109interface. Here we look at the latter, and extend our learning curve 
    1110widget with a control that plots the curve. 
     
    2625The widget still provides learning curve table, but this is now 
    2726offered in a tabbed pane together with a graph. The code for 
    28 definition of the tabbed pane, and initialization of the graph is:: 
     27definition of the tabbed pane, and initialization of the graph is 
    2928 
    30     # start of content (right) area 
    31     tabs = OWGUI.tabWidget(self.mainArea) 
     29.. literalinclude:: OWLearningCurveC.py 
     30   :start-after: # ~SPHINX start main area tabs~ 
     31   :end-before: # ~SPHINX end main area tabs~ 
    3232 
    33     # graph widget 
    34     tab = OWGUI.createTabPage(tabs, "Graph") 
    35     self.graph = OWGraph(tab) 
    36     self.graph.setAxisAutoScale(QwtPlot.xBottom) 
    37     self.graph.setAxisAutoScale(QwtPlot.yLeft) 
    38     tab.layout().addWidget(self.graph) 
    39     self.setGraphGrid() 
     33:class:`~OWGraph.OWGrap` is a convenience subclass of :class:`QwtPlot` 
     34and is imported from OWGraph module. For the graph, we use 
     35:func:`setAxisAutoScale` to request that the axis are automatically 
     36set in regard to the data that is plotted in the graph. We plot 
     37the graph in using the following code 
    4038 
    41 :obj:`OWGrap` is a convenience subclass of QwtPlot and is imported from 
    42 OWGraph module. For the graph, we use :obj:`setAxisAutoScale` to 
    43 request that the axis are automatically set in regard to the data that 
    44 is plotted in the graph. We plot the graph in using the following 
    45 code:: 
    46  
    47     def drawLearningCurve(self, learner): 
    48         if not self.data: return 
    49         curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
    50  
    51         learner.curve = curve 
    52         self.setGraphStyle(learner) 
    53         self.graph.replot() 
     39.. literalinclude:: OWLearningCurveC.py 
     40   :pyobject: OWLearningCurveC.drawLearningCurve 
    5441 
    5542This is simple. We store the curve returned from :obj:`addCurve` with a 
    56 learner, and use a trick allowed in Orange that we can simply store 
    57 this as a new attribute to the learning object. By default, Orange 
    58 would give a warning of the type:: 
    59  
    60     c:\Python23\Lib\site-packages\orange\OrangeWidgets\Test\OWLearningCurveC.py:269: 
    61      AttributeWarning: 'curve' is not a builtin attribute of 'kNNLearner' 
    62       setattr(learner, "curve", curve) 
    63  
    64 but we surpress such warnings with a line:: 
    65  
    66     warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
    67  
     43learner. 
    6844 
    6945.. warning:: 
    7046 
    71    This is a very bad design. Please do **not** store widget data in the 
    72    input objects. 
     47   This is a very bad design. Please do **not** store widget specific 
     48   data in the input objects. 
    7349 
    7450 
    75 in the initialization part of the widget. In this way, each learner 
    76 also stores the current scores, which is a list of numbers to be 
    77 plotted in Qwt graph. The details on how the plot is set are dealt 
    78 with in :obj:`setGraphStyle` function:` :: 
     51In this way, each learner also stores the current scores, which is a 
     52list of numbers to be plotted in Qwt graph. The details on how the 
     53plot is set are dealt with in :obj:`setGraphStyle` function: 
    7954 
    80     def setGraphStyle(self, learner): 
    81         curve = learner.curve 
    82         if self.graphDrawLines: 
    83             curve.setStyle(QwtPlotCurve.Lines) 
    84         else: 
    85             curve.setStyle(QwtPlotCurve.NoCurve) 
    86         curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
    87           QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
    88           QSize(self.graphPointSize, self.graphPointSize))) 
    89         curve.setPen(QPen(learner.color, 5)) 
     55.. literalinclude:: OWLearningCurveC.py 
     56   :pyobject: OWLearningCurveC.setGraphStyle 
     57 
    9058 
    9159Notice that the color of the plot line that is specific to the 
     
    10371instances of one class should be plotted in scatter plot and parallel 
    10472axis plot using the same color. Developers are thus advised to use 
    105 :obj:`ColorPaletteHSV`, which is provided as a method within 
    106 :mod:`OWWidget` module. :obj:`ColorPaletteHSV` takes an 
    107 integer as an attribute, and returns a list of corresponding number of 
     73:obj:`ColorPaletteHSV`, which can be imported from :mod:`OWWidget` 
     74module. :obj:`ColorPaletteHSV` takes an 
     75integer as an parameter, and returns a list of corresponding number of 
    10876colors. In our learning curve widget, we use it within a function that 
    109 sets the list box with learners:: 
     77sets the list box with learners 
    11078 
    111     def updatellb(self): 
    112         self.blockSelectionChanges = 1 
    113         self.llb.clear() 
    114         colors = ColorPaletteHSV(len(self.learners)) 
    115         for (i,lt) in enumerate(self.learners): 
    116             l = lt[1] 
    117             item = QListWidgetItem(ColorPixmap(colors[i]), l.name) 
    118             self.llb.addItem(item) 
    119             item.setSelected(l.isSelected) 
    120             l.color = colors[i] 
    121         self.blockSelectionChanges = 0 
     79.. literalinclude:: OWLearningCurveC.py 
     80   :pyobject: OWLearningCurveC.updatellb 
    12281 
    12382The code above sets the items of the list box, where each item 
     
    12685returned by :obj:`ColorPixmap` function defined in 
    12786:obj:`OWColorPalette.py`. Else, the classifier's list box control is 
    128 defined in the initialization of the widget using:: 
     87defined in the initialization of the widget using 
    12988 
    130     self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    131     self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
    132                              selectionMode=QListWidget.MultiSelection, 
    133                              callback=self.learnerSelectionChanged) 
    134  
    135     self.llb.setMinimumHeight(50) 
    136     self.blockSelectionChanges = 0 
     89.. literalinclude:: OWLearningCurveC.py 
     90   :start-after: # ~SPHINX start color cb~ 
     91   :end-before: # ~SPHINX end color cb~ 
    13792 
    13893Now, what is this :obj:`blockSelectionChanges`? Any time 
    13994user makes a selection change in list box of classifiers, we want to 
    14095invoke the procedure called 
    141 :obj:`learnerSelectionChanged`. But we want to perform 
     96:func:`learnerSelectionChanged`. But we want to perform 
    14297actions there when changes in the list box are invoked from clicking 
    14398by a user, and not by changing list box items from a program. This is 
    144 why, every time we want :obj:`learnerSelectionChanged` not to 
     99why, every time we want :func:`learnerSelectionChanged` not to 
    145100perform its function, we set :obj:`self.blockSelectionChanges` 
    146101to 1. 
    147102 
    148 In our widget, :obj:`learnerSelectionChanged` figures out 
     103In our widget, :func:`learnerSelectionChanged` figures out 
    149104if any curve should be removed from the graph (the user has just 
    150105deselected the corresponding item in the list box) or added to the 
    151 graph (the user just selected a learner):: 
     106graph (the user just selected a learner) 
    152107 
     108.. literalinclude:: OWLearningCurveC.py 
     109   :pyobject: OWLearningCurveC.learnerSelectionChanged 
     110 
     111.. 
    153112    def learnerSelectionChanged(self): 
    154113        if self.blockSelectionChanges: 
  • docs/extend-widgets/rst/progressbar.rst

    r11408 r11593  
    77computation. For that purpose orange widgets use progress bar 
    88functions, to which they report progress in terms of per-cent 
    9 completion of the task. The progress is then either reported above the 
    10 icon of the widget in Orange Canvas. 
     9completion of the task. The progress is then either on the icon 
     10of the widget in Orange Canvas 
    1111 
    12 .. image:: progressbar-canvas.png 
     12.. image:: learningcurve.png 
    1313 
    1414or in the title bar of the widget's window. There, Orange also 
    1515reports on the estimated time of completion of the task: 
    1616 
    17 .. image:: progressbar-widget.png 
     17.. image:: learningcurve-output.png 
    1818 
    19 Class :obj:`OWWidget`, the mother class of all 
    20 widgets, has for this purpose a set of functions, which include: 
     19:class:`OWWidget` has for this purpose a set of functions, which include: 
    2120 
    2221.. method:: progressBarInit() 
     
    2625.. method:: progressBarFinished() 
    2726 
    28  
    2927where value is any number between 0 and 100. Sometimes, like it is 
    3028the case for our widgets, we know about the number of iterations involved in 
    3129computation, and we would only like to advance the progress bar for 
    3230some constant at the end of the iteration. For this, we use 
    33 :obj:`ProgressBar` class in :obj:`OWGUI`, and the code in 
     31:class:`ProgressBar` class in :mod:`OWGUI`, and the code in 
    3432the learning curve widget described in the previous lesson that does 
    3533it is as follows:: 
    3634 
    3735    def getLearningCurve(self, learners): 
    38         pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    39         curve = orngTest.learningCurveN(learners, self.data, 
    40             folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
     36        pb = OWGUI.ProgressBar(self, iterations=self.steps * self.folds) 
     37        curve = Orange.evaluation.testing.learning_curve_n( 
     38            learners, self.data, folds=self.folds, 
     39            proportions=self.curvePoints, callback=pb.advance) 
    4140        pb.finish() 
    4241        return curve 
    4342 
    44 :obj:`ProgressBar` class removes the need to define any 
    45 special function to compute the percent of the task done and set the 
    46 progress bar, and instead uses :obj:`ProgressBar`'s method 
    47 :obj:`advance` for this purpose. 
     43:class:`ProgressBar` class removes the need to define any special 
     44function to compute the percent of the task done and set the 
     45progress bar, and instead uses :class:`ProgressBar`'s method 
     46:func:`advance` for this purpose. 
  • docs/extend-widgets/rst/settings.rst

    r11439 r11593  
    4646    class OWDataSamplerB(OWWidget): 
    4747        settingsList = ['proportion', 'commitOnChange'] 
    48         def __init__(self, parent=None, signalManager=None): 
    4948 
    5049Any setting has to be initialized, and then we need to call 
     
    157156of this widget. To distinguish it with a widget we have developed in the 
    158157previous section, we have designed a special 
    159 :download:`icon <DataSamplerB.png>` for it. If you wish to test is 
     158:download:`icon <DataSamplerB.svg>` for it. If you wish to test is 
    160159widget in the Orange Canvas, put its code in the Test directory we 
    161160have created for the previous widget, update the Canvas registry, and 
     
    410409:obj:`ContextField("attrX", DomainContextHandler.Required)` 
    411410 
    412 .. 
    413    But see this monster from widget "Select Attributes" (file OWDataDomain.py):: 
    414  
    415        contextHandlers = {"": DomainContextHandler("", 
    416            [ContextField("chosenAttributes", 
    417                           DomainContextHandler.RequiredList, 
    418                           selected="selectedChosen", reservoir="inputAttributes"), 
    419             ContextField("classAttribute", 
    420                           DomainContextHandler.RequiredList, 
    421                           selected="selectedClass", reservoir="inputAttributes"), 
    422             ContextField("metaAttributes", 
    423                           DomainContextHandler.RequiredList, 
    424                           selected="selectedMeta", reservoir="inputAttributes") 
    425        ])} 
    426  
    427  
    428    :obj:`ContextField`'s constructor gets the name and flags and a list of 
    429    arguments that are written directly into the object instance. To follow the 
    430    example, recall what Select Attributes looks like: it allows you to select a 
    431    subset of attributes, the class attribute and the meta attributes that you 
    432    want to use; the attributes in the corresponding three list boxes are stored 
    433    in the widget's variables :obj:`chosenAttributes`, :obj:`classAttribute` 
    434    and :obj:`metaAttributes` respectively. When the user selects some attributes 
    435    in any of these boxes, the selection is stored in :obj:`selectedChosen`, 
    436    :obj:`selectedClass` and :obj:`selectedMeta`. The remaining attributes 
    437    - those that are not in any of these three list boxes - are in the leftover 
    438    listbox on the left-hand side of the widget, and the content of the box is 
    439    stored in the widget's variable :obj:`inputAttributes`. 
    440  
    441    The above definition tells that the context needs to store the contents of 
    442    the three list boxes by specifying the corresponding variables; the list of 
    443    attributes is given as the name of the field and the list of selected 
    444    attributes is in the optional named attribute :obj:`selected`. By 
    445    :obj:`reservoir` we told the context handler that the attributes are taken 
    446    from :obj:`inputAttributes`. So, when a context is retrieved, all the 
    447    attributes that are not in any of the three list boxes are put into 
    448    :obj:`inputAttributes`. 
    449  
    450    Why the mess? Couldn't we just store :obj:`inputAttributes` as the fourth 
    451    list box? Imagine that the user first loads the data with attributes A, B, 
    452    C, D, E and F, puts A, B, C in chosen and D in class. E and F are left in 
    453    :obj:`inputAttributes`. Now she loads another data which has attributes A, 
    454    B, C, D, E, and G. The contexts should match (the new data has all the 
    455    attributes we need), but :obj:`inputAttributes` should now contain E and 
    456    G, not E and F, since F doesn't exist any more, while G needs to be made 
    457    available. 
    458  
    459    You can use :obj:`ContextField` (instead of tuples and strings) for 
    460    declaring any fields, but you will usually need them only for lists or, 
    461    maybe, some complicated future controls. 
    462  
    463411 
    464412***************************** 
  • docs/reference/rst/code/exclude-from-regression.txt

    r10291 r11588  
    88kmeans-silhouette.py 
    99kmeans-trace.py 
     10tree_c45.py 
     11tree_c45_printtree.py 
  • install-scripts/mac/bundle-build-hg.sh

    r11533 r11594  
    2020 
    2121echo "Preaparing the bundle template" 
    22 TEMPLATE_VERSION=`curl --silent http://orange.biolab.si/download/bundle-templates/CURRENT.txt` 
     22TEMPLATE_VERSION=$(curl --silent http://orange.biolab.si/download/bundle-templates/CURRENT.txt) 
    2323curl --silent http://orange.biolab.si/download/bundle-templates/Orange-template-${TEMPLATE_VERSION}.tar.gz | tar -xz -C $WORK_DIR 
    2424 
     
    2828fi 
    2929 
    30 # Create bundle startup script 
    31 cat <<-'EOF' > ${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/Orange 
     30APP=${TMP_BUNDLE_DIR}/Orange.app 
     31 
     32# Python interpreter in the bundle 
     33PYTHON=${APP}/Contents/MacOS/python 
     34 
     35# Python version 
     36PY_VER=`$PYTHON -c "import sys; print sys.version[:3]"` 
     37 
     38SITE_PACKAGES=${APP}/Contents/Frameworks/Python.framework/Versions/${PY_VER}/lib/python${PY_VER}/site-packages/ 
     39 
     40# easy_install script in the bundle 
     41EASY_INSTALL=${APP}/Contents/MacOS/easy_install 
     42 
     43# Link Python.app startup script to top bundle 
     44ln -fs ../Frameworks/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python ${APP}/Contents/MacOS/PythonAppStart 
     45 
     46echo "Preparing startup scripts" 
     47 
     48# Create an enironment startup script 
     49cat <<-'EOF' > $APP/Contents/MacOS/ENV 
     50    # Create an environment for running python from the bundle 
     51    # Should be run as "source ENV" 
     52 
     53    BUNDLE_DIR=`dirname "$0"`/../ 
     54    BUNDLE_DIR=`perl -MCwd=realpath -e 'print realpath($ARGV[0])' "$BUNDLE_DIR"`/ 
     55    FRAMEWORKS_DIR="$BUNDLE_DIR"Frameworks/ 
     56    RESOURCES_DIR="$BUNDLE_DIR"Resources/ 
     57 
     58    PYVERSION="2.7" 
     59 
     60    PYTHONEXECUTABLE="$FRAMEWORKS_DIR"Python.framework/Resources/Python.app/Contents/MacOS/Python 
     61    PYTHONHOME="$FRAMEWORKS_DIR"Python.framework/Versions/"$PYVERSION"/ 
     62 
     63    DYLD_FRAMEWORK_PATH="$FRAMEWORKS_DIR"${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH} 
     64 
     65    export PYTHONEXECUTABLE 
     66    export PYTHONHOME 
     67 
     68    export DYLD_FRAMEWORK_PATH 
     69 
     70    # Some non framework libraries are put in $FRAMEWORKS_DIR by macho_standalone 
     71    export DYLD_LIBRARY_PATH="$FRAMEWORKS_DIR"${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH} 
     72EOF 
     73 
     74# Create Orange application startup script 
     75cat <<-'EOF' > ${APP}/Contents/MacOS/Orange 
    3276    #!/bin/bash 
    3377 
    34     source `dirname "$0"`/ENV 
     78    DIRNAME=$(dirname "$0") 
     79    source "$DIRNAME"/ENV 
    3580 
    3681    # LaunchServices passes the Carbon process identifier to the application with 
     
    4085    fi 
    4186 
    42     exec -a "$0" "$PYTHONEXECUTABLE" -m Orange.OrangeCanvas.main "$@" 
     87    exec -a "$0" "$DIRNAME"/PythonAppStart -m Orange.OrangeCanvas.main "$@" 
    4388EOF 
    4489 
    45 chmod +x ${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/Orange 
    46  
    47 # Python interpreter in the bundle 
    48 PYTHON=${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/python 
    49  
    50 # easy_install script in the bundle 
    51 EASY_INSTALL=${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/easy_install 
    52  
    53 #Python version 
    54 PY_VER=`$PYTHON -c "import sys; print sys.version[:3]"` 
     90chmod +x ${APP}/Contents/MacOS/Orange 
    5591 
    5692# First install/upgrade distrubute. The setup.py scripts might 
     
    6399echo "Checkouting and building orange" 
    64100echo "===============================" 
    65 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange orange $REVISION $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
     101./bundle-inject-hg.sh https://bitbucket.org/biolab/orange orange $REVISION $REPOS_DIR ${APP} 
    66102 
    67103echo "Specifically building orangeqt" 
    68104echo "------------------------------" 
    69105 
    70 CUR_DIR=`pwd` 
    71 cd $REPOS_DIR/orange/source/orangeqt 
     106pushd $REPOS_DIR/orange/source/orangeqt 
    72107echo "Fixing sip/pyqt configuration" 
    73108 
    74 APP=${TMP_BUNDLE_DIR}/Orange.app 
    75 APP_ESCAPED=`echo ${TMP_BUNDLE_DIR}/Orange.app | sed s/'\/'/'\\\\\/'/g` 
    76 sed -i.bak "s/Users.*Orange.app/$APP_ESCAPED/g"  $APP/Contents/Frameworks/Python.framework/Versions/$PY_VER/lib/python$PY_VER/site-packages/PyQt4/pyqtconfig.py 
    77 sed -i.bak "s/Users.*Orange.app/$APP_ESCAPED/g"  $APP/Contents/Frameworks/Python.framework/Versions/$PY_VER/lib/python$PY_VER/site-packages/sipconfig.py 
     109sed -i.bak "s@/Users/.*/Orange.app/@$APP/@g" ${SITE_PACKAGES}/PyQt4/pyqtconfig.py 
     110sed -i.bak "s@/Users/.*/Orange.app/@$APP/@g" ${SITE_PACKAGES}/sipconfig.py 
    78111export PATH=$APP/Contents/Resources/Qt4/bin:$PATH 
    79112$PYTHON setup.py install 
    80 cd $CUR_DIR 
     113 
     114popd 
    81115 
    82116echo "Fixing Qt plugins search path" 
     
    84118Plugins = ../../../../../Resources/Qt4/plugins/" > $APP/Contents/Frameworks/Python.framework/Resources/Python.app/Contents/Resources/qt.conf 
    85119 
     120echo "[Paths] 
     121Plugins = Resources/Qt4/plugins/" > $APP/Contents/Resources/qt.conf 
     122 
    86123 
    87124echo "Checkouting and building bioinformatics addon" 
    88125echo "=============================================" 
    89 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-bioinformatics bioinformatics tip $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
     126./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-bioinformatics bioinformatics tip $REPOS_DIR ${APP} 
    90127 
    91128echo "Checkouting and building text addon" 
    92129echo "===================================" 
    93 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-text text tip $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
     130./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-text text tip $REPOS_DIR ${APP} 
    94131 
    95132echo "Installing networkx" 
    96133echo "+++++++++++++++++++" 
    97 ./bundle-inject-pypi.sh networkx-1.6 http://pypi.python.org/packages/source/n/networkx/networkx-1.6.tar.gz $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
     134./bundle-inject-pypi.sh networkx-1.6 http://pypi.python.org/packages/source/n/networkx/networkx-1.6.tar.gz $REPOS_DIR ${APP} 
    98135 
    99136echo "Installing suds library" 
    100137echo "+++++++++++++++++++++++" 
    101 ./bundle-inject-pypi.sh suds-0.4 http://pypi.python.org/packages/source/s/suds/suds-0.4.tar.gz $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
     138./bundle-inject-pypi.sh suds-0.4 http://pypi.python.org/packages/source/s/suds/suds-0.4.tar.gz $REPOS_DIR ${APP} 
    102139 
    103140echo "Instaling slumber library" 
     
    108145find $TMP_BUNDLE_DIR \( -name '*~' -or -name '*.bak' -or -name '*.pyc' -or -name '*.pyo' -or -name '*.pyd' \) -exec rm -rf {} ';' 
    109146 
    110 ln -s ../Frameworks/Python.framework/Versions/Current/lib/python${PY_VER}/site-packages/Orange ${TMP_BUNDLE_DIR}/Orange.app/Contents/Resources/Orange 
     147ln -s ../Frameworks/Python.framework/Versions/Current/lib/python${PY_VER}/site-packages/Orange ${APP}/Contents/Resources/Orange 
    111148 
    112149     
  • setup.py

    r11555 r11580  
    784784        include_package_data = True, 
    785785        zip_safe = False, 
    786  
     786        test_suite = 'Orange.testing.unit.tests.test_suite', 
    787787        cmdclass = cmdclass, 
    788788        ext_modules = ext_modules, 
  • source/orange/discretize.cpp

    r7665 r11583  
    726726      break; 
    727727 
    728       float entro1 = S1dist.abs*float(getEntropy(S1dist))/N; 
    729       float entro2 = S2dist.abs*float(getEntropy(S2dist))/N; 
    730       float E = entro1+entro2; 
     728    float entro1 = S1dist.abs*float(getEntropy(S1dist))/N; 
     729    float entro2 = S2dist.abs*float(getEntropy(S2dist))/N; 
     730    float E = entro1+entro2; 
    731731    if (   (!wins || (E<bestE)) && ((wins=1)==1) 
    732732        || (E==bestE) && rgen.randbool(++wins)) { 
Note: See TracChangeset for help on using the changeset viewer.