Python

This module provides bindings to the Python programming language. Basic usage in the context of QtiPlot will be discussed below, but for more in-depth information on the language itself, please refer to its excellent documentation.

The Initialization File

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 QtiPlot imports Python's standard math functions as well as special functions from SciPy (if available). Also, it creates some handy shortcuts, like table("table1") for qti.app.table("table1").

When activating Python support, QtiPlot searches the following places, executing the first file it can find:

  1. ~/.qtiplotrc.py[c]

  2. /etc/qtiplotrc.py[c]

  3. ./qtiplotrc.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, QtiPlot will try to compile the source file (if you've got write permissions for the output file).

Python Basics

Mathematical expressions work largely as expected. However, there's one caveat, especially when switching from muParser (which has been used exclusively in previous versions of QtiPlot): 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,

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.

Defining Functions and Control Flow

The basic syntax for defining a function (for use within one particular note, for example) is

def answer():
	return 42
	

If 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 42
	

You can add your own function to QtiPlot'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
qti.mathFunctions["answer"] = answer
	

If you want to remove a function from the list, do:

del qti.mathFunctions["answer"]
	

Note that functions have their own local scope. That means that if you enter a function definition in a Note, you will not be able to access (neither reading nor writing) Note-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).

Mathematical Functions

Python comes with some basic mathematical functions that are automatically imported (if you use the initialization file shipped with QtiPlot). Along with them, the constants e (Euler's number) and pi (the one and only) are defined.

Table 7.3. Supported Mathematical Functions

NameDescription
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

Accessing QtiPlot's functions from Python

We will assume that you are using the initialization file shipped with QtiPlot.

Establishing contact

Accessing the objects in your project is straight-forward,

t = table("table1")
m = matrix("matrix1")
g = graph("graph1")
n = note("Note1")
	  

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()
	  

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").

Working with Tables

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()
	  

Working with Matrices

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()
	  

Plotting and Working with Graphs

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:

0.

Line

1.

Symbols

2.

Line and Symbols

3.

Columns

4.

Area

5.

Pie

6.

Vertical drop lines

7.

Splines and Symbols

8.

Vertical steps

9.

Histogram

10.

Rows

You can plot more than one column at once by giving a Python tuple (see the Python Tutorial) as an argument:

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 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)
	  

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)
	  

Fitting

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)

  • PluginFit(layer, curve)

For each of these, you can optionally restrict the X range that will be used for the fit, like in

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)
	  

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")