This module provides bindings to the Python programming language. Basic usage in the context of SciDAVis will be discussed below, but for more in-depth information on the language itself, please refer to its excellent documentation.
Make sure your current project uses Python as its interpreter by selecting the menu point Scripting->Scripting Language and double-clicking on "Python" in the resulting dialog (if the dialog appears, but does not contain the "Python" item, your installation of SciDAVis has been compiled without Python support). Next, open a Notes window and enter print "Hello World!". Position the cursor anywhere on this line and press Ctrl+J, or select "Execute" from the context menu. The string "Hello World!" should appear below the line you entered. Congratulations, you've made contact with Python! You can also enter more complex code fragments, such as function or class definitions, in which case you have to select the whole code block before executing it.
You can also use Notes windows as a handy calculator. Enter a mathematical expression on a line of its own - say, 5*sin(pi/2). Press Ctr+Enter, or select "Evaluate" from the context menu. You are rewarded by a new line, stating (to general astonishment), that the result of evaluating your expression is #> 5. If you have SciPy and/or PyGSL installed, you will have immediate access to a huge number of interesting functions, browsable via the submenu "Functions" of the context menu. Pressing Shift+F1 while in this menu will give you a short description of the current function. The advantage of only executing/evaluating single lines or selections on request is that you can freely mix text and calculations within a Notes window.
Another particularly handy place for using Python code are column formulas. These work just like evaluating expressions in Note windows, with the additional advantage of some pre-defined variables: i (the row currently being computed), j (the column number), sr (start row), er (end row) and self (the table to which the column being computed belongs; see below for what you can do with it).
If you are already familiar with Python, you might ask yourself at this point if you can use more complicated column formulas than just single expressions (for instance, if:/else: decisions based on the current row number). The answer is: yes, you can. For the benefit of those not familiar with Python, we will give a short introduction to the language before discussing how to do this.
Mathematical expressions work largely as expected. However, there's one caveat, especially when switching from muParser: a^b does not mean "raise a to the power of b" but rather "bitwise exclusive or of a and b"; Python's power operator is **. Thus:
2^3 # read: 10 xor 11 = 01 #> 1 2**3 #> 8
One thing you have to know when working with Python is that indentation is very important. It is used for grouping (most other languages use either braces or keywords like do...end for this). For example, executing the following:
x=23 for i in (1,4,5): x=i**2 print(x)will do what you would expect: it prints out the numbers 1, 16 and 25; each on a line of its own. Deleting just a bit of space will change the functionality of your program:
x=23 for i in (1,4,5): x=i**2 print(x)will print out only one number - no, not 23, but rather 25. This example was designed to also teach you something about variable scoping: There are no block-local variables in Python.
There are two different variable scopes to be aware of: local and
global variables. Unless specified otherwise, variables are local to the
context in which they were defined. Thus, the variable
x
can have three different values in, say, two
different Note windows and a column formula. Global variables on the
other hand can be accessed from everywhere within your project. A
variable x
is declared global by executing the
statement global x. You have to do this before
assigning a value to x
, but you have to do it only
once within the project (no need to "import" the variable before using
it). Note that there is a slight twist to these rules when you
define your own functions.
The basic syntax for defining a function (for use within one particular note, for example) is
def answer(): return 42If you want your function to be accessible from the rest of your project, you have to declare it global before the definition:
global answer def answer(): return 42You can add your own function to SciDAVis's function list. We'll also provide a documentation string that will show up, for example, in the "set column values" dialog:
global answer def answer(): "Return the answer to the ultimate question about life, the universe and everything." return 42 sci.mathFunctions["answer"] = answerIf you want to remove a function from the list, execute
del sci.mathFunctions["answer"]
Note that functions have their own local scope. That means that if you enter a function definition in a Notes window, you will not be able to access (neither reading nor writing) Notes-local variables from within the function. However, you can access global variables as usual.
If-then-else decisions are entered as follows:
if x>23: print(x) else: print("The value is too small.")
You can do loops, too:
for i in range(1, 11): print(i)This will print out the numbers between 1 and 10 inclusively (the upper limit does not belong to the range, while the lower limit does).
As we've already mentioned above, there's a bit more to evaluation than just expressions. Note that Python itself can indeed only evaluate expressions; the following describes a feature of SciDAVis added on top of Python in order to support more interesting column formulas.
If you use statements (e.g. variable assignments or control structures) in a formula, SciDAVis will assume it to be the body of a function. That is, the following code will not work:
a=1 a+aThe statement in the first line means that the formula cannot be evaluated as a single expression. Instead, the above code assigns every row in the column the return value of the following function:
def f(): a=1 a+aHowever, since Python does not implicitly interpret expressions as something to return, this evaluates to nothing. The correct way to enter formulas with statements in them is to explicitly return something:
a=1 return a+a
There is a slight catch associated with this strategy. In a Notes window, SciDAVis will allow you to evaluate variable assignments like ee=1.6021773e-19 without complaining - but this will not lead to the result presumably intended, i.e. introducing a shortcut for the elementary charge to be used within the notes window. What actually happens is this: SciDAVis evaluates the function
def f(): ee=1.6021773e-19As mentioned in the Python introduction above, the function f has its own variable scope and (unless it happens to be declared global) the variable ee will only be visible within this scope (instead of the Notes window's scope). The solution is simple: always execute things like assignments and function definitions, never evaluate them.
Python comes with some basic mathematical functions that are automatically imported (if you use the initialization file shipped with SciDAVis). Along with them, the constants e (Euler's number) and pi (the one and only) are defined. Many, many more functions can be obtained by installing SciPy and/or PyGSL.
Table 7-4. Supported Mathematical Functions
Name | Description |
---|---|
acos(x) | inverse cosinus |
asin(x) | inverse sinus |
atan(x) | inverse tangent |
atan2(y,x) | equivalent to atan(y/x), but more efficient |
ceil(x) | ceiling; smallest integer greater or equal to x |
cos(x) | cosinus of x |
cosh(x) | hyperbolic cosinus of x |
degrees(x) | convert angle from radians to degrees |
exp(x) | Exponential function: e raised to the power of x. |
fabs(x) | absolute value of x |
floor(x) | largest integer smaller or equal to x |
fmod(x,y) | remainder of integer division x/y |
frexp(x) | Returns the tuple (mantissa,exponent) such that x=mantissa*(2**exponent) where exponent is an integer and 0.5 <=abs(m)<1.0 |
hypot(x,y) | equivalent to sqrt(x*x+y*y) |
ldexp(x,y) | equivalent to x*(2**y) |
log(x) | natural (base e) logarythm of x |
log10(x) | decimal (base 10) logarythm of x |
modf(x) | return fractional and integer part of x as a tuple |
pow(x,y) | x to the power of y; equivalent to x**y |
radians(x) | convert angle from degrees to radians |
sin(x) | sinus of x |
sinh(x) | hyperblic sinus of x |
sqrt(x) | square root of x |
tan(x) | tangent of x |
tanh(x) | hyperbolic tangent of x |
We will assume that you are using the initialization file shipped with SciDAVis.
Accessing the objects in your project is straight-forward,
t = table("Table1") m = matrix("Matrix1") g = graph("Graph1") n = note("Notes1")as is creating new objects:
# create an empty table named "tony" with 5 rows and 2 columns: t = newTable("tony", 5, 2) # use defaults t = newTable() # create an empty matrix named "gina" with 42 rows and 23 columns: m = newMatrix("gina", 42, 23) # use defaults m = newMatrix() # create an empty graph window g = newGraph() # create an empty note named "momo" n = note("momo") # use defaults n = note()
New objects will always be added to the active folder. The functions table, matrix, graph and note will start searching in the active folder and, failing this, continue with a depth-first recursive search of the project's root folder. In order to access other folders, there are the functions
f = activeFolder() # and f = rootFolder()which can be used to access subfolders and windows:
f2 = f.folders()[number] f2 = f.folder(name, caseSensitive=True, partialMatch=False) t = f.table(name, recursive=False) m = f.matrix(name, recursive=False) g = f.graph(name, recursive=False) n = f.note(name, recursive=False)If you supply True for the recursive argument, a depth-first recursive search of all subfolders will be performed and the first match returned.
Also, every piece of code is executed in the context of an
object which you can access via the self
variable. For example,
entering self.cell("t",i) as a column formula
is equivalent to the convenience function
col("t").
We'll assume that you have assigned some table to the variable
t
. You can access its numeric cell values with
t.cell(col, row) # and t.setCell(col, row, value)Whenever you have to specify a column, you can use either the column name (as a string) or the consecutive column number (starting with 1). Row numbers also start with 1, just as they are displayed. If you want to work with arbitrary texts or the textual representations of numeric values, you can use
t.text(col, row) # and t.setText(col, row, string)The number of columns and rows is accessed via
t.numRows() t.numCols() t.setNumRows(number) t.setNumCols(number)Column names can be read and written with
t.colName(number) t.setColName(col, newName)Normalize a single or all columns:
t.normalize(col) t.normalize()Import values from
file
, using
sep
as separator char and ignoring
ignore
lines at the head of the file. The flags
should be self-explanatory. t.importASCII(file, sep="\t", ignore=0, renameCols=False, stripSpaces=True, simplifySpace=False, newTable=False)After having changed some table values from a script, you will likely want to update dependent Graphs:
t.notifyChanges()
As a simple example, let's set some column values without using the dialog.
t = table("table1") for i in range(1, t.numRows()+1): t.setCell(1, i, i**2) t.notifyChanges()
We'll assume that you have assigned some matrix to the variable
m
. Accessing cell values is very similar to Table,
but since Matrix doesn't use column logic, row arguments are specified
before columns and obviously you can't use column name.
m.cell(row, col) m.setCell(row, col, value) m.text(row, col) m.setText(row, col, string)Also like with tables, there's
m.numRows() # and m.numCols()
If you want to create a new Graph window for some data in table table1, you can use the plot command:
g = plot(table, column, type)
type
is a number between 0 and 10 and
specifies the desired plot type: Line
Symbols
Line and Symbols
Columns
Area
Pie
Vertical drop lines
Splines and Symbols
Vertical steps
Histogram
Rows
g = plot(table("table1"), (2,4,7), 2)
If you want to add a curve to an existing Graph window, you have to choose the destination layer. Usually,
l = g.activeLayer()will do the trick, but you can also select a layer by its number:
l = g.layer(num)You can then add or remove curves to or from this layer:
l.insertCurve(table, column, type=1) l.insertCurve(table, Xcolumn, Ycolumn, type=1) l.removeCurve(curveName) l.removeCurve(curveNumber) l.deleteFitCurves()In case you need the number of curves on a layer, you can get it with
l.numCurves()
Layers and whole Graphs can be printed and exported from within Python. Before you do this, you would probably want to change layer and axis titles as well as legend texts:
l.setTitle(title) l.setXTitle(Xtitle) l.setYTitle(Ytitle) l.setLegend(text)You can also customize the scales of the different axes using:
l.setScale(int axis, double start, double end, double step=0.0, int majorTicks=5, int minorTicks=5, int type=0, bool inverted=False);where
axis
is a number between 0 and 3 with the following signification: Left axis
Right axis
Bottom axis
Top axis
type
specifies the desired scale type: Linear
Log10
step
defines the size of the interval between the major scale ticks. If not specified (default value is 0.0), the step size is calculated automatically.
The other flags should be self-explanatory.
Now, here is how you can export a layer l.print() l.exportToSVG(filename) l.exportToEPS(filename) l.exportImage(filename, filetype="PNG", quality=100, transparent=False)and a graph
g.print() g.exportToSVG(filename) g.exportToEPS(filename)
Assuming you have a Graph named "graph1" with a curve entitled "table1_2" (on its active layer), a minimal Fit example would be:
f = GaussFit(graph("graph1").activeLayer(), "table1_2") f.guessInitialValues() f.fit()This creates a new GaussFit object on the curve, lets it guess the start parameters and does the fit. The following fit types are supported:
LinearFit(layer, curve)
PolynomialFit(layer, curve, degree=2, legend=False)
ExponentialFit(layer, curve, growth=False)
TwoExpFit(layer, curve)
ThreeExpFit(layer, curve)
GaussFit(layer, curve)
GaussAmpFit(layer, curve)
LorentzFit(layer,curve)
SigmoidalFit(layer, curve)
NonLinearFit(layer, curve)
f = NonLinearFit(layer, curve) f.setParameters(name1, ...) f.setFormula(formula_string)
PluginFit(layer, curve)
f = PluginFit(layer, curve) f.load(pluginName)
f = LinearFit(graph("graph1").activeLayer(), "table1_2", 2, 7) f.fit()
After creating the Fit object and before calling its fit() method, you can set a number of parameters that influence the fit:
f.setDataFromCurve(curve) change data source f.setDataFromCurve(curve, graph) change data source f.setDataFromCurve(curve, from, to) change data source f.setDataFromCurve(curve, from, to, graph) change data source f.setInterval(from, to) change data range f.setInitialValue(number, value) f.setInitialValues(value1, ...) f.guessInitialValues() f.setAlgorithm(algo) # algo = Fit.ScaledLevenbergMarquardt, Fit.UnscaledLevenbergMarquardt, Fit.NelderMeadSimplex f.setWeightingData(method, colname) # method = Fit.NoWeighting, Fit.Instrumental, Fit.Statistical, Fit.Dataset f.setTolerance(tolerance) f.setOutputPrecision(precision) f.setMaximumIterations(number) f.scaleErrors(yes = True) f.setColor(qt.QColor("green")) change the color of the result fit curve to green (default color is red)
After you've called fit(), you have a number of possibilities for extracting the results:
f.results() f.errors() f.chiSquare() f.rSquare() f.dataSize() f.numParameters() f.parametersTable("params") f.covarianceMatrix("cov")
This file allows you to customize the Python environment, import modules and define functions and classes that will be available in all of your projects. The default initialization file shipped with SciDAVis imports Python's standard math functions as well as special functions from SciPy and PyGSL(if available). Also, it creates some handy shortcuts, like table("table1") for sci.app.table("table1").
When activating Python support, SciDAVis searches the following places, executing the first file it can find:
~/.scidavisrc.py[c]
/etc/scidavisrc.py[c]
./scidavisrc.py[c]
Files ending in .pyc are compiled versions of the .py source files and therefore load a bit faster. The compiled version will be used if the source file is older or nonexistent. Otherwise, SciDAVis will try to compile the source file (if you've got write permissions for the output file).