Ignore:
Files:
5 added
7 deleted
42 edited

Legend:

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

    r11598 r11557  
    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  
    409401        self.save_action = \ 
    410402            QAction(self.tr("Save"), self, 
     
    584576        file_menu.addAction(self.new_action) 
    585577        file_menu.addAction(self.open_action) 
    586         file_menu.addAction(self.open_and_freeze_action) 
    587578        file_menu.addAction(self.reload_last_action) 
    588579 
     
    890881        else: 
    891882            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 
    910883 
    911884    def open_scheme_file(self, filename): 
  • Orange/OrangeCanvas/main.py

    r11596 r11535  
    181181                log.info("%r style sheet not found.", stylesheet) 
    182182 
     183    if stylesheet_string is not None: 
     184        app.setStyleSheet(stylesheet_string) 
     185 
    183186    # Add the default canvas_icons search path 
    184187    dirpath = os.path.abspath(os.path.dirname(OrangeCanvas.__file__)) 
     
    186189 
    187190    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

    r11592 r11548  
    44# A General Orange Widget, from which all the Orange Widgets are derived 
    55# 
    6 import warnings 
    7  
    86from Orange.utils import environ 
    97from Orange.orng.orngEnviron import directoryNames as old_directory_names 
     
    206204        self.__wasShown = False 
    207205        self.__progressBarValue = -1 
    208         self.__progressState = 0 
    209206 
    210207    # uncomment this when you need to see which events occured 
     
    732729        if self.progressBarHandler: 
    733730            self.progressBarHandler(self, 0) 
    734  
    735         if self.__progressState != 1: 
    736             self.__progressState = 1 
    737             self.processingStateChanged.emit(1) 
     731        self.processingStateChanged.emit(1) 
    738732 
    739733    def progressBarSet(self, value, processEventsFlags=QEventLoop.AllEvents): 
     
    746740        old = self.__progressBarValue 
    747741        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  
    755742            self.__progressBarValue = value 
    756743            usedTime = max(1, time.time() - self.startTime) 
     
    793780        if self.progressBarHandler: 
    794781            self.progressBarHandler(self, 101) 
    795  
    796         if self.__progressState != 0: 
    797             self.__progressState = 0 
    798             self.processingStateChanged.emit(0) 
     782        self.processingStateChanged.emit(0) 
    799783 
    800784    # 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

    r11586 r11576  
    11import Orange 
    22 
    3 from Orange.core import Preprocessor_discretize 
     3from Orange.core import\ 
     4    EquiNDiscretization as EqualFreq,\ 
     5    EquiDistDiscretization as EqualWidth,\ 
     6    EntropyDiscretization as Entropy,\ 
     7    BiModalDiscretization as BiModal,\ 
     8    Preprocessor_discretize 
     9 
    410 
    511class DiscretizeTable(object): 
     
    2531    """ 
    2632    def __new__(cls, data=None, features=None, discretize_class=False, 
    27                 method=Orange.feature.discretization.EqualFreq(n=3), clean=True): 
     33                method=EqualFreq(n=3), clean=True): 
    2834        if data is None: 
    2935            self = object.__new__(cls) 
     
    3541 
    3642    def __init__(self, features=None, discretize_class=False, 
    37                  method=Orange.feature.discretization.EqualFreq(n=3), clean=True): 
     43                 method=EqualFreq(n=3), clean=True): 
    3844        self.features = features 
    3945        self.discretize_class = discretize_class 
  • Orange/fixes/fix_changed_names.py

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

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

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

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

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

    r11591 r10655  
    77import subprocess 
    88 
    9 import Orange 
    109from Orange.utils import environ 
    1110from Orange.testing import testing 
     
    1312 
    1413class 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) 
    2518 
    2619    def setUp(self): 
    27         sys.path.append(self.orange_dir) 
    28  
    29     def tearDown(self): 
    30         del sys.path[-1] 
     20        pass 
    3121 
    3222    def test_regression_on(self, roottest, indir, outdir, name): 
     
    4838        stdout, stderr = p.communicate() 
    4939        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  
    5840        self.assertEqual(rv, "ok", "Regression test %s: %s" % (rv, name) \ 
    5941                        if stderr == "" else \ 
    6042                        "Regression test %s: %s\n\n%s" % (rv, name, stderr)) 
    6143        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  
    8344 
    8445root = os.path.normpath(os.path.join(environ.install_dir, "..")) 
  • Orange/utils/addons.py

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

    r11589 r10845  
    1919in that file for instructions on how to do that). 
    2020 
    21 Running tests 
    22 ------------- 
    23 After Orange is installed, you can check if everything is working OK by running the included tests:: 
    24  
    25     python setup.py test 
    26  
    27 This 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  
    2921Starting Orange Canvas 
    3022---------------------- 
  • docs/development/rst/c.rst

    r11587 r11577  
    33################################ 
    44 
    5 This page gives an introduction to extending Orange in C++ with emphasis on 
    6 how to define interfaces to Python. Besides reading this page, we recommend 
    7 studying some of existing extension modules like orangeom, and the Orange's 
    8 interface itself. 
    9  
    10 We shall first present a general picture and then focus on specific parts of the 
    11 interface. 
    12  
    13 Instead of general tools for creating interfaces between C++ and Python 
    14 (Swig, Sip, PyBoost...), Orange uses its own specific set of tools. 
    15  
    16 To expose a C++ object to Python, we need to mark them as exportable, select a 
    17 general constructor template to use or program a specific one, we have to mark 
    18 the attributes to be exported, and provide the interfaces for C++ member 
    19 functions. When we give the access to mostly C++ code as it is, the interface 
    20 functions have only a few lines. When we want to make the exported function more 
    21 friendly, eg. allow various types of arguments or fitting the default arguments 
    22 according to the given ones, these functions are longer. 
    23  
    24 To define a non-member function, we write the function itself as described in 
    25 the Python's manual (see the first chapter of "Extending and Embedding the 
    26 Python Interpreter") and then mark it with a specific keyword. 
    27 Pyxtract will recognize the keyword and add it to the list of exported functions. 
    28  
    29 To define a special method, one needs to provide a function with the appropriate 
    30 name constructed from the class name and the special method's name, which is the 
    31 same as in Python's PyTypeObjects. 
    32  
    33 For instance, the elements of ``ExampleTable`` (examples) can be accessed 
    34 through indexing because we defined a C function that gets an index (and the 
    35 table, of course) and returns the corresponding example. Here is the function 
    36 (with error detection removed for the sake of clarity). :: 
     5This 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 
     7This 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*************************************** 
     10A simple example: Exporting function(s) 
     11*************************************** 
     12 
     13If 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 
     15We 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 
     17We 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 
     19The function should be declared like this:: 
     20 
     21    PyObject *triangulate(PyObject *, PyObject *args, PyObject *) 
     22        PYARGS(METH_VARARGS, "(examples[, attr1, attr2, nEdgeTypes]) -> Graph") 
     23 
     24This 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 
     26We 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 
     34Now 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 
     49For 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 
     51We 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 
     53We 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 
     55For 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 
     74We 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 
     92The ``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 
     101There'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 
     103We 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 
     105What 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 
     109Do yourself a favour and put this line into a batch file. See the ``_pyxtract.bat`` files in various Orange's directories. 
     110 
     111Option ``-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 
     113What 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 
     133Functions ``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 
     135So, 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 
     154We 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 
     156Setting 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 
     159Python is installed. This will simplify the options and also help 
     160you upgrade to a newer version. (If you don't want to do this, 
     161just replace the below reference ``$(PYTHON)`` with ``c:\python23``.) 
     162 
     163* Open the Orange workspace (sources/orange.dsw) and add your stuff as new projects. 
     164Add new project into workspace". You need a "Win32 Dynamic-link 
     165Library"; create it as an empty or simple project. 
     166This document will suppose you've put it into a subdirectory 
     167of ``orange/source`` (eg ``orange/source/myproject``) 
     168 
     169* Edit the project settings. Make sure to edit the settings for 
     170both Release and Debug version - or for Release, in the unlikely case that you won't 
     171need 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################################################################## 
     194General mechanism and tools used in Orange's C to Python interface 
     195################################################################## 
     196 
     197This 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 
     199If 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################################ 
     202Orange's C++ to Python interface 
     203################################ 
     204 
     205Instead 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 
     207In 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 
     209To 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 
     211Orange'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 
     213The 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 
     215We 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). :: 
    37216 
    38217    PyObject *ExampleTable_getitem_sq(PyObject *self, int idx) 
     
    42221    } 
    43222 
    44 Also, ``ExampleTable`` has a non-special method ``sort([list-of-attributes])``. 
    45 This is implemented through a C function that gets a list of attributes and 
    46 calls the C++ class' method 
    47 ``TExampleTable::sort(const vector<int> order)``. To illustrate, this is a 
    48 slightly simplified function (we've removed some flexibility regarding the 
    49 parameters and the exception handling). :: 
     223Also, ``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). :: 
    50224 
    51225    PyObject *ExampleTable_sort(PyObject *self, PyObject *args) PYARGS(METH_VARARGS, "() -> None") 
     
    61235        varListFromDomain(PyTuple_GET_ITEM(args, 0), table->domain, attributes, true, true); 
    62236        vector<int> order; 
    63         for(TVarList::reverse_iterator vi(attributes.rbegin()), ve(attributes.rend()); vi!=ve; vi++) { 
     237        for(TVarList::reverse_iterator vi(attributes.rbegin()), ve(attributes.rend()); vi!=ve; vi++) 
    64238            order.push_back(table->domain->getVarNum(*vi)); 
    65         } 
     239 
    66240        table->sort(order); 
    67241        RETURN_NONE; 
    68242    } 
    69243 
    70 The function casts the ``PyObject *`` into the 
    71 corresponding C++ object, reads the arguments, calls the C++ 
    72 functions and returns the result (``None``, in this case). 
    73  
    74 Interfacing with Python requires a lot of manual work, but this gives a 
    75 programmer the opportunity to provide a function which accepts many different 
    76 forms of arguments. The above function, for instance, accepts a list in 
    77 which attributes are specified by indices, names or descriptors, all 
    78 corresponding to the ``ExampleTable`` which is being sorted. Inheritance of 
    79 methods, on the other hand, ensures that only the methods that are truly 
    80 specific for a class need to be coded. 
    81  
    82 The part of the interface that is built automatically is taken care of by 
    83 two scripts. ``pyprops`` parses all Orange's header files and extracts all 
    84 the class built-in properties. The second is ``pyxtract``, which goes 
    85 through the C++ files that contain the interface functions such as those above. 
    86 It recognizes the functions that implement special or member methods and 
    87 constructs the corresponding ``PyTypeObject``s. 
     244Basically, 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 
     246There 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 
     249The 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 
     251The 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. 
    88252 
    89253******* 
     
    91255******* 
    92256 
    93 Pyprops scans each hpp file for classes we want to export to Python). Properties 
    94 can be ``bool``, ``int``, ``float``, ``string``, ``TValue`` or a wrapped Orange 
    95 type. 
    96  
    97 Class definition needs to look as follows. :: 
     257Pyprops 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 
     259Pyprops' parser is essentially a trivial finite automaton. Class definition needs to look as follows. :: 
    98260 
    99261    class [ORANGE_API] <classname>; [: public <parentclass> ] 
    100262 
    101 This should be in a single line. To mark the class for export, this should be 
    102 followed by ``__REGISTER_CLASS`` or ``__REGISTER_ABSTRACT_CLASS`` before any 
    103 properties or components are defined. The difference between the two, as far as 
    104 pyprops is concerned, is that abstract classes do not define the ``clone`` 
    105 method. 
     263This 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. 
    106264 
    107265To export a property, it should be defined like this. :: 
     
    109267    <type> <name> //P[R|O] [>|+<alias>] <description> 
    110268 
    111 Pyprops doesn't check the type and won't object if you use other types than 
    112 those listed above. The error will be discovered later, during linking. ``//P`` 
    113 signals that we want to export the property. If followed by ``R`` or ``O``, the 
    114 property is read-only or obsolete. The property can also have an alias name; 
    115 ``>`` renames it and ``+`` adds an alias. 
     269Pyprops 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. 
    116270 
    117271Each property needs to be declared in a separate line, e.g. :: 
     
    120274    int y; //P; 
    121275 
    122 If we don't want to export a certain property, we omit the ``//P`` mark. An 
    123 exception 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 
    125 should still know about them because for the purpose of garbage collection. You 
    126 should mark them by ``//C`` so that they are put into the list of objects that 
    127 need to be counted. Failing to do so would cause a memory leak. 
    128  
    129 If a class directly or indirectly holds references to any wrapped objects that 
    130 are neither properties nor components, it needs to declare ``traverse`` and 
    131 ``clear`` as described in Python documentation. 
    132  
    133 Pyprops creates a ppp file for each hpp, which includes the extracted 
    134 information in form of C++ structures that compile into the interface. 
    135 The ppp file needs to be included in the corresponding cpp file. For 
    136 instance, domain.ppp is included in domain.cpp. 
     276If 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 
     278If 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 
     280Pyprops 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 
     282Then 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 
     284ppp 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. 
    137285 
    138286******** 
     
    140288******** 
    141289 
    142 Pyxtract's job is to detect the functions that define special methods (such as 
    143 printing, conversion, sequence and arithmetic related operations...) and member 
    144 functions. Based on what it finds for each specific class, it constructs the 
    145 corresponding ``PyTypeObject``s. For the functions to be recognized, they must 
    146 follow a specific syntax. 
    147  
    148 There are two basic mechanisms for marking the functions to export. Special 
    149 functions 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, 
    152 inheritance relations, constants etc. are marked by macros such as ``PYARGS`` 
    153 in the above definition of ``ExampleTable_sort``. Most of these macros don't do 
    154 anything except for marking stuff for pyxtract. 
     290Pyxtract'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 
     292There 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. 
    155293 
    156294Class declaration 
    157295================= 
    158296 
    159 Each class needs to be declared as exportable. If it's a base class, pyxtract 
    160 needs to know the data structure for the instances of this class. As for all 
    161 Python objects the structure must be "derived" from ``PyObject`` (Python is 
    162 written in C, so the subclasses are not derived in the C++ sense but extend the 
    163 C structure instead). Most objects are derived from Orange; the only exceptions 
    164 are ``orange.Example``, ``orange.Value`` and ``orange.DomainDepot``. 
    165  
    166 Pyxtract should also know how the class is constructed - it can have a specific 
    167 constructor, one of the general constructors or no constructor at all. 
    168  
    169 The class is declared in one of the following ways (here are some examples from 
    170 actual Orange code). 
     297Each 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 
     299Pyxtract 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 
     302The class is declared in one of the following ways. 
    171303 
    172304``BASED_ON(EFMDataDescription, Orange)`` 
     
    174306 
    175307``ABSTRACT(ClassifierFD, Classifier)`` 
    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  
    178 Abstract C++ classes are not necessarily defined as ``ABSTRACT`` in the Python 
    179 interface. For example, ``TClassifier`` is an abstract C++ class, but you can 
    180 seemingly construct an instance of ``Classifier`` in Python. What happens is 
    181 that there is an additional C++ class ``TClassifierPython``, which poses as 
    182 Python's class ``Classifier``. So the Python class ``Classifier`` is not defined 
    183 as ``ABSTRACT`` or ``BASED_ON`` but using the ``Classifier_new`` function, as 
    184 described below. 
     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 
     310Abstract 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. 
    185311 
    186312 
    187313``C_NAMED(EnumVariable, Variable, "([name=, values=, autoValues=, distributed=, getValueFrom=])")`` 
    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. 
     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. 
    189315 
    190316``C_UNNAMED(RandomGenerator, Orange, "() -> RandomGenerator")`` 
     
    192318 
    193319``C_CALL(BayesLearner, Learner, "([examples], [weight=, estimate=] -/-> Classifier")`` 
    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``. 
     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``.`` 
    195321 
    196322``C_CALL3(MakeRandomIndices2, MakeRandomIndices2, MakeRandomIndices, "[n | gen [, p0]], [p0=, stratified=, randseed=] -/-> [int]")`` 
     
    203329    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. 
    204330 
    205 Even if the class is defined by ``DATASTRUCTURE``, you can still specify a 
    206 different constructor, most probably the last form of it (the ``_new`` 
    207 function). In this case, specify a keyword ``ROOT`` as a parent and pyxtract 
    208 will understand that this is the base class. 
    209  
    210 Object construction in Python is divided between two methods. The constructors 
    211 we discussed above construct the essential part of the object - they allocate 
    212 the necessary memory and initialize the fields far enough that the object is 
    213 valid 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 
    216 for ``init`` is to set any attributes that user gave as the keyword arguments to 
    217 the constructor. 
    218  
    219 For instance, Python's statement 
    220 ``orange.EnumVariable("a", values=["a", "b", "c"])`` is executed so that ``new`` 
    221 constructs the variable and gives it the name, while ``init`` sets the 
    222 ``values`` field. 
    223  
    224 The ``new`` operator can also accept keyword arguments. For 
    225 instance, when constructing an ``ExampleTable`` by reading the data from a file, 
    226 you can specify a domain (using keyword argument ``domain``), a list of 
    227 attributes to reuse if possible (``use``), you can tell it not to reuse the 
    228 stored domain or not to store the newly constructed domain (``dontCheckStored``, 
    229 ``dontStore``). After the ``ExampleTable`` is constructed, ``init`` is called to 
    230 set the attributes. To tell it to ignore the keyword arguments that the 
    231 constructor might (or had) used, we write the following. :: 
     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 
     349Function 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 
     351Even 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 
     353Object 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 
     355For 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 
     357The ``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. :: 
    232358 
    233359    CONSTRUCTOR_KEYWORDS(ExampleTable, "domain use useMetas dontCheckStored dontStore filterMetas") 
    234360 
    235 There's another macro related to attributes. Let ``ba`` be an orange object, say 
    236 an instance of ``orange.BayesLearner``. If you assign new attributes as usual 
    237 directly, eg. ``ba.myAttribute = 12``, you will get a warning (you should use 
    238 the object's method ``setattr(name, value)`` to avoid it). Some objects have 
    239 some attributes that cannot be implemented in C++ code, yet they are usual and 
    240 useful. For instance, ``Graph`` can use attributes ``objects``, ``forceMapping`` 
    241 and ``returnIndices``, which can only be set from Python (if you take a look at 
    242 the documentation on ``Graph`` you will see why these cannot be implemented in 
    243 C++). Yet, since user are allowed to set these attributes and will do so often, 
    244 we don't want to give warnings. We achieve this by :: 
     361``init`` will ignore all the keywords from the list. 
     362 
     363Talking 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 :: 
    245364 
    246365    RECOGNIZED_ATTRIBUTES(Graph, "objects forceMapping returnIndices") 
     366 
     367How 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. 
    247368 
    248369 
     
    250371=============== 
    251372 
    252 Special methods act as the class built-in methods. They define what the type can 
    253 do: if it, for instance, supports multiplication, it should define the operator 
    254 that gets the object itself and another object and return the product (or throw 
    255 an exception). If it allows for indexing, it defines an operator that gets the 
    256 object itself and the index, and returns the element. These operators are 
    257 low-level; most can be called from Python scripts but they are also internally 
    258 by Python. For instance, if ``table`` is an ``ExampleTable``, then 
    259 ``for e in table:`` or ``reduce(f, table)`` will both work by calling the 
    260 indexing operator for each table's element. 
    261 For more details, consider the Python manual, chapter "Extending and 
    262 Embedding the Python Interpreter" section "Defining New Types". 
    263  
    264 To 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 
    267 single line. Regarding the arguments and the result, it should conform to 
    268 Python's specifications. Pyxtract will detect the methods and set the pointers 
    269 in ``PyTypeObject`` correspondingly. 
    270  
    271 Here is a list of methods: the left column represents a method name that 
    272 triggers pyxtract (these names generally correspond to special method names of 
    273 Python classes as a programmer in Python sees them) and the second is the 
    274 name of the field in ``PyTypeObject`` or subjugated structures. See Python 
    275 documentation for description of functions' arguments and results. Not all 
    276 methods can be directly defined; for those that can't, it is because we either 
    277 use an alternative method (eg. ``setattro`` instead of ``setattr``) or pyxtract 
    278 gets or computes the data for this field in some other way. 
     373Special 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 
     375We 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 
     377To 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 
     379Here'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? 
    279380 
    280381General methods 
     
    289390|              |                       | has been taken care of. If you have a brand new object,   | 
    290391|              |                       | 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?!)                 | 
    291395+--------------+-----------------------+-----------------------------------------------------------+ 
    292396| ``.``        | ``tp_getattr``        | Can't be redefined since we use ``tp_getattro`` instead.  | 
     
    430534+-------------+----------------------+ 
    431535 
    432 For example, here is what gets called when you want to know the length of an 
    433 example table. :: 
     536For example, here's what gets called when you want to know the length of an example table. :: 
    434537 
    435538    int ExampleTable_len_sq(PyObject *self) 
     
    440543    } 
    441544 
    442 ``PyTRY`` and ``PyCATCH`` take care of C++ exceptions. ``SELF_AS`` is a macro 
    443 for casting, ie unwrapping the points (this is an alternative to ``CAST_TO``). 
     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). 
    444546 
    445547 
     
    447549==================================== 
    448550 
    449 Exporting of most of C++ class fields is already taken care by the lists that 
    450 are compiled by pyprops. There are only a few cases in the entire Orange where 
    451 we needed to manually write specific handlers for setting and getting the 
    452 attributes. This needs to be done if setting needs some special processing or 
    453 when simulating an attribute that does not exist in the underlying C++ class. 
    454  
    455 An example for this is class ``HierarchicalCluster``. It contains results of a 
    456 general, 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 
    458 binary, 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 
    460 the number of branches; if there are none, it returns ``None``, if there are 
    461 more than two, it complains, while otherwise it returns the first branch. :: 
     551Exporting 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 
     553An 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. :: 
    462554 
    463555    PyObject *HierarchicalCluster_get_left(PyObject *self) 
     
    478570    } 
    479571 
    480 As 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 
    482 function name needs to be ``<classname>_get_<attributename>``. 
    483 Setting an attribute is similar; function name should be 
    484 ``<classname>_set_<attributename>``, it should accept two Python 
    485 objects (the object and the attribute value) and return an ``int``, where 0 
    486 signifies success and -1 a failure. 
    487  
    488 If you define only one of the two handlers, you'll get a read-only or write-only 
    489 attribute. 
     572As 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 
     574If you define only one of the two handlers, you'll get a read-only or write-only attribute. 
    490575 
    491576 
     
    493578================ 
    494579 
    495 We have already shown an example of a member function - the ``ExampleTable``'s 
    496 method ``sort``. The general template is 
    497 ``PyObject *<classname>_<methodname>(<arguments>) PYARGS(<arguments-keyword>, <documentation-string>)``. 
    498 In the case of the ``ExampleTable``'s ``sort``, this looks like this. :: 
     580You'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. :: 
    499581 
    500582    PyObject *ExampleTable_sort(PyObject *self, PyObject *args) PYARGS(METH_VARARGS, "() -> None") 
    501583 
    502 Argument type can be any of the usual Python constants stating the number and 
    503 the kind of arguments, such as ``METH_VARARGS`` or ``METH_O`` - this constant 
    504 gets copied to the corresponding list (browse Python documentation for 
    505 ``PyMethodDef``). 
    506  
     584Argument 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 
     586If you want more examples, just search Orange's files for the keyword ``PYARGS``. 
    507587 
    508588Class constants 
    509589=============== 
    510590 
    511 Orange classes, as seen from Python, can also have constants, such as 
    512 ``orange.Classifier.GetBoth``. Classifier's ``GetBoth`` is visible as a member 
    513 of the class, the derived classes and all their instances (eg. 
    514 ``BayesClassifier.GetBoth`` and ``bayes.GetBoth``). 
    515  
    516 There are several ways to define such constants. If they are simple integers or 
    517 floats, you can use ``PYCLASSCONSTANT_INT`` or ``PYCLASSCONSTANT_FLOAT``, like 
    518 in :: 
     591Orange 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 
     593There are several ways to define such constants. If they are simple integers or floats, you can use ``PYCLASSCONSTANT_INT`` or ``PYCLASSCONSTANT_FLOAT``, like in :: 
    519594 
    520595    PYCLASSCONSTANT_INT(Classifier, GetBoth, 2) 
     
    524599    PYCLASSCONSTANT_INT(C45TreeNode, Leaf, TC45TreeNode::Leaf) 
    525600 
    526 Pyxtract will convert the given constant to a Python object (using 
    527 ``PyInt_FromLong`` or ``PyFloat_FromDouble>``). 
    528  
    529 When the constant is an object of some other type, use ``PYCLASSCONSTANT``. In 
    530 this form (not used in Orange so far), the third argument can be either an 
    531 instance of ``PyObject *`` or a function call. In either case, the object or 
    532 function must be known at the point where the pyxtract generated file is 
    533 included. 
     601Pyxtract will convert the given constant to a Python object (using ``PyInt_FromLong`` or ``PyFloat_FromDouble>``). 
     602 
     603When 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. 
    534604 
    535605 
     
    537607======== 
    538608 
    539 Pickling is taken care of automatically if the class provides a Python 
    540 constructor that can construct the object without arguments (it may *accept* 
    541 arguments, but should be able to do without them. If there is no such 
    542 constructor, the class should provide a ``__reduce__`` method or it should 
    543 explicitly declare that it cannot be pickled. If it doesn't pyxtract will issue 
    544 a warning that the class will not be picklable. 
     609Pickling 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. 
    545610 
    546611Here are the rules: 
    547612 
    548613* Classes that provide a ``__reduce__`` method (details follow below) are pickled through that method. 
    549  
    550614* 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  
    552615* 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 :: 
    553616 
    554617    PyObject *DefaultClassifier_new(PyTypeObject *tpe, PyObject *args) 
    555618        BASED_ON(Classifier, "([defaultVal])") ALLOWS_EMPTY 
    556  
    557 and is picklable through code ``Orange.__reduce__``. But again, see the warning 
    558 below. 
     619  and is picklable through code ``Orange.__reduce__``. But again, see the warning below. 
    559620 
    560621* 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. 
     
    562623 
    563624    NO_PICKLE(TabDelimExampleGenerator) 
    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  
    573 When the constructor is used, as in points 2 and 3, pickling will only work if 
    574 all fields of the C++ class can be set "manually" from Python, are set through 
    575 the constructor, or are set when assigning other fields. In other words, if 
    576 there are fields that are not 
    577 marked as ``//P`` for pyprops, you will most probably need to manually define 
    578 a ``__reduce__`` method, as in point 1. 
    579  
    580 The details of what the ``__reduce__`` method must do are described in the 
    581 Python documentation. In our circumstances, it can be implemented in two ways 
    582 which differ in what function is used for unpickling: it can either use the 
    583 class' constructor or we can define a special method for unpickling. 
    584  
    585 The former usually happens when the class has a read-only property (``//PR``), 
    586 which is set by the constructor. For instance, ``AssociationRule`` has read-only 
    587 fields ``left`` and ``right``, which are needs to be given to the constructor. 
    588 This is the ``__reduce__`` method for the class. :: 
     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 
     628When 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 
     630The 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 
     632The 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. :: 
    589633 
    590634    PyObject *AssociationRule__reduce__(PyObject *self) 
     
    599643    } 
    600644 
    601 As described in the Python documentation, the ``__reduce__`` should return a 
    602 tuple in which the first element is the function that will do the unpickling, 
    603 and the second argument are the arguments for that function. Our unpickling 
    604 function is simply the classes' type (calling a type corresponds to calling a 
    605 constructor) and the arguments for the constructor are the left- and right-hand 
    606 side of the rule. The third element of the tuple is classes' dictionary. 
    607  
    608 When unpickling is more complicated - usually when the class has no constructor 
    609 and contains fields of type ``float *`` or similar - we need a special 
    610 unpickling 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 
    613 the source code; note that the instance's true class need to be pickled, too. 
    614 Also, check how we use ``TCharBuffer`` throughout the code to store and pickle 
    615 binary data as Python strings. 
    616  
    617 Be careful when manually writing the unpickler: if a C++ class derived from that 
    618 class inherits its ``__reduce__``, the corresponding unpickler will construct an 
    619 instance of a wrong class (unless the unpickler functions through Python's 
    620 constructor, ``ob_type->tp_new``). Hence, classes derived from a class which 
    621 defines an unpickler have to define their own ``__reduce__``, too. 
     645As 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 
     647When 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 
     649Be 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. 
    622650 
    623651Non-member functions and constants 
    624652================================== 
    625653 
    626 Non-member functions are defined in the same way as member functions except 
    627 that their names do not start with the class name. Here is how the ``newmetaid`` 
    628 is implemented :: 
     654Most 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. :: 
    629655 
    630656    PyObject *newmetaid(PyObject *, PyObject *) PYARGS(0,"() -> int") 
     
    635661    } 
    636662 
    637 Orange also defines some non-member constants. These are defined in a similar 
    638 fashion as the class constants. 
    639 ``PYCONSTANT_INT(<constant-name>, <integer>)`` defines an integer 
    640 constant and ``PYCONSTANT_FLOAT`` would be used for a continuous one. 
    641 ``PYCONSTANT`` is used for objects of other types, as the below example that 
    642 defines an (obsolete) constant ``MeasureAttribute_splitGain`` shows. :: 
     663Orange 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. :: 
    643664 
    644665    PYCONSTANT(MeasureAttribute_splitGain, (PyObject *)&PyOrMeasureAttribute_gainRatio_Type) 
    645666 
    646 Class constants from the previous section are put in a pyxtract generated file 
    647 that is included at the end of the file in which the constant definitions and 
    648 the corresponding classes are. Global constant modules are included in another 
    649 file, far away from their actual definitions. For this reason, ``PYCONSTANT`` 
    650 cannot refer to any functions (the above example is an exception - all class 
    651 types are declared in this same file and are thus available at the moment the 
    652 above code is used). Therefore, if the constant is defined by a function call, 
    653 you need to use another keyword, ``PYCONSTANTFUNC``:: 
     667Class 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``:: 
    654668 
    655669    PYCONSTANTFUNC(globalRandom, stdRandomGenerator) 
    656670 
    657 Pyxtract 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 
    660 your code, like this:: 
     671Pyxtract 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:: 
    661672 
    662673    PyObject *stdRandomGenerator() 
     
    665676    } 
    666677 
    667 Another example are ``VarTypes``. ``VarTypes`` is a tiny module inside Orange 
    668 that contains nothing but five constants, representing various attribute types. 
    669 From pyxtract perspective, ``VarTypes`` is a constant. This is the complete 
    670 definition. :: 
     678Another 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. :: 
    671679 
    672680    PyObject *VarTypes() 
     
    683691    PYCONSTANTFUNC(VarTypes, VarTypes) 
    684692 
    685 If you want to understand the constants completely, check the Orange's pyxtract 
    686 generated file initialization.px. 
     693If you want to understand the constants completely, check the Orange's pyxtract generated file initialization.px. 
    687694 
    688695How does it all fit together 
    689696============================ 
    690697 
    691 We will finish the section with a description of the files generated by the two 
    692 scripts. Understanding these may be needed for debugging purposes. 
     698This 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. 
    693699 
    694700File specific px files 
    695701---------------------- 
    696702 
    697 For each compiled cpp file, pyxtract creates a px file with the same name. The 
    698 file starts with externs declaring the base classes for the classes whose types 
    699 are 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. 
     703For 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 
     705Then 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. 
    708710 
    709711* Definitions of doc strings for call operator and constructor. 
    710712 
    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``?) :: 
     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``?) :: 
    723716 
    724717    POrange FloatVariable_default_constructor(PyTypeObject *type) 
     
    726719        return POrange(mlnew TFloatVariable(), type); 
    727720    } 
    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. 
     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. 
    733722 
    734723* Aliases. A list of renamed attributes. 
    735724 
    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). 
     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). 
    760730 
    761731 
     
    765735Initialization.px defines the global module stuff. 
    766736 
    767 First, here is a list of all ``TOrangeTypes``. The list is used for checking 
    768 whether some Python object is of Orange's type or derived from one, for finding 
    769 a Python class corresponding to a C++ class (based on C++'s RTTI). Orange also 
    770 exports the list as ``orange._orangeClasses``; this is a ``PyCObject`` so it can 
    771 only be used by other Python extensions written in C. 
    772  
    773 Then come declarations of all non-member functions, followed by a 
    774 ``PyMethodDef`` structure with them. 
    775  
    776 Finally, here are declarations of functions that return manually constructed 
    777 constants (eg ``VarTypes``) and declarations of functions that add class 
    778 constants (eg ``Classifier_addConstants``). The latter functions were generated 
    779 by pyxtract and reside in the individual px files. Then follows a function that 
    780 calls all the constant related functions declared above. This function also adds 
    781 all class types to the Orange module. 
     737First, 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 
     739Then come declarations of all non-member functions, followed by a ``PyMethodDef`` structure with them. 
     740 
     741Finally, 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``? 
    782742 
    783743The main module now only needs to call ``addConstants``. 
     
    794754    #define PyOrange_AsDomain(op) (GCPtr< TDomain >(PyOrange_AS_Orange(op))) 
    795755 
    796 ************************** 
    797 What and where to include? 
    798 ************************** 
    799  
    800 As already mentioned, ppp files should be included (at the beginning) of the 
    801 corresponding cpp files, instead of the hpp file. For instance, domain.ppp is 
    802 included in domain.cpp. Each ppp should be compiled only once, all other files 
    803 needing the definition of ``TDomain`` should still include domain.hpp as usual. 
    804  
    805 File-specific px files are included in the corresponding cpp files. 
    806 lib_kernel.px is included at the end of lib_kernel.cpp, from which it was 
    807 generated. initialization.px should preferably be included in the file that 
    808 initializes the module (function ``initorange`` needs to call ``addConstants``, 
    809 which is declared in initialization.px. These px files contain definitions and 
    810 must be compiled only once. externs.px contains declarations and can be included 
    811 wherever needed. 
    812  
    813 For Microsoft Visual Studio, create a new, blank workspace. Specify the 
    814 directory with orange sources as "Location". Add a new project of type "Win 32 
    815 Dynamic-Link Library"; change the 
    816 location back to d:\ai\orange\source. Make it an empty DLL project. 
    817  
    818 Whatever names you give your module, make sure that the .cpp and .hpp files you 
    819 create as you go on are in orange\source\something (replace "something" with 
    820 something), since the further instructions will suppose it. 
     756***************** 
     757Where to include? 
     758***************** 
     759 
     760As 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 
     762File-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 
     764Some 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 
     766Create 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 
     768Whatever 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. 
  • docs/extend-widgets/rst/OWAttributeSampler.py

    r11593 r11085  
    55<priority>1020</priority> 
    66""" 
    7 import Orange 
    87 
    98from OWWidget import * 
     
    1211class OWAttributeSampler(OWWidget): 
    1312    settingsList = [] 
    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~ 
     13    contextHandlers = {"": DomainContextHandler("", [ 
     14            ContextField("classAttribute", DomainContextHandler.Required), 
     15            ContextField("attributeList", DomainContextHandler.List + DomainContextHandler.SelectedRequired, 
     16                         selected="selectedAttributes")])} 
    2517 
    2618    def __init__(self, parent=None, signalManager=None): 
    2719        OWWidget.__init__(self, parent, signalManager, 'AttributeSampler') 
    2820 
    29         self.inputs = [("Examples", Orange.data.Table, self.dataset)] 
    30         self.outputs = [("Examples", Orange.data.Table)] 
     21        self.inputs = [("Examples", ExampleTable, self.dataset)] 
     22        self.outputs = [("Examples", ExampleTable)] 
    3123 
    3224        self.icons = self.createAttributeIconDict() 
     
    3729        self.loadSettings() 
    3830 
    39         OWGUI.listBox(self.controlArea, self, "selectedAttributes", 
    40                       "attributeList", 
    41                       box="Selected attributes", 
    42                       selectionMode=QListWidget.ExtendedSelection) 
    43  
     31        OWGUI.listBox(self.controlArea, self, "selectedAttributes", "attributeList", box="Selected attributes", selectionMode = QListWidget.ExtendedSelection) 
    4432        OWGUI.separator(self.controlArea) 
    45         self.classAttrCombo = OWGUI.comboBox( 
    46             self.controlArea, self, "classAttribute", 
    47             box="Class attribute") 
    48  
     33        self.classAttrCombo = OWGUI.comboBox(self.controlArea, self, "classAttribute", box="Class attribute") 
    4934        OWGUI.separator(self.controlArea) 
    50         OWGUI.button(self.controlArea, self, "Commit", 
    51                      callback=self.outputData) 
     35        OWGUI.button(self.controlArea, self, "Commit", callback = self.outputData) 
    5236 
    5337        self.resize(150,400) 
     
    5943        self.classAttrCombo.clear() 
    6044        if data: 
    61             self.attributeList = [(attr.name, attr.varType) 
    62                                   for attr in data.domain] 
     45            self.attributeList = [(attr.name, attr.varType) for attr in data.domain] 
    6346            self.selectedAttributes = [] 
    6447            for attrName, attrType in self.attributeList: 
     
    8063            self.send("Examples", None) 
    8164        else: 
    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) 
     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) 
    8767            self.send("Examples", newData) 
    8868 
     69 
     70############################################################################## 
     71# Test the widget, run from prompt 
    8972 
    9073if __name__=="__main__": 
     
    9376    ow.show() 
    9477 
    95     data = Orange.data.Table('iris.tab') 
     78    data = orange.ExampleTable('iris.tab') 
    9679    ow.dataset(data) 
    9780 
  • docs/extend-widgets/rst/OWDataSamplerB.py

    r11593 r11085  
    22<name>Data Sampler (B)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerB.svg</icon> 
     4<icon>icons/DataSamplerB.png</icon> 
    55<priority>20</priority> 
    66""" 
    7 import Orange 
    87from OWWidget import * 
    98import OWGUI 
     
    1211    settingsList = ['proportion', 'commitOnChange'] 
    1312    def __init__(self, parent=None, signalManager=None): 
    14         OWWidget.__init__(self, parent, signalManager) 
     13        OWWidget.__init__(self, parent, signalManager, 'SampleDataB') 
    1514 
    16         self.inputs = [("Data", Orange.data.Table, self.data)] 
    17         self.outputs = [("Sampled Data", Orange.data.Table)] 
     15        self.inputs = [("Data", ExampleTable, self.data)] 
     16        self.outputs = [("Sampled Data", ExampleTable)] 
    1817 
    1918        self.proportion = 50 
     
    5049 
    5150    def selection(self): 
    52         indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
     51        indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
    5352        ind = indices(self.dataset) 
    5453        self.sample = self.dataset.select(ind, 0) 
     
    6261            self.commit() 
    6362 
     63############################################################################## 
     64# Test the widget, run from prompt 
    6465 
    6566if __name__=="__main__": 
     
    6768    ow = OWDataSamplerB() 
    6869    ow.show() 
    69     dataset = Orange.data.Table('iris.tab') 
     70    dataset = orange.ExampleTable('iris.tab') 
    7071    ow.data(dataset) 
    7172    appl.exec_() 
  • docs/extend-widgets/rst/OWDataSamplerC.py

    r11593 r11085  
    22<name>Data Sampler (C)</name> 
    33<description>Randomly selects a subset of instances from the data set</description> 
    4 <icon>icons/DataSamplerC.svg</icon> 
     4<icon>icons/DataSamplerC.png</icon> 
    55<priority>30</priority> 
    66""" 
    7 import Orange 
    8  
    97from OWWidget import * 
    108import OWGUI 
     
    1513        OWWidget.__init__(self, parent, signalManager, 'SampleDataC') 
    1614         
    17         self.inputs = [("Data", Orange.data.Table, self.data)] 
    18         self.outputs = [("Sampled Data", Orange.data.Table), 
    19                         ("Other Data", Orange.data.Table)] 
     15        self.inputs = [("Data", ExampleTable, self.data)] 
     16        self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
    2017 
    2118        self.proportion = 50 
     
    5249 
    5350    def selection(self): 
    54         indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
     51        indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
    5552        ind = indices(self.dataset) 
    5653        self.sample = self.dataset.select(ind, 0) 
     
    6663            self.commit() 
    6764 
     65############################################################################## 
     66# Test the widget, run from prompt 
    6867 
    6968if __name__=="__main__": 
     
    7170    ow = OWDataSamplerC() 
    7271    ow.show() 
    73     dataset = Orange.data.Table('iris.tab') 
     72    dataset = orange.ExampleTable('iris.tab') 
    7473    ow.data(dataset) 
    7574    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurveA.py

    r11593 r11085  
    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/LearningCurve.svg</icon> 
     4<icon>icons/LearningCurveA.png</icon> 
    55<priority>1000</priority> 
    66""" 
    77 
    8 import Orange 
    9  
    108from OWWidget import * 
    11 import OWGUI 
     9import OWGUI, orngTest, orngStat 
    1210 
    1311class OWLearningCurveA(OWWidget): 
    1412    settingsList = ["folds", "steps", "scoringF", "commitOnChange"] 
    15  
     13     
    1614    def __init__(self, parent=None, signalManager=None): 
    1715        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1816 
    19         self.inputs = [("Data", Orange.data.Table, self.dataset), 
    20                        ("Learner", Orange.core.Learner, self.learner, 
    21                         Multiple)] 
    22  
     17        self.inputs = [("Data", ExampleTable, self.dataset), 
     18                       ("Learner", orange.Learner, self.learner, Multiple)] 
     19         
    2320        self.folds = 5     # cross validation folds 
    2421        self.steps = 10    # points in the learning curve 
     
    2623        self.commitOnChange = 1 # compute curve on any change of parameters 
    2724        self.loadSettings() 
    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)] 
     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)] 
    3527        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    3628        self.data = None   # data on which to construct the learning curve 
     
    4436 
    4537        OWGUI.separator(self.controlArea) 
    46  
    4738        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    4839        scoringNames = [x[0] for x in self.scoring] 
    49         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
    50                        callback=self.computeScores) 
     40        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
    5141 
    5242        OWGUI.separator(self.controlArea) 
     43        box = OWGUI.widgetBox(self.controlArea, "Options") 
     44        OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
     45                   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)]) 
    5348 
    54         box = OWGUI.widgetBox(self.controlArea, "Options") 
    55         OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
    56                    label='Cross validation folds:  ', 
    57                    callback=lambda: self.computeCurve(self.commitOnChange)) 
    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)]) 
    6249        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    63         self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
    64                                       callback=self.computeCurve, disabled=1) 
    65  
    66         OWGUI.rubber(self.controlArea) 
     50        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    6751 
    6852        # table widget 
    69         self.table = OWGUI.table(self.mainArea, 
    70                                  selectionMode=QTableWidget.NoSelection) 
    71  
     53        self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
     54                 
    7255        self.resize(500,200) 
    7356 
    7457    ##############################################################################     
    75     # slots: handle input signals 
     58    # slots: handle input signals         
    7659 
    7760    def dataset(self, data): 
     
    8568            self.curves = [] 
    8669            self.scores = [] 
    87         self.commitBtn.setEnabled(self.data is not None) 
     70        self.commitBtn.setEnabled(self.data<>None) 
    8871 
    8972    def learner(self, learner, id=None): 
     
    124107            self.infob.setText("No learners.") 
    125108        self.commitBtn.setEnabled(len(self.learners)) 
    126  
     109##        if len(self.scores): 
    127110        if self.data: 
    128111            self.setTable() 
     
    147130    def getLearningCurve(self, learners):    
    148131        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    149         curve = Orange.evaluation.testing.learning_curve_n( 
    150             learners, self.data, folds=self.folds, 
    151             proportions=self.curvePoints, callback=pb.advance) 
     132        curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
    152133        pb.finish() 
    153134        return curve 
    154135 
    155136    def setCurvePoints(self): 
    156         self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
     137        self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
    157138 
    158139    def setTable(self): 
     
    173154            self.table.setColumnWidth(i, 80) 
    174155 
     156############################################################################## 
     157# Test the widget, run from prompt 
    175158 
    176159if __name__=="__main__": 
     
    179162    ow.show() 
    180163     
    181     l1 = Orange.classification.bayes.NaiveLearner() 
     164    l1 = orange.BayesLearner() 
    182165    l1.name = 'Naive Bayes' 
    183166    ow.learner(l1, 1) 
    184167 
    185     data = Orange.data.Table('iris.tab') 
     168    data = orange.ExampleTable('iris.tab') 
    186169    ow.dataset(data) 
    187170 
    188     l2 = Orange.classification.bayes.NaiveLearner() 
     171    l2 = orange.BayesLearner() 
    189172    l2.name = 'Naive Bayes (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)) 
     173    l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
     174    l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
    194175    ow.learner(l2, 2) 
    195176 
    196     l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
     177    import orngTree 
     178    l4 = orngTree.TreeLearner(minSubset=2) 
    197179    l4.name = "Decision Tree" 
    198180    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveB.py

    r11593 r11085  
    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/LearningCurve.svg</icon> 
     4<icon>icons/LearningCurveB.png</icon> 
    55<priority>1010</priority> 
    66""" 
    77 
    8 import Orange 
    9  
    108from OWWidget import * 
    11 import OWGUI 
    12  
     9import OWGUI, orngTest, orngStat 
    1310 
    1411class OWLearningCurveB(OWWidget): 
     
    1815        OWWidget.__init__(self, parent, signalManager, 'LearningCurveA') 
    1916 
    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)] 
     17        self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
     18                       ("Test Data", ExampleTable, self.testset), 
     19                       ("Learner", orange.Learner, self.learner, Multiple)] 
    2420         
    2521        self.folds = 5     # cross validation folds 
     
    2824        self.commitOnChange = 1 # compute curve on any change of parameters 
    2925        self.loadSettings() 
    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)] 
     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)] 
    3728        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    3829        self.data = None   # data on which to construct the learning curve 
     
    4738 
    4839        OWGUI.separator(self.controlArea) 
    49  
    5040        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    5141        scoringNames = [x[0] for x in self.scoring] 
    52         OWGUI.comboBox(box, self, "scoringF", items=scoringNames, 
    53                        callback=self.computeScores) 
     42        OWGUI.comboBox(box, self, "scoringF", items=scoringNames, callback=self.computeScores) 
    5443 
    5544        OWGUI.separator(self.controlArea) 
    56  
    5745        box = OWGUI.widgetBox(self.controlArea, "Options") 
    58         OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
    59                    label='Cross validation folds:  ', 
     46        OWGUI.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds:  ', 
    6047                   callback=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)]) 
     48        OWGUI.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points:  ', 
     49                   callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
    6650 
    6751        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    68         self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
    69                                       callback=self.computeCurve, disabled=1) 
     52        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    7053 
    7154        # table widget 
    72         self.table = OWGUI.table(self.mainArea, 
    73                                  selectionMode=QTableWidget.NoSelection) 
     55        self.table = OWGUI.table(self.mainArea, selectionMode=QTableWidget.NoSelection) 
    7456                 
    7557        self.resize(500,200) 
    7658 
    77     ############################################################################## 
    78     # slots: handle input signals 
     59    ##############################################################################     
     60    # slots: handle input signals         
    7961 
    8062    def trainset(self, data): 
     
    8870            self.curves = [] 
    8971            self.scores = [] 
    90         self.commitBtn.setEnabled(self.data is not None) 
     72        self.commitBtn.setEnabled(self.data<>None) 
    9173 
    9274    def testset(self, testdata): 
     
    158140        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    159141        if not self.testdata: 
    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                 ) 
     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)             
    170146        pb.finish() 
    171147        return curve 
    172148 
    173149    def setCurvePoints(self): 
    174         self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)] 
     150        self.curvePoints = [(x+1.)/self.steps for x in range(self.steps)] 
    175151 
    176152    def setTable(self): 
     
    191167            self.table.setColumnWidth(i, 80) 
    192168 
     169############################################################################## 
     170# Test the widget, run from prompt 
    193171 
    194172if __name__=="__main__": 
     
    197175    ow.show() 
    198176     
    199     l1 = Orange.classification.bayes.NaiveLearner() 
     177    l1 = orange.BayesLearner() 
    200178    l1.name = 'Naive Bayes' 
    201179    ow.learner(l1, 1) 
    202180 
    203     data = Orange.data.Table('iris.tab') 
    204     indices = Orange.data.sample.SubsetIndices2(data, p0 = 0.7) 
     181    data = orange.ExampleTable('iris.tab') 
     182    indices = orange.MakeRandomIndices2(data, p0 = 0.7) 
    205183    train = data.select(indices, 0) 
    206184    test = data.select(indices, 1) 
     
    209187    ow.testset(test) 
    210188 
    211     l2 = Orange.classification.bayes.NaiveLearner() 
     189    l2 = orange.BayesLearner() 
    212190    l2.name = 'Naive Bayes (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)) 
     191    l2.estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10) 
     192    l2.conditionalEstimatorConstructor = orange.ConditionalProbabilityEstimatorConstructor_ByRows(estimatorConstructor = orange.ProbabilityEstimatorConstructor_m(m=10)) 
    217193    ow.learner(l2, 2) 
    218194 
    219     l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
     195    import orngTree 
     196    l4 = orngTree.TreeLearner(minSubset=2) 
    220197    l4.name = "Decision Tree" 
    221198    ow.learner(l4, 4) 
  • docs/extend-widgets/rst/OWLearningCurveC.py

    r11593 r11085  
    66""" 
    77 
    8 import Orange 
    9  
    108from OWWidget import * 
    119from OWColorPalette import ColorPixmap 
     10import OWGUI, orngTest, orngStat 
    1211from OWGraph import * 
    13  
    14 import OWGUI 
    1512 
    1613import warnings 
     
    2320        OWWidget.__init__(self, parent, signalManager, 'LearningCurveC') 
    2421 
    25         self.inputs = [("Data", Orange.data.Table, self.dataset), 
    26                        ("Learner", Orange.classification.Learner, 
    27                         self.learner, Multiple)] 
     22        self.inputs = [("Data", ExampleTable, self.dataset), 
     23                       ("Learner", orange.Learner, self.learner, Multiple)] 
    2824 
    2925        self.folds = 5     # cross validation folds 
     
    3430        self.graphDrawLines = 1 # draw lines between points in the graph 
    3531        self.graphShowGrid = 1  # show gridlines in the graph 
    36         self.selectedLearners = [] 
    37  
     32        self.selectedLearners = []  
    3833        self.loadSettings() 
    3934 
    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)] 
     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)] 
    5039        self.learners = [] # list of current learners from input channel, tuples (id, learner) 
    5140        self.data = None   # data on which to construct the learning curve 
     
    6049        ## class selection (classQLB) 
    6150        OWGUI.separator(self.controlArea) 
    62  
    63         # ~SPHINX start color cb~ 
    6451        self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    65         self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
    66                                  selectionMode=QListWidget.MultiSelection, 
    67                                  callback=self.learnerSelectionChanged) 
     52        self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", selectionMode=QListWidget.MultiSelection, callback=self.learnerSelectionChanged) 
    6853         
    6954        self.llb.setMinimumHeight(50) 
    7055        self.blockSelectionChanges = 0 
    71         # ~SPHINX end color cb~ 
    7256 
    7357        OWGUI.separator(self.controlArea) 
    74  
    7558        box = OWGUI.widgetBox(self.controlArea, "Evaluation Scores") 
    7659        scoringNames = [x[0] for x in self.scoring] 
     
    7962 
    8063        OWGUI.separator(self.controlArea) 
    81  
    8264        box = OWGUI.widgetBox(self.controlArea, "Options") 
    8365        OWGUI.spin(box, self, 'folds', 2, 100, step=1, 
     
    8668        OWGUI.spin(box, self, 'steps', 2, 100, step=1, 
    8769                   label='Learning curve points:  ', 
    88                    callback=[self.setCurvePoints, 
    89                              lambda: self.computeCurve(self.commitOnChange)]) 
     70                   callback=[self.setCurvePoints, lambda: self.computeCurve(self.commitOnChange)]) 
    9071 
    9172        OWGUI.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') 
    92         self.commitBtn = OWGUI.button(box, self, "Apply Setting", 
    93                                       callback=self.computeCurve, disabled=1) 
    94  
    95         # ~SPHINX start main area tabs~ 
     73        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
     74 
    9675        # start of content (right) area 
    9776        tabs = OWGUI.tabWidget(self.mainArea) 
    9877 
    99         # graph tab 
     78        # graph widget 
    10079        tab = OWGUI.createTabPage(tabs, "Graph") 
    10180        self.graph = OWGraph(tab) 
     
    10584        self.setGraphGrid() 
    10685 
    107         # table tab 
     86        # table widget 
    10887        tab = OWGUI.createTabPage(tabs, "Table") 
    10988        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
    110         # ~SPHINX end main area tabs~ 
    11189 
    11290        self.resize(550,200) 
     
    209187    def getLearningCurve(self, learners): 
    210188        pb = OWGUI.ProgressBar(self, iterations=self.steps*self.folds) 
    211         curve = Orange.evaluation.testing.learning_curve_n( 
    212             learners, self.data, folds=self.folds, 
    213             proportions=self.curvePoints, 
    214             callback=pb.advance) 
    215  
     189        curve = orngTest.learningCurveN(learners, self.data, folds=self.folds, proportions=self.curvePoints, callback=pb.advance) 
    216190        pb.finish() 
    217191        return curve 
     
    276250        else: 
    277251            curve.setStyle(QwtPlotCurve.NoCurve) 
    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  
     252        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
     253          QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
     254          QSize(self.graphPointSize, self.graphPointSize))) 
    284255        curve.setPen(QPen(learner.color, 5)) 
    285256 
    286257    def drawLearningCurve(self, learner): 
    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) 
     258        if not self.data: return 
     259        curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
    294260         
    295261        learner.curve = curve 
     
    302268            self.drawLearningCurve(l[1]) 
    303269 
     270############################################################################## 
     271# Test the widget, run from prompt 
    304272 
    305273if __name__=="__main__": 
     
    308276    ow.show() 
    309277 
    310     l1 = Orange.classification.bayes.NaiveLearner() 
     278    l1 = orange.BayesLearner() 
    311279    l1.name = 'Naive Bayes' 
    312280    ow.learner(l1, 1) 
    313281 
    314     data = Orange.data.Table('iris.tab') 
     282    data = orange.ExampleTable('iris.tab') 
    315283    ow.dataset(data) 
    316284 
    317     l2 = Orange.classification.bayes.NaiveLearner() 
     285    l2 = orange.BayesLearner() 
    318286    l2.name = 'Naive Bayes (m=10)' 
    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") 
     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") 
    323291    ow.learner(l3, 3) 
    324292 
    325     l4 = Orange.classification.tree.TreeLearner(minSubset=2) 
     293    import orngTree 
     294    l4 = orngTree.TreeLearner(minSubset=2) 
    326295    l4.name = "Decision Tree" 
    327296    ow.learner(l4, 4) 
     
    330299#    ow.learner(None, 2) 
    331300#    ow.learner(None, 4) 
     301     
     302 
    332303 
    333304    appl.exec_() 
  • docs/extend-widgets/rst/OWLearningCurve_plot.py

    r11593 r11085  
    7373        self.commitBtn = OWGUI.button(box, self, "Apply Setting", callback=self.computeCurve, disabled=1) 
    7474 
    75         # ~start main area tabs~ 
    7675        # start of content (right) area 
    7776        tabs = OWGUI.tabWidget(self.mainArea) 
    7877 
    79         # graph tab 
     78        # graph widget 
    8079        tab = OWGUI.createTabPage(tabs, "Graph") 
    8180        self.graph = OWPlot(tab) 
     
    8584        self.setGraphGrid() 
    8685 
    87         # table tab 
     86        # table widget 
    8887        tab = OWGUI.createTabPage(tabs, "Table") 
    8988        self.table = OWGUI.table(tab, selectionMode=QTableWidget.NoSelection) 
    90         # ~end main area tabs~ 
    9189 
    9290        self.resize(550,200) 
     
    258256 
    259257    def drawLearningCurve(self, learner): 
    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) 
     258        if not self.data: return 
     259        curve = self.graph.add_curve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
    267260         
    268261        learner.curve = curve 
  • docs/extend-widgets/rst/basics.rst

    r11593 r11439  
    1414some really nice interaction may be over 1000 lines long. 
    1515 
     16When we have started to write this tutorial, we have been working 
     17on widgets for quite a while. There are now (now being in the very 
     18time this page has been crafted) about 50 widgets available, and we 
     19have pretty much defined how widgets and their interfaces should look 
     20like. We have also made some libraries that help set up GUI with only 
     21a few lines of code, and some mechanisms that one may found useful and 
     22user friendly, like progress bars and alike. 
     23 
    1624On this page, we will start with some simple essentials, and then 
    1725show how to build a simple widget that will be ready to run within 
     
    2533category has an associated priority. Opening Orange Canvas, a visual 
    2634programming environment that comes with Orange, widgets are listed in 
    27 a toolbox on the left: 
     35toolbox on the top of the window: 
    2836 
    2937.. image:: widgettoolbox.png 
    3038 
    31 The widgets and categories to which they belong are discovered at Orange 
    32 Canvas startup leveraging setuptools/distribute and it's `entry points 
    33 <http://pythonhosted.org/distribute/setuptools.html#dynamic-discovery-of-services-and-plugins>`_ 
    34 protocol. In particular Orange Canvas looks for widgets using a 
    35 `orange.widgets` entry point. 
    36  
    37  
    38 First we will examine an existing widget in Orange. The Test Learners 
    39 widget which is implemented in `OWTestLearners.py 
    40 <http://orange.biolab.si/trac/browser/orange/Orange/OrangeWidgets/Evaluate/OWTestLearners.py>`_. 
    41  
    42 Here is its header:: 
    43  
    44     """ 
     39By default, Orange is installed in site-packages directory of 
     40Python libraries. Widgets are all put in the subdirectories of 
     41OrangeWidget directory; these subdirectories define widget 
     42categories. For instance, under windows and default settings, a 
     43directory that stores all the widgets displayed in the Evaluate pane is 
     44*C:\\Python23\\Lib\\site-packages\\Orange\\OrangeWidgets\\Evaluate*. Figure 
     45above shows that at the time of writing of this text there were five 
     46widgets for evaluation of classifiers, and this is how my Evaluate 
     47directory looked like: 
     48 
     49.. image:: explorer.png 
     50 
     51Notice that there are a number of files in Evaluate directory, so 
     52how does Orange Canvas distinguish those that define widgets? Well, 
     53widgets are Python script files that start with a header. Here is a 
     54header for OWTestLearners.py:: 
     55 
    4556    <name>Test Learners</name> 
    4657    <description>Estimates the predictive performance of learners on a data set.</description> 
    47     <icon>icons/TestLearners1.svg</icon> 
     58    <icon>icons/TestLearners.png</icon> 
    4859    <priority>200</priority> 
    49     """ 
    50  
    51 OWTestLearners is a Python module, so the header information we 
     60 
     61OWTestLearners is a Python script, so the header information we 
    5262show about lies within the comment block, with triple quote opening 
    5363and closing the comment. Header defines the name of the widget, its 
     
    5565icon, and a number expressing the priority of the widget. The name of 
    5666the widget as given in the header will be the one that will be used 
    57 throughout in Orange Canvas. The description of the widget is shown 
    58 once mouse rests on an toolbox icon representing the widget. And for 
     67throughout in Orange Canvas. As for naming, the actual file name of 
     68the widget is not important. The description of the widget is shown 
     69once mouse rests on an toolbar icon representing the widget. And for 
    5970the priority: this determines the order in which widgets appear in the 
    60 toolbox within a category. 
     71toolbox. The one shown above for Evaluate groups has widget named Test 
     72Learners with priority 200, Classifications with 300, ROC Analysis 
     73with 1010, Lift Curve with 1020 and Calibration Plot with 1030. Notice 
     74that every time the priority number crosses a multiplier of a 1000, 
     75there is a gap in the toolbox between the widgets; in this way, a 
     76subgroups of the widgets within the same group can be imposed. 
    6177 
    6278Widgets communicate. They use typed channels, and exchange 
     
    6884    self.outputs = [("Evaluation Results", orngTest.ExperimentResults)] 
    6985 
     86Above two lines are for Test Learners widget, so hovering with your 
     87mouse over its icon in the widget toolbox would yield: 
     88 
     89.. image:: mouseoverwidgetintoolbox.png 
    7090 
    7191We will go over the syntax of channel definitions later, but for 
     
    7393 
    7494   - Widgets are defined in a Python files. 
    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. 
     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. 
    80103   - The sole role of priority is to specify the placement (order) of widgets 
    81104     in the Orange Canvas toolbox. 
     
    85108     is seen from the outside. 
    86109 
    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. 
     110Oh, by the way. Orange caches widget descriptions to achieve a faster 
     111startup, but this cache is automatically refreshed at startup if any change 
     112is detected in widgets' files. 
    91113 
    92114*********** 
     
    97119have some fun and write a widget. We will start with a very simple 
    98120one, that will receive a data set on the input and will output a data 
    99 set with 10% of the data instances. We will call this widget 
    100 `OWDataSamplerA.py` (OW for Orange Widget, DataSampler since this is what 
    101 widget will be doing, and A since we prototype a number of this widgets 
    102 in our tutorial). 
    103  
    104 But first we must create a simple `python project`_ layout called *Demo*, 
    105 that 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  
    109 The layout should be:: 
    110  
    111    Demo/ 
    112          setup.py 
    113          orangedemo/ 
    114                      __init__.py 
    115                      OWDataSamplerA.py 
    116  
    117 and the :download:`setup.py` should contain 
    118  
    119 .. literalinclude:: setup.py 
    120  
    121 Note that we declare our *orangedemo* package as containing widgets 
    122 from an ad hoc defined category *Demo*. 
    123  
    124 Following the previous example of OWTestLearners, our module defining 
    125 the OWDataSamplerA widget starts with a following header:: 
     121set with 10% of the data instances. Not to mess with other widgets, we 
     122will create a Test directory within OrangeWidgets directory, and write 
     123the widget in a file called `OWDataSamplerA.py`: OW for Orange Widget, 
     124DataSampler since this is what widget will be doing, and A since we 
     125prototype a number of this widgets in our tutorial. 
     126 
     127The script defining the OWDataSamplerA widget starts with a follwing header:: 
    126128 
    127129    <name>Data Sampler</name> 
    128130    <description>Randomly selects a subset of instances from the data set</description> 
    129     <icon>icons/DataSamplerA.svg</icon> 
     131    <icon>icons/DataSamplerA.png</icon> 
    130132    <priority>10</priority> 
    131133 
     
    136138 
    137139Orange Widgets are all derived from the class OWWidget. The name of 
    138 the class should match the file name, so the lines following the 
     140the class should be match the file name, so the lines following the 
    139141header in our Data Sampler widget should look something like:: 
    140142 
    141     import Orange 
    142143    from OWWidget import * 
    143144    import OWGUI 
     
    146147 
    147148        def __init__(self, parent=None, signalManager=None): 
    148             OWWidget.__init__(self, parent, signalManager) 
    149  
    150             self.inputs = [("Data", Orange.data.Table, self.data)] 
    151             self.outputs = [("Sampled Data", Orange.data.Table)] 
     149            OWWidget.__init__(self, parent, signalManager, 'SampleDataA') 
     150 
     151            self.inputs = [("Data", ExampleTable, self.data)] 
     152            self.outputs = [("Sampled Data", ExampleTable)] 
    152153 
    153154            # GUI 
     
    157158            self.resize(100,50) 
    158159 
    159 In initialization, the widget calls the :func:`__init__` method 
    160 of a base class. Widget then defines inputs and outputs. For input, 
    161 this is a *Data* channel, accepting tokens of the type 
    162 :class:`Orange.data.Table` and specifying that :func:`data` method will 
     160In initialization, the widget calls the :obj:`__init__` function 
     161of a base class, passing the name 'SampleData' which will, 
     162essentially, be used for nothing else than a stem of a file for saving 
     163the parameters of the widgets (we will regress on these somehow 
     164latter in tutorial). Widget then defines inputs and outputs. For 
     165input, widget defines a "Data" channel, accepting tokens of the type 
     166orange.ExampleTable and specifying that :obj:`data` function will 
    163167be used to handle them. For now, we will use a single output channel 
    164168called "Sampled Data", which will be of the same type 
    165 (Orange.data.Table). 
    166  
    167 Notice that the types of the channels are specified by a class; 
    168 you can use any class here, but if your widgets need to talk with 
    169 other widgets in Orange, you will need to check which classes are 
    170 used there. Luckily, and as one of the main design principles, 
    171 there are just a few channel types that current Orange widgets are 
    172 using. 
     169(orange.ExampleTable). 
     170 
     171Notice that the types of the channels are 
     172specified by a class name; you can use any classes here, but if your 
     173widgets need to talk with other widgets in Orange, you will need to 
     174check which classes are used there. Luckily, and as one of the main 
     175design principles, there are just a few channel types that current 
     176Orange widgets are using. 
    173177 
    174178The next four lines specify the GUI of our widget. This will be 
     
    192196 
    193197In order to complete our widget, we now need to define how will it 
    194 handle the input data. This is done in a method called :func:`data` 
    195 (remember, we did introduce this name in the specification of the 
    196 input channel):: 
     198handle the input data. This is done in a function called 
     199:obj:`data` (remember, we did introduce this name in the 
     200specification of the input channel):: 
    197201 
    198202    def data(self, dataset): 
     
    209213            self.send("Sampled Data", None) 
    210214 
    211 The :obj:`dataset` argument is the token sent through the input 
    212 channel which our method needs to handle. 
     215The function is defined within a class definition, so its first 
     216argument has to be :obj:`self`. The second argument called 
     217:obj:`dataset` is the token sent through the input channel which 
     218our function needs to handle. 
    213219 
    214220To handle the non-empty token, the widget updates the interface 
     
    219225"Sampled Data". 
    220226 
    221 Notice that the token can be empty (``None``), resulting from either 
    222 the sending widget to which we have connected intentionally emptying 
    223 the channel, or when the link between the two widgets is removed. 
    224 In any case, it is important that we always write token handlers 
    225 that appropriately handle the empty tokens. In our implementation, 
    226 we took care of empty input data set by appropriately setting the 
    227 GUI of a widget and sending an empty token to the output channel. 
    228  
    229  
    230 Although our widget is now ready to test, for a final touch, let's 
    231 design an icon for our widget. As specified in the widget header, we 
    232 will call it :download:`DataSamplerA.svg <DataSamplerA.svg>` and will 
    233 put it in `icons` subdirectory of `orangedemo` directory. 
    234  
    235 With this we cen now go ahead and install the orangedemo package. We 
    236 will do this by running :code:`python setup.py develop` command from 
    237 the `Demo` directory. 
    238  
    239 .. note:: 
    240    Depending on your python installation you might need 
    241    administrator/superuser privileges. 
     227Notice that the token can be empty (``dataset is None``), 
     228resulting from either the sending widget to which we have connected 
     229intentionally emptying the channel, or when the link between the two 
     230widgets is removed. In any case, it is important that we always write 
     231token handlers that appropriately handle the empty tokens. In our 
     232implementation, we took care of empty input data set by appropriately 
     233setting the GUI of a widget and sending an empty token to the 
     234output 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). 
    242242 
    243243For a test, we now open Orange Canvas. There should be a new pane in a 
    244 widget toolbox called Demo. If we click on this pane, it displays an 
    245 icon of our widget. Try to hover on it to see if the header and channel 
    246 info was processed correctly: 
     244widget toolbox called Test (this is the name of the directory we have 
     245used to put in our widget). If we click on this pane, it displays an 
     246icon of our widget. Try to hoover on it to see if the header and 
     247channel info was processed correctly: 
    247248 
    248249.. image:: samplewidgetontoolbox.png 
    249250 
    250251Now for the real test. We put the File widget on the schema (from 
    251 Data pane) and load the iris.tab data set. We also put our Data 
    252 Sampler widget on the scheme and open it (double click on the icon, 
    253 or right-click and choose Open): 
     252Data pane), read iris.tab data set. We also put our Data Sampler widget on the pane and 
     253open it (double click on the icon, or right-click and choose 
     254Open): 
    254255 
    255256.. image:: datasamplerAempty.png 
    256257 
    257 Now connect the File and Data Sampler widget (click on an output 
    258 connector of the File widget, and drag the line to the input connector 
    259 of the Data Sampler). If everything is ok, as soon as you release the 
    260 mouse, the connection is established and, the token that was waiting 
    261 on the output of the file widget was sent to the Data Sampler widget, 
    262 which in turn updated its window: 
     258Drag this window off the window with the widget schema of Orange 
     259Canvas, and connect File and Data Sampler widget (click on an ouput 
     260connector - green box - of the File widget, and drag the line to the 
     261input connector of the Data Sampler). If everything is ok, as soon as 
     262you release the mouse the connection is established and, the token 
     263that was waiting on the output of the file widget was sent to the Data 
     264Sampler widget, which in turn updated its window: 
    263265 
    264266.. image:: datasamplerAupdated.png 
     
    290292        ow = OWDataSamplerA() 
    291293        ow.show() 
    292         dataset = Orange.data.Table('iris.tab') 
     294        dataset = orange.ExampleTable('iris.tab') 
    293295        ow.data(dataset) 
    294296        appl.exec_() 
    295297 
    296298These are essentially some calls to Qt routines that run GUI for our 
    297 widgets. Notice that we call the :func:`data` method directly. 
     299widgets. At the core, however, notice that instead of sending the 
     300token to the input channel, we directly called the routine for token 
     301handling (:obj:`data`). 
     302 
     303To test your widget in more complex environment, that for instance 
     304requires to set a complex schema in which your widget collaborates, 
     305use Orange Canvas to set the schema and then either 1) save the schema 
     306to 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 
     308run each time you will test your widget. 
  • docs/extend-widgets/rst/channels.rst

    r11593 r11439  
    1818******************** 
    1919 
    20 In essence, the basic idea about "multi-input" channels is that they can 
     20First, I do not like the name, but can't make up anything better. In 
     21essence, the basic idea about "multi-input" channels is that they can 
    2122be used to connect them with several output channels. That is, if a 
    2223widget supports such a channel, several widgets can feed their input 
     
    2425 
    2526Say we want to build a widget that takes a data set and test 
    26 various predictive modeling techniques on it. A widget has to have an 
     27various predictive modelling techniques on it. A widget has to have an 
    2728input data channel, and this we know how to deal with from our 
    2829:doc:`previous <settings>` lesson. But, somehow differently, we 
     
    4950widget are defined by:: 
    5051 
    51     self.inputs = [("Data", Orange.data.Table, self.dataset), 
    52                    ("Learner", Orange.classification.Learner, 
    53                     self.learner, Multiple + Default)] 
     52    self.inputs = [("Data", ExampleTable, self.dataset), 
     53                   ("Learner", orange.Learner, 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:: 
    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. 
     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. 
    6968 
    7069How does the widget know from which widget did the token come from? 
     
    7271sending the token, and having a multi-input channel only tells Orange to 
    7372send a token together with sending widget id, the two arguments with 
    74 which the receiving function is called. For our *"Learner"* 
    75 channel the receiving function is :func:`learner`, and this looks 
     73which the receiving function is called. For our :obj:`Learner` 
     74channel the receiving function is :obj:`learner`, and this looks 
    7675like the following:: 
    7776 
     
    132131(:obj:`None`). Remember that sending an empty learner 
    133132essentially means that the link with the sending widget was removed, 
    134 hence we need to remove such learner from our list. If a non-empty 
     133hance we need to remove such learner from our list. If a non-empty 
    135134learner was sent, then it is either a new learner (say, from a widget 
    136135we have just linked to our learning curve widget), or an update 
     
    150149all that is needed is the augmenting the list:: 
    151150 
    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)] 
     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)] 
    158157 
    159158which is defined in the initialization part of the widget. The 
     
    179178is:: 
    180179 
    181     self.outputs = [("Sampled Data", Orange.data.Table), 
    182                     ("Other Data", Orange.data.Table)] 
     180    self.outputs = [("Sampled Data", ExampleTable), ("Other Data", ExampleTable)] 
    183181 
    184182We used this in the third incarnation of :download:`data sampler widget <OWDataSamplerC.py>`, 
    185 with essentially the only other change in the code in the :func:`selection` and 
    186 :func:`commit` functions:: 
     183with essentially the only other change in the code in the :obj:`selection` and 
     184:obj:`commit` functions:: 
    187185 
    188186    def selection(self): 
    189         indices = Orange.data.sample.SubsetIndices2(p0=self.proportion / 100.) 
     187        indices = orange.MakeRandomIndices2(p0=self.proportion / 100.) 
    190188        ind = indices(self.dataset) 
    191189        self.sample = self.dataset.select(ind, 0) 
     
    199197If a widget that has multiple channels of the same type is 
    200198connected to a widget that accepts such tokens, Orange Canvas opens a 
    201 window asking the user to confirm which channels to connect. Hence, 
    202 if we have just connected *Data Sampler (C)* widget to a Data Table 
    203 widget in a schema below: 
     199window asking the user to confirm which channels to connect. The 
     200channel mentioned in :obj:`self.outputs` is connected by 
     201default. Hence, if we have just connected Data Sampler 
     202(C) widget to a Data Table widget in a schema below: 
    204203 
    205204.. image:: datasampler-totable.png 
     
    224223training data set channel will be the default one. 
    225224 
    226 When enlisting the input channel of the same type, the default 
     225When enlisting the input channel of the same type, the non-default 
    227226channels have a special flag in the channel specification list. So for 
    228227our new :download:`learning curve <OWLearningCurveB.py>` widget, the 
    229228channel specification is:: 
    230229 
    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)] 
     230    self.inputs = [("Train Data", ExampleTable, self.trainset, Default), 
     231                   ("Test Data", ExampleTable, self.testset), 
     232                   ("Learner", orange.Learner, self.learner, Multiple)] 
    234233 
    235234That is, the :obj:`Train Data` channel is a single-token 
     
    241240.. image:: file-to-learningcurveb.png 
    242241 
    243 That is, no window with a query on which channels to connect to will 
    244 open, as the default *"Train Data"* was selected. 
     242That is, no window with a query on which channels 
     243to connect to will open. To find out which channels got connected, 
     244double click on the green link between the two widgets: 
     245 
     246.. image:: file-to-learningcurveb-channels.png 
  • docs/extend-widgets/rst/contextsettings.rst

    r11593 r11408  
    2121selecting a subset of attributes and the class attributes (note that a 
    2222better widget for this task is already included in your Orange 
    23 installation). 
     23instalation). 
    2424 
    2525.. image:: attributesampler.png 
     
    2929somehow store the user's selection. 
    3030 
    31 Here's the widget's :func:`__init__` function. 
    32  
    33 Part of :download:`OWAttributeSampler.py <OWAttributeSampler.py>` 
    34  
    35 .. literalinclude:: OWAttributeSampler.py 
    36    :pyobject: OWAttributeSampler.__init__ 
     31Here's the widget's :obj:`__init__` function. 
     32 
     33Part 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) 
    3755 
    3856Note that we are strictly using controls from OWGUI. As for the 
     
    4664 
    4765When the widget gets the data, a function :obj:`dataset` is 
    48 called:: 
     66called. 
     67 
     68Part of :download:`OWAttributeSampler.py`:: 
    4969 
    5070    def dataset(self, data): 
     
    6585 
    6686 
    67 .. literalinclude:: OWAttributeSampler.py 
    68    :pyobject: OWAttributeSampler.outputData 
    69  
     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) 
    7094 
    7195Nothing special here (yet). We fill the list box, deselect all 
     
    86110exist in the actual domain at all. 
    87111 
    88 To 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~ 
     112To 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")])} 
    93119 
    94120at the same place where we usually declare :obj:`settingsList`. 
     
    130156(:obj:`DomainContextHandler.Optional`); sometimes certain 
    131157attribute doesn't really matter, so if it is present in the domain, 
    132 it's going to be used, otherwise not. And for the list, we could say 
     158it's gonna be used, otherwise not. And for the list, we could say 
    133159:obj:`DomainContextHandler.List + DomainContextHandler.Required` 
    134160in which case all the attributes on the list would be required for the 
    135161domain to match. 
    136162 
    137 The default flag is :obj:`DomainContextHandler.Required`, and there 
    138 are other shortcuts for declaring the context, too. The above code could 
    139 be simplified as :: 
    140  
    141     contextHandlers = { 
    142         "": DomainContextHandler( 
    143             "", 
    144             ["classAttribute", 
    145              ContextField("attributeList", 
    146                           DomainContextHandler.SelectedRequiredList, 
    147                           selected="selectedAttributes")])} 
     163The 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")])} 
    148169 
    149170Why the dictionary and the empty string as the key? A widget can 
     
    165186function :obj:`dataset` 
    166187 
    167 .. literalinclude:: OWAttributeSampler.py 
    168    :pyobject: OWAttributeSampler.dataset 
    169  
    170 We added only two lines. First, before you change any controls in 
    171 the widget, you need to call :obj:`self.closeContext` (the function 
    172 has an optional argument, the context name, but since we use the 
    173 default name, an empty string, we can omit it). This reads the 
    174 data from the widget into the stored context. Then the function 
    175 proceeds as before: the controls (the list box and combo box) 
    176 are filled in as if there were no context handling (this is 
    177 important, so once again: widget should be set up as if there 
    178 were not context dependent settings). When the controls are put 
    179 in a consistent state, we call :obj:`self.openContext`. The first 
    180 argument is the context name and the second is the object from 
    181 which the handler reads the context. In case of 
    182 :obj:`DomainContextHandler` this can be either a domain or the 
    183 data. :obj:`openContext` will make the context handler search 
    184 through the stored context for the one that (best) matches the 
    185 data, 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 
    187 etc.). If no context is found, a new context is established and the 
    188 data from widget is copied to the context. 
     188Part 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 
     210We 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. 
    189211 
    190212What can be stored as a context dependent setting? Anything, even 
  • docs/extend-widgets/rst/graphing.rst

    r11593 r11439  
    44 
    55The most fun widgets are of course those that include graphics. For 
    6 this we either use Qt's :class:`QGraphicsScene` (widgets for tree and 
    7 heat map visualizations, for instance, use this), or use a special 
    8 control for drawing data plots as provided in Qwt library and :mod:`PyQwt` 
     6this we either use control called canvas, which is Qt's general 
     7control for doing any graphics of choice (widgets for tree and heat map 
     8visualizations, for instance, use this), or use a special control for 
     9drawing data plots as provided in Qwt library and PyQwt 
    910interface. Here we look at the latter, and extend our learning curve 
    1011widget with a control that plots the curve. 
     
    2526The widget still provides learning curve table, but this is now 
    2627offered in a tabbed pane together with a graph. The code for 
    27 definition of the tabbed pane, and initialization of the graph is 
     28definition of the tabbed pane, and initialization of the graph is:: 
    2829 
    29 .. literalinclude:: OWLearningCurveC.py 
    30    :start-after: # ~SPHINX start main area tabs~ 
    31    :end-before: # ~SPHINX end main area tabs~ 
     30    # start of content (right) area 
     31    tabs = OWGUI.tabWidget(self.mainArea) 
    3232 
    33 :class:`~OWGraph.OWGrap` is a convenience subclass of :class:`QwtPlot` 
    34 and is imported from OWGraph module. For the graph, we use 
    35 :func:`setAxisAutoScale` to request that the axis are automatically 
    36 set in regard to the data that is plotted in the graph. We plot 
    37 the graph in using the following code 
     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() 
    3840 
    39 .. literalinclude:: OWLearningCurveC.py 
    40    :pyobject: OWLearningCurveC.drawLearningCurve 
     41:obj:`OWGrap` is a convenience subclass of QwtPlot and is imported from 
     42OWGraph module. For the graph, we use :obj:`setAxisAutoScale` to 
     43request that the axis are automatically set in regard to the data that 
     44is plotted in the graph. We plot the graph in using the following 
     45code:: 
     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() 
    4154 
    4255This is simple. We store the curve returned from :obj:`addCurve` with a 
    43 learner. 
     56learner, and use a trick allowed in Orange that we can simply store 
     57this as a new attribute to the learning object. By default, Orange 
     58would 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 
     64but we surpress such warnings with a line:: 
     65 
     66    warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
     67 
    4468 
    4569.. warning:: 
    4670 
    47    This is a very bad design. Please do **not** store widget specific 
    48    data in the input objects. 
     71   This is a very bad design. Please do **not** store widget data in the 
     72   input objects. 
    4973 
    5074 
    51 In this way, each learner also stores the current scores, which is a 
    52 list of numbers to be plotted in Qwt graph. The details on how the 
    53 plot is set are dealt with in :obj:`setGraphStyle` function: 
     75in the initialization part of the widget. In this way, each learner 
     76also stores the current scores, which is a list of numbers to be 
     77plotted in Qwt graph. The details on how the plot is set are dealt 
     78with in :obj:`setGraphStyle` function:` :: 
    5479 
    55 .. literalinclude:: OWLearningCurveC.py 
    56    :pyobject: OWLearningCurveC.setGraphStyle 
    57  
     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)) 
    5890 
    5991Notice that the color of the plot line that is specific to the 
     
    71103instances of one class should be plotted in scatter plot and parallel 
    72104axis plot using the same color. Developers are thus advised to use 
    73 :obj:`ColorPaletteHSV`, which can be imported from :mod:`OWWidget` 
    74 module. :obj:`ColorPaletteHSV` takes an 
    75 integer as an parameter, and returns a list of corresponding number of 
     105:obj:`ColorPaletteHSV`, which is provided as a method within 
     106:mod:`OWWidget` module. :obj:`ColorPaletteHSV` takes an 
     107integer as an attribute, and returns a list of corresponding number of 
    76108colors. In our learning curve widget, we use it within a function that 
    77 sets the list box with learners 
     109sets the list box with learners:: 
    78110 
    79 .. literalinclude:: OWLearningCurveC.py 
    80    :pyobject: OWLearningCurveC.updatellb 
     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 
    81122 
    82123The code above sets the items of the list box, where each item 
     
    85126returned by :obj:`ColorPixmap` function defined in 
    86127:obj:`OWColorPalette.py`. Else, the classifier's list box control is 
    87 defined in the initialization of the widget using 
     128defined in the initialization of the widget using:: 
    88129 
    89 .. literalinclude:: OWLearningCurveC.py 
    90    :start-after: # ~SPHINX start color cb~ 
    91    :end-before: # ~SPHINX end color cb~ 
     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 
    92137 
    93138Now, what is this :obj:`blockSelectionChanges`? Any time 
    94139user makes a selection change in list box of classifiers, we want to 
    95140invoke the procedure called 
    96 :func:`learnerSelectionChanged`. But we want to perform 
     141:obj:`learnerSelectionChanged`. But we want to perform 
    97142actions there when changes in the list box are invoked from clicking 
    98143by a user, and not by changing list box items from a program. This is 
    99 why, every time we want :func:`learnerSelectionChanged` not to 
     144why, every time we want :obj:`learnerSelectionChanged` not to 
    100145perform its function, we set :obj:`self.blockSelectionChanges` 
    101146to 1. 
    102147 
    103 In our widget, :func:`learnerSelectionChanged` figures out 
     148In our widget, :obj:`learnerSelectionChanged` figures out 
    104149if any curve should be removed from the graph (the user has just 
    105150deselected the corresponding item in the list box) or added to the 
    106 graph (the user just selected a learner) 
     151graph (the user just selected a learner):: 
    107152 
    108 .. literalinclude:: OWLearningCurveC.py 
    109    :pyobject: OWLearningCurveC.learnerSelectionChanged 
    110  
    111 .. 
    112153    def learnerSelectionChanged(self): 
    113154        if self.blockSelectionChanges: 
  • docs/extend-widgets/rst/progressbar.rst

    r11593 r11408  
    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 on the icon 
    10 of the widget in Orange Canvas 
     9completion of the task. The progress is then either reported above the 
     10icon of the widget in Orange Canvas. 
    1111 
    12 .. image:: learningcurve.png 
     12.. image:: progressbar-canvas.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:: learningcurve-output.png 
     17.. image:: progressbar-widget.png 
    1818 
    19 :class:`OWWidget` has for this purpose a set of functions, which include: 
     19Class :obj:`OWWidget`, the mother class of all 
     20widgets, has for this purpose a set of functions, which include: 
    2021 
    2122.. method:: progressBarInit() 
     
    2526.. method:: progressBarFinished() 
    2627 
     28 
    2729where value is any number between 0 and 100. Sometimes, like it is 
    2830the case for our widgets, we know about the number of iterations involved in 
    2931computation, and we would only like to advance the progress bar for 
    3032some constant at the end of the iteration. For this, we use 
    31 :class:`ProgressBar` class in :mod:`OWGUI`, and the code in 
     33:obj:`ProgressBar` class in :obj:`OWGUI`, and the code in 
    3234the learning curve widget described in the previous lesson that does 
    3335it is as follows:: 
    3436 
    3537    def getLearningCurve(self, learners): 
    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) 
     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) 
    4041        pb.finish() 
    4142        return curve 
    4243 
    43 :class:`ProgressBar` class removes the need to define any special 
    44 function to compute the percent of the task done and set the 
    45 progress bar, and instead uses :class:`ProgressBar`'s method 
    46 :func:`advance` for this purpose. 
     44:obj:`ProgressBar` class removes the need to define any 
     45special function to compute the percent of the task done and set the 
     46progress bar, and instead uses :obj:`ProgressBar`'s method 
     47:obj:`advance` for this purpose. 
  • docs/extend-widgets/rst/settings.rst

    r11593 r11439  
    4646    class OWDataSamplerB(OWWidget): 
    4747        settingsList = ['proportion', 'commitOnChange'] 
     48        def __init__(self, parent=None, signalManager=None): 
    4849 
    4950Any setting has to be initialized, and then we need to call 
     
    156157of this widget. To distinguish it with a widget we have developed in the 
    157158previous section, we have designed a special 
    158 :download:`icon <DataSamplerB.svg>` for it. If you wish to test is 
     159:download:`icon <DataSamplerB.png>` for it. If you wish to test is 
    159160widget in the Orange Canvas, put its code in the Test directory we 
    160161have created for the previous widget, update the Canvas registry, and 
     
    409410:obj:`ContextField("attrX", DomainContextHandler.Required)` 
    410411 
     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 
    411463 
    412464***************************** 
  • docs/reference/rst/code/exclude-from-regression.txt

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

    r11594 r11533  
    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 APP=${TMP_BUNDLE_DIR}/Orange.app 
    31  
    32 # Python interpreter in the bundle 
    33 PYTHON=${APP}/Contents/MacOS/python 
    34  
    35 # Python version 
    36 PY_VER=`$PYTHON -c "import sys; print sys.version[:3]"` 
    37  
    38 SITE_PACKAGES=${APP}/Contents/Frameworks/Python.framework/Versions/${PY_VER}/lib/python${PY_VER}/site-packages/ 
    39  
    40 # easy_install script in the bundle 
    41 EASY_INSTALL=${APP}/Contents/MacOS/easy_install 
    42  
    43 # Link Python.app startup script to top bundle 
    44 ln -fs ../Frameworks/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python ${APP}/Contents/MacOS/PythonAppStart 
    45  
    46 echo "Preparing startup scripts" 
    47  
    48 # Create an enironment startup script 
    49 cat <<-'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} 
    72 EOF 
    73  
    74 # Create Orange application startup script 
    75 cat <<-'EOF' > ${APP}/Contents/MacOS/Orange 
     30# Create bundle startup script 
     31cat <<-'EOF' > ${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/Orange 
    7632    #!/bin/bash 
    7733 
    78     DIRNAME=$(dirname "$0") 
    79     source "$DIRNAME"/ENV 
     34    source `dirname "$0"`/ENV 
    8035 
    8136    # LaunchServices passes the Carbon process identifier to the application with 
     
    8540    fi 
    8641 
    87     exec -a "$0" "$DIRNAME"/PythonAppStart -m Orange.OrangeCanvas.main "$@" 
     42    exec -a "$0" "$PYTHONEXECUTABLE" -m Orange.OrangeCanvas.main "$@" 
    8843EOF 
    8944 
    90 chmod +x ${APP}/Contents/MacOS/Orange 
     45chmod +x ${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/Orange 
     46 
     47# Python interpreter in the bundle 
     48PYTHON=${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/python 
     49 
     50# easy_install script in the bundle 
     51EASY_INSTALL=${TMP_BUNDLE_DIR}/Orange.app/Contents/MacOS/easy_install 
     52 
     53#Python version 
     54PY_VER=`$PYTHON -c "import sys; print sys.version[:3]"` 
    9155 
    9256# First install/upgrade distrubute. The setup.py scripts might 
     
    9963echo "Checkouting and building orange" 
    10064echo "===============================" 
    101 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange orange $REVISION $REPOS_DIR ${APP} 
     65./bundle-inject-hg.sh https://bitbucket.org/biolab/orange orange $REVISION $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
    10266 
    10367echo "Specifically building orangeqt" 
    10468echo "------------------------------" 
    10569 
    106 pushd $REPOS_DIR/orange/source/orangeqt 
     70CUR_DIR=`pwd` 
     71cd $REPOS_DIR/orange/source/orangeqt 
    10772echo "Fixing sip/pyqt configuration" 
    10873 
    109 sed -i.bak "s@/Users/.*/Orange.app/@$APP/@g" ${SITE_PACKAGES}/PyQt4/pyqtconfig.py 
    110 sed -i.bak "s@/Users/.*/Orange.app/@$APP/@g" ${SITE_PACKAGES}/sipconfig.py 
     74APP=${TMP_BUNDLE_DIR}/Orange.app 
     75APP_ESCAPED=`echo ${TMP_BUNDLE_DIR}/Orange.app | sed s/'\/'/'\\\\\/'/g` 
     76sed -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 
     77sed -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 
    11178export PATH=$APP/Contents/Resources/Qt4/bin:$PATH 
    11279$PYTHON setup.py install 
    113  
    114 popd 
     80cd $CUR_DIR 
    11581 
    11682echo "Fixing Qt plugins search path" 
     
    11884Plugins = ../../../../../Resources/Qt4/plugins/" > $APP/Contents/Frameworks/Python.framework/Resources/Python.app/Contents/Resources/qt.conf 
    11985 
    120 echo "[Paths] 
    121 Plugins = Resources/Qt4/plugins/" > $APP/Contents/Resources/qt.conf 
    122  
    12386 
    12487echo "Checkouting and building bioinformatics addon" 
    12588echo "=============================================" 
    126 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-bioinformatics bioinformatics tip $REPOS_DIR ${APP} 
     89./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-bioinformatics bioinformatics tip $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
    12790 
    12891echo "Checkouting and building text addon" 
    12992echo "===================================" 
    130 ./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-text text tip $REPOS_DIR ${APP} 
     93./bundle-inject-hg.sh https://bitbucket.org/biolab/orange-text text tip $REPOS_DIR ${TMP_BUNDLE_DIR}/Orange.app 
    13194 
    13295echo "Installing networkx" 
    13396echo "+++++++++++++++++++" 
    134 ./bundle-inject-pypi.sh networkx-1.6 http://pypi.python.org/packages/source/n/networkx/networkx-1.6.tar.gz $REPOS_DIR ${APP} 
     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 
    13598 
    13699echo "Installing suds library" 
    137100echo "+++++++++++++++++++++++" 
    138 ./bundle-inject-pypi.sh suds-0.4 http://pypi.python.org/packages/source/s/suds/suds-0.4.tar.gz $REPOS_DIR ${APP} 
     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 
    139102 
    140103echo "Instaling slumber library" 
     
    145108find $TMP_BUNDLE_DIR \( -name '*~' -or -name '*.bak' -or -name '*.pyc' -or -name '*.pyo' -or -name '*.pyd' \) -exec rm -rf {} ';' 
    146109 
    147 ln -s ../Frameworks/Python.framework/Versions/Current/lib/python${PY_VER}/site-packages/Orange ${APP}/Contents/Resources/Orange 
     110ln -s ../Frameworks/Python.framework/Versions/Current/lib/python${PY_VER}/site-packages/Orange ${TMP_BUNDLE_DIR}/Orange.app/Contents/Resources/Orange 
    148111 
    149112     
  • setup.py

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

    r11583 r7665  
    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.