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

The qtiplotrc.py file allows you to customize the Python environment, import modules and define functions and classes that will be available in all of your projects. If QtiPlot was built using Qt 4 library, the name of the default initialization file is qtiplotrc_qt4.py.

The default initialization file shipped with QtiPlot imports Python's standard math functions as well as (if available) special functions from SciPy, the symbolic mathematics library SymPy and helper functions for RPy2. Also, it creates some handy shortcuts, like table("table1") for qti.app.table("table1").

When activating Python support, QtiPlot searches the initialization file in a default folder, which can be customized via the File Locations tab of the Preferences dialog. If the initialization file is not found, QtiPlot will issue a warning and muParser will be kept as the default interpreter.

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. All statements outside of any function use module scope; that is, variables attached to one column script or script window. All statements inside a function use that function's "local" scope, but they can also read module variables. System-wide globals are stored in the special variable globals. To store your own:

globals.mydata = 5
print globals.mydata
	  
Global scope rules recently changed In older versions, write this instead:

global mydata
mydata = 5
print mydata
	  

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 other modules, you have to add it to the globals:

def answer():
	return 42
globals.answer = answer
	
If you have an older versions of QtiPlot, 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:

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

Global scope rules recently changed In older versions, a function's local scope hid the module scope. That means that if you entered a function definition in a Note, you would not be able to access (neither reading nor writing) Note-local variables from within the function. However, you could 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-5. Python: Supported Mathematical Functions

NameDescription
acos(x)inverse cosine
asin(x)inverse sine
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)cosine of x
cosh(x)hyperbolic cosine 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) logarithm of x
log10(x)decimal (base 10) logarithm 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)sine of x
sinh(x)hyperbolic sine of x
sqrt(x)square root of x
tan(x)tangent of x
tanh(x)hyperbolic tangent of x

Accessing QtiPlot's objects from Python

We will assume that you are using the initialization file shipped with QtiPlot. Accessing the objects in your project is straight-forward,


t = table("Table1")
m = matrix("Matrix1")
g = graph("Graph1")
p = plot3D("Graph2")
n = note("Notes1")
# get a pointer to the QTextEdit object used to display information in the results log window:
log = resultsLog()
# display some information in the data display toolbar:
displayInfo(text)
# get a pointer to the QLineEdit object in the data display toolbar and access the information displayed:
info = infoLineEdit()
text = info.text()
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 a graph window named "test" with two layers disposed on a 2 rows x 1 column grid
g = newGraph("test", 2, 2, 1)
# create an empty 3D plot window with default title
p = newPlot3D()
# create an empty note named "momo"
n = newNote("momo")
# use defaults
n = newNote()
The currently selected Table/Matrix etc. can be accessed with the following commands:

t = currentTable()
m = currentMatrix()
g = currentGraph()
n = currentNote()
The functions will only return a valid object if a window of the wanted type is actually selected. You can check if the object is valid with a simple if clause:
if isinstance(t,qti.Table): print "t is a table"

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

Once you have established contact with a MDI window, you can modify some of its properties, like the name, the window label, the geometry, etc.. For example, here's how to rename a window, change its label and the way they are displayed in the window title bar, the so called caption policy:

t = table("Table1")
setWindowName(t, "toto")
t.setWindowLabel("tutu")
t.setCaptionPolicy(MDIWindow.Both)
The caption policy can have one of the following values:

  1. Name

    -> the window caption is determined by the window name

  2. Label

    -> the caption is determined by the window label

  3. Both

    -> caption = "name - label"

It is also possible to add comments to a project window. The comments may include line breaks, contrary to the window label:

t = table("Table1")
t.setWindowComment("Experiment name: ...\nDate: ...\nProcedure: ...\n")
print t.windowComment()
Here's how you can access and modify the geometry of a window in the project:

t = table("Table1")
t.setGeometry(10, 10, 800, 600)
print t.x(), t.y(), t.width(), t.height()
It is possible to modify the status (minimized, maximized, normal) of a window in the project:

t = table("Table1")
t.showMinimized()
t.showMaximized()
t.showNormal()
It is also possible to hide a window:

hideWindow(table("Table1"))
...and to restore its visibility:

table("Table1").setVisible(True)
For a fast editing process, you can create template files from existing tables, matrices or plots. The templates can be used later on in order to create customized windows very easily:

saveAsTemplate(graph("Graph1"), "my_plot.qpt")
g = openTemplate("my_plot.qpt")
Also, you can easily clone a MDI window:

g1 = clone(graph("Graph1"))
If you want to delete a project window, you can use the close()method. You might want to deactivate the confirmation message, first:

w.confirmClose(False)
w.close()
All QtiPlot subwindows are displayed in a QMdiArea. You can get a pointer to this object via the workspace()method. This can be particularly usefull if you need to customize the behavior of the workspace via your scripts. Here follows a small example script that pops-up a message displaying the name of the active MDI subwindow each time a new window is activated:

def showMessage():
	QtGui.QMessageBox.about(qti.app, "", workspace().activeSubWindow().objectName())

QtCore.QObject.connect(workspace(), QtCore.SIGNAL("subWindowActivated(QMdiSubWindow *)"), showMessage)
If you want to automatically arrange all the subwindows in the workspace you can use the following methods:

tileWindows()
cascadeWindows()
It is possible to access the list of all MDI window objects in a QtiPlot project:

for w in qti.app.windows():
	print w.objectName()
...or the list of selected MDI windows in the project explorer:

for w in qti.app.selectedExplorerWindows():
	print w.objectName()

User settings

It is worth knowing that it is possible to export and store the custom application settings to an *.ini file:

qti.app.saveSettingsAs("/Users/ion/Desktop/myPrefs.ini")
Of course, it is possible to load custom application settings from such an *.ini file or from an *.ini file that has been previousely created via the preferences dialog, using the Save As...button:

qti.app.readSettings("/Users/ion/Desktop/myPrefs.ini")

Project files

It is possible to load a project file or any file type supported by QtiPlot using the function open(fileName, factorySettings = False, newProject = True). If the factorySettings argument is set to True, your current custom settings are discarded and QtiPlot resets all options to the default values. If the newProject argument is set to True (the default), a new project window is created. This function returns a reference to the main application window, which can be null if the input file doesn't exist or is not readable, for example.


app = qti.app.open("/Users/ion/Desktop/hygecdf.qti")
if not app:
	print "Invalid reference!"
else:
	dir = app.rootFolder()
	for w in dir.windows():
		print w.objectName()

The current project file can be closed using the function closeProject(). If you do not want to save your modifications to the project and want to avoid the QtiPlot save message dialog, you can use the function setSavedProject(), like shown bellow:


qti.app.setSavedProject()
qti.app.closeProject()

Project folders

Storing your data tables/matrices and your plots in folders can be very convenient and helpful when you're analyzing loads of data files in the same project. New objects will always be added to the active folder. You can get a pointer to it via:

f = activeFolder()
The functions table, matrix, graph and note will start searching in the active folder and, failing this, will continue with a depth-first recursive search of the project's root folder, given by:

f = rootFolder()
In order to access subfolders and windows, the following functions are provided:

f1 = folder("folder1")
f2 = f.folder("folder2", caseSensitive=True, partialMatch=False)
f3 = f.folders()[2] # f3 is the 3rd subfolder of parent folder f
t = f.table(name, recursive=False)
m = f.matrix(name, recursive=False)
g = f.graph(name, recursive=False)
n = f.note(name, recursive=False)
lst = f.windows()
for w in lst:
	print w.objectName()
If you supply True for the recursive argument, a depth-first recursive search of all subfolders will be performed and the first match returned.

New folders can be created using:


newFolder = addFolder("New Folder", parentFolder = 0)
If the parentFolderis not specified, the new folder will be added as a subfolder of the project's root folder. When you create a new folder via a Python script, it doesn't automatically become the active folder of the project. You have to set this programatically, using:

changeFolder(newFolder, bool force=False)
Folders can be deleted using:

deleteFolder(folder)

Folders can be renamed using the function renameFolder(). Please note that this operation may fail if the new folder name already exists. An error message pops-up in this case. Empty input strings are ignored.


f = folder("f1")
if renameFolder(f, "f2"):
	print "Success!"
It is possible to copy folders within a project:

copyFolder(folder("source"), folder("destination"))
Project windows can be moved between existing folders using:

table("Table1").moveTo(folder("folder1"))
graph("Graph1").moveTo(addFolder("folder2"))

You can save a window or a folder as a project file, and of course, you can also save the whole project:


saveWindow(table("Table1"), "Table1.qti", compress=False)
saveFolder(folder, "new_file.qti", compress=False)
saveProjectAs("new_file_2.qti", compress=False)
saveProject()
print projectName()

If compress is set to True, the project file will be archived to the .gz format, using zlib.

Also, you can load a QtiPlot or an Origin project file into a new folder. The new folder will have the base name of the project file and will be added as a subfolder to the parentFolder or to the current folder if no parent folder is specified.


newFolder = appendProject("projectName", parentFolder = 0)

If you don't want to be asked for confirmation when a table/matrix is renamed during this operation, or when deleting a folder via a Python script, you must change your preferences concerning prompting of warning messages, using the Preferences dialog ("Confirmations" tab).

Folders store their own log information containing the results of the analysis operations performed on the child windows. This information is updated in the result log window each time you change the active folder in the project. You can access and manipulate these log strings via the following functions:


text = folder.logInfo()
folder.appendLogInfo("Hello!")
folder.clearLogInfo()

Folders can also store a comment string:


f = folder("test")
f.setComment("My comments!")
print f.comment()
Finally, it is worth mentioning that it is possible to access the list of all selected folder items in the project explorer:

for folder in qti.app.selectedFolders():
	print folder.name()

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. In many places there is an alternative API which represents a table as a Python sequence is provided. Here rows are addressed by Python indices or slices which start at 0. These places are marked as such.

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)
An alternative way to get/set the value of a cell is using the getter/setter functions bellow. Assigning a Nonevalue will clear the cell:

t.cellData(col, row)
# and
t.setCellData(col, row, value)

Based on the type of the column, QtiPlot handles all the casting under the hood and throws an TypeError if setting the data for a cell isn't possible. The type of a column can be one of the following:

Table.Numeric:

returns/accepts only numerical values.

Table.Text:

returns/accepts only text strings.

Table.Date:

returns/accepts Python datetime.datetime objects and also accepts a QDateTime.


import time
from datetime import datetime
t = newTable("datetime", 2, 1)
t.setColDateFormat(1, "dd/MM/yyyy")
t.setCellData(1, 1, datetime.utcnow()) # from Python datetime object
t.setCellData(1, 2, QDateTime.currentDateTime()) # from QDateTime object
Table.Time:

returns/accepts datetime.time objects and also accepts a QTime.


import time
from datetime import datetime
t = newTable("time", 2, 1)
t.setColTimeFormat(1, "hh:mm:ss")
t.setCellData(1, 1, datetime.now().time()) # from Python time object
t.setCellData(1, 2, QTime.currentTime()) # from QTime object
Table.Month:

accepts any numerical values, including floating point values and internally transforms them to integer values corresponding to the months of the year: 1 (January) to 12 (December, for which also 0 is accepted).

Table.Day:

accepts any numerical values, including floating point values and internally transforms them to integer values corresponding to the days of the week: 1 (monday) to 7 (sunday, for which also 0 is accepted).

Table.TextAndNumeric:

returns/accepts both numerical values and text strings. It is the default type.

You can set the format of a column to text using:

t.setColTextFormat(col)
Or you can adjust the numeric format:

t.setColNumericFormat(col, format, precision)
were colis the number of the column to adjust and precisionstates the number of digits. The formatcan be one of the following:

Table.Default (0)

standard format

Table.Decimal (1)

decimal format with precision digits

Table.Scientific (2)

lower case scientific format (e.g. 1.0e+03)

Table.ScientificUpperCase (3)

upper case scientific format (e.g. 1.0E+03)

You can reset the Table.TextAndNumeric type for a column if it was modified previously using the following function:

t.setColTextNumericFormat(col, format, precision)
In the same way you can set a column to hold a date. Here the text of a cell is interpreted using a format string:

t.setColDateFormat(col, format)
t.setColDateFormat("col1", "yyyy-MM-dd HH:mm")
were colis the name/number of a column and formatthe format string. In the format string the following placeholders are recognized, all other input characters being treated as text:

d

the day as number without a leading zero (1 to 31)

dd

the day as number with a leading zero (01 to 31)

ddd

the abbreviated localized day name (e.g. 'Mon' to 'Sun')

dddd

the long localized day name (e.g. 'Monday' to 'Sunday')

M

the month as number without a leading zero (1-12)

MM

the month as number with a leading zero (01-12)

MMM

the abbreviated localized month name (e.g. 'Jan' to 'Dec')

MMMM

the long localized month name (e.g. 'January' to 'December')

yy

the year as two digit number (00-99)

yyyy

the year as four digit number

h

the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)

hh

the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)

H

the hour without a leading zero (0 to 23, even with AM/PM display)

HH

the hour with a leading zero (00 to 23, even with AM/PM display)

m

the minute without a leading zero (0 to 59)

mm

the minute with a leading zero (00 to 59)

s

the second without a leading zero (0 to 59)

ss

the second with a leading zero (00 to 59)

z

the milliseconds without leading zeroes (0 to 999)

zzz

the milliseconds with leading zeroes (000 to 999)

AP or A

interpret as an AM/PM time. AP must be either "AM" or "PM".

ap or a

interpret as an AM/PM time. ap must be either "am" or "pm".

Analog you can say that a text column should hold a time only...

t.setColTimeFormat(col, format)
t.setColTimeFormat(1, "HH:mm:ss")
... a month ...

t.setColMonthFormat(col, format)
t.setColMonthFormat(1, "M")
Here the format is the following:

M

Only the first letter of the month, i.e. "J"

MMM

The short form, like "Jan"

MMMM

The full name, "January"

... or the day of week:

t.setColDayFormat(col, format)
t.setColDayFormat(1, "ddd")
Here the format is the following:

d

Only the first letter of the day, i.e. "M"

ddd

The short form, like "Mon"

dddd

The full name, "Monday"

If you need all the data of a row or column you can use the rowData()and colData()methods. This is much faster then iterating manually over the cells. Alternatively you can use the []operator in combination with Python indices or slices, which start at 0.

valueList = t.colData(col) # col may be a string or a number starting at 1
rowTuple = t.rowData(row) # row number starting at 1
rowTuple = t[idx] # row index starts at 0
rowTupleList = t[slice]
A Table is iterable. The data is returned row wise as tuple.

for c1, c2, c3 in t:
  # do stuff, assuming t has three columns
It is possible to assign row, random or normal random values to a complete column or to a subset of rows in a column:

	t.setRowValues(1, 1, 15)#startRow = 1, endRow = 15
	t.setRandomValues(col, startRow = 1, endRow = -1)
	t.setNormalRandomValues(col, startRow = 1, endRow = -1, standardDeviation = 1.0)
Assigning values to a complete row or column is also possible. While the new row data has to be a tuple which length must match the column number, column data just has to be iteratable. If the iterator stops before the end of the table is reached, a StopIterationexception is raised. In combination with the offsetthis allows to fill a column chunk wise. A positive offset starts filling the column after this row number. A negative offset ignores the first values of the iterator.

t.setColData(col, iterableValueSequence, offset=0)
# just fill the first column with a list of values, staring at row 6
t.setColData(1, [12,23,34,56,67], 5)
# fill the second column with Fibonacci numbers, omitting the first three.
def FibonacciGenerator():
  a, b = 1, 1
  while True:
	a, b = b, a+b
	yield a
t.setColData(2, FibonacciGenerator(), -3)
t.setRowData(row, rowTuple) # row starts at 1
# assuming t has exactly two columns...
t.setRowData(2, (23, 5)) # fill the second row
t[1] = 23, 5 # using a Python index, starting at 0
# adding a new row and set it's values
t.appendRowData(rowTuple)

The number of columns and rows of a table can be accessed or modified using:


t.numRows() # same as len(t)
t.numCols()
t.setNumRows(number)
t.setNumCols(number)
You can add a new column at the end of the table using the addColumn()function. This function returns a reference to the new column object.

c1 = t.addColumn()
c1.setName("A")

c2 = addColumn(Table.X)
c2.setName("X")
It is possible to insert new columns before a startColumnusing the functions below:

t.insertColumns(startColumn, count)
Adding an empty row at the end of the table is done with the addRow()method. It returns the new row number.

newRowIndex = t.addRow()
It is also possible to swap two columns using:

t.swapColumns(column1, column2)
You can delete a column or a range of rows using the functions below:

t.removeCol(number)
t.deleteRows(startRowNumber, endRowNumber)
It is also possible to use Python's delstatement to remove rows. Note that in this case a Python index or slice (instead of row numbers) is used, which start at 0.

del t[5] # deletes row 6
del t[0:4] # deletes row 1 to 5
Column names can be read and written with:

t.colName(number)
t.colNames()
t.setColName(col, newName, enumerateRight=False)
t.setColNames(newNamesList)

When setting a column name, please note that, for internal consistency reasons, the underscore character is not allowed and is automatically replaced with a minus sign.

If enumerateRight is set to True, all the table columns starting from index col will have their names modified to a combination of the newName and a numerical increasing index. If this parameter is not specified, by default it is set to False. The plural forms get/set all headers at once.

Other column properties like the long name, the unit and the comment can be read/modified using the following methods:


t.setColLongName(1, "Distance")
print t.colLongName(1)
t.setColUnit(1, "nm")
print t.colUnit(1)
t.setColComment(1, "Experiment no. 10")
print t.colComment(1)
You can enable/disable the display of these header label rows via the following functions:

t.showLongName(True)
t.showUnits(False)
t.showComments(True)

You can change the plot role (abscissae, ordinates, error bars, etc...) of a table column col using:


t.setColumnRole(col, role)
print t.columnRole(col)
where rolespecifies the desired column role:

0.

Table.None

1.

Table.X

2.

Table.Y

3.

Table.Z

4.

Table.xErr

5.

Table.yErr

6.

Table.Label

You can normalize a single column or all columns in a table:


t.normalize(col)
t.normalize()

Sort a single or all columns:


t.sortColumn(col, order = Qt.AscendingOrder)
t.sort(type = Table.IndependentSort, order = Qt.AscendingOrder, leadingColumnName = "")
where the variable typecan take one of the following values:

0.

Table.IndependentSort

1.

Table.DependentSort

and the orderargument can be:

0.

Qt.AscendingOrder

1.

Qt.DescendingOrder

The small script bellow demonstrates how to use the sort method in order to perform a dependent sort in descending order on an entire table using a leading column:


t = newTable()
t.setColName(1, "LeadCol")
t.setRowValues(1)
t.setRowValues(2)
t.sort(Table.DependentSort, Qt.DescendingOrder, "LeadCol")

It is possible to customize the alignment of the text from the table cells which by default is set to Qt.AlignCenter:


t.setAlignment(Qt.AlignLeft) # for all table columns
t.setAlignment(Qt.AlignRight, True) # for selected columns only

Columns

It is also possible to manipulate individual table columns using their index. Column indices start at 1:


t = newTable("test", 10, 2)
c1 = t.column(1)
c1.setName("A")
c2 = t.column(2)
c2.setName("Date")

Column objects can also be accessed by their name. For example using the test table created with the script above, one gets a referrence to the column objects as follows:


c1 = t.column("A")
c2 = t.column("Date")

Please beware that the column method used above returns a null reference if the table doesn't contain a column with the name you indicated. Once you have a valid reference to a column object, c, you can access all its properties using the following getter functions:


c.name() # column short name
c.longName() # column long name
c.fullName() # returns tableName_shortName
c.unit()
c.comment()
c.command() # the formula set for the column
c.formatInfo() # the format string in case the column type is date/time
c.numericPrecision() # the number of significant digits
c.numericFormat()
c.alignment() # alignment of the text: Qt.AlignLeft, Qt.AlignCenter or Qt.AlignRight
c.type()
c.plotRole()
c.isReadOnly()
c.width()
c.size() # returns the number of rows
c.isEmpty() # returns True if all cells in the column are empty
c.lastValidRow() # index of the last non empty cell starting from the bottom
c.hasOnlyTextValues() # returns True if all cells have string values
c.index() # column index in the parent table (leftmost column has index 1)

It is of course possible to modify the above properties using the corresponding setter functions:


c.setName("A")
c.setLongName("test long name")
c.setUnit("cm")
c.setComment("column comment")
c.updateHeaderView() # makes the changes visible in the table header
c.setCommand("sin(0.1*i)")
c.setDateFormat("dd/MM/yyyy") # set format to Table.Date and format string to "dd/MM/yyyy"
c.setTimeFormat("hh:mm:ss")# set format to Table.Time and format string to "hh:mm:ss"
c.setMonthFormat("MMMM") # set Table.Month format; other format strings: "M" and "MMM"
c.setDayFormat("dddd") # set Table.Day format; other format strings: "d" and "ddd"
c.setNumericPrecision(10)
c.setNumericFormat(Table.Decimal, 7) # set decimal format with 7 precision digits
c.setType(Table.Text) # set text format
c.setPlotRole(Table.X)
c.setReadOnly()
c.setReadOnly(False) # reenable column modifications
c.setWidth(90)
c.setAlignment(Qt.AlignRight)
c.setHidden() # hides column
c.setHidden(False) # makes column visible

When setting a column name, please note that, for internal consistency reasons, the underscore character is not allowed and is automatically replaced with a minus sign.

Please note that after setting a new column formula you need to trigger a recalculation of the cell values:


t = newTable("test", 30, 2)
c = t.column(2)
c.setCommand("sin(0.1*i)")
t.recalculate(c.index())

It is straightforward to access cell values using their index, but you must be aware of the fact that for all column functions the row indices start at zero, contrary to table functions where indices start at 1:


val = c.value(0) # returns the value of the 1st cell as a double
string = c.text(0) # returns the text string displayed in the 1st cell

It is also possible to get a Python list with the values from a range of cells or from the entire column:


print c.values(1) # all cells starting with 2nd row
print c.values(1, 2) # values in 2nd and 3rd row
print c.values() # all column values

The value of a column cell can be modified using the following setter functions:


c.setValue(0, 1178.14)
c.setText(0, "text string")

It is also possible to fill a range of cells or the whole column with values from a Python list:


t = newTable("test", 10, 3)
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
t.column(1).setValues(list1, 0, 9) # fill cells from 1st to 10th
list2 = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7]
t.column(2).setValues(list2)
list3 = ['a', 'b', 'c', 'd', "test"]
t.column(3).setValues(list3, 1) # fill cells starting with 2nd row

QtiPlot stores column cell values either as floating point numerical values (doubles) or as text strings, depending on the column type. If the column type is Table.Date or Table.Time QtiPlot converts internaly the numerical values to a QDateTime object using the Table.dateTime() function and displays them using the format string specified for the column. QtiPlot also provides two methods that allow to perform the reverse conversion from a QDateTime or QTime object to a double floating point numerical value:


val1 = Table.fromDateTime(QDateTime.currentDateTime())
val2 = Table.fromTime(QTime.currentTime())
print val1
print val2

The following script shows how you can fill a table column with date values:


t = newTable("test", 10, 2)
c = t.column(1)
c.setName("Date")
c.setDateFormat("dd/MM/yyyy")

date = QDateTime.currentDateTime()
for i in range(0, c.size()):
	c.setValue(i, Table.fromDateTime(date.addDays(i)))

You can delete the content of a single cell or a range of cells using the functions bellow. Please note that after these operations you need to refresh the table view in order to be able to observe the changes:


c.clearCell(1) # clear 2nd cell
c.clear(0, 9) # clear range from 1st to 10th cell
c.deleteCells(0, 9) # delete/remove range from 1st to 10th cell
c.updateView()

For all column operations that involve a cell range if you don't specify the index of th start row it is by default considered to be zero and if you omit the end row index (or you set it to -1) it is considered to be index of the last/bottom cell:


c.clear(10) # keeps the first 10 cells from top and clears the rest
# it is equivalent to:
c.clear(10, -1)
c.clear() # clears the whole column
c.updateView()

The following functions provide automatic filling of a column with data values:


c.setRowValues(0, 7) # fill cell range with row indices
c.setRandomValues(0, 9) # fill cell range with random values
c.setNormalRandomValues(0, -1, 1.0) # fill column with a Gaussian distribution with standard deviation sigma = 1.0

It is possible to normalize a column to 1 or to sort its values in ascending or descending order:


c.normalize()
c.sort() # sort column in ascending order
c.sort(Qt.DescendingOrder) # sort column in descending order

The script bellow shows how to duplicate a table column:


t = table("Table1")
c = t.duplicateColumn(t.column(1))
c.setName("Col1Clone")

Finally, it is worth mentioning that you can copy a column (with or without its cell values) from one table to another:


t1 = newTable("test", 10, 3)
t2 = table("Table1")
c1 = t1.column(1)
c1.copy(t2.column(1))
c2 = t1.column(2)
c2.copy(t2.column(2), False) # don't copy data values
c3 = t1.column(3)
c3.copyData(t2.column(3)) # copy only the data values

Statistics on columns

There are a number of predefined statistical functions that can be used for columns having a numerical (not Text) type:


c.rms(0, 9) # returns the root mean square, also known as the quadratic mean
c.sd(0, 10) # returns the standard deviation of a cell range
c.sum(0, 10) # returns the sum of all cells in the range
c.avg(0, 20) # returns the average of a cell range
c.avg() # returns the average of the whole column
c.minValue(0, 9) # returns the minimum value in a cell range
c.minValue() # returns the minimum value in column
c.maxValue(0, 100) # returns the maximum value in a cell range

Data searching

It is possible to search for a text string in a column. The search operation returns the row index of the first occurence of the researched string. If the text string can not be found the function returns -1. Please note that by default the search operation is case insensitive and wildcard characters are allowed.


row = c.find("qti")
print row
row = c.find("Qti", QTextDocument.FindCaseSensitively) # case sensitive search
print row
row = c.find("Tuesday", QTextDocument.FindWholeWords) # find whole words
print row

Data mask

It is possible to mask a data cell or a range of cells in a column using:


c.mask(1, 10)
c.updateView()
print c.isMasked(1) # returns True
print c.hasMaskedCells(11, -1) # returns False

c.swapMask() # set a complementary mask
c.updateView()

Import ASCII files

The function importASCIIcan be used to import values from an ASCII file filePath, using sepas separator character, ignoring ignoreLineslines at the beginning of the file and all lines starting with a commentstring. Please note that the default values for the arguments of this function are given after the equality sign.

t.importASCII(filePath, sep="\t", ignoreLines=0, renameCols=False, stripSpaces=True, simplifySpace=False, importComments=False, comment="#", readOnly=False, importAs=Table.Overwrite, locale=QLocale(), endLine=0, maxRows=-1)

The names of the function arguments should be self explaining. For example, if the renameCols argument is given the value True, QtiPlot will try to rename the table columns using the text strings from the first line in the imported file. Same for the importComments argument, but in this case the second imported line is used to set the comments for the table columns.

If the simplifySpace argument is given the value True all whitespace is removed from the start and the end of each line of the file and each sequence of internal whitespace is replaced with a single space. This can of course affect the number of imported columns if the column separator sep contains whitespace characters.

As you see from the above list of import options, you have the possibility to set the new columns as read-only. This will prevent the imported data from being modified. You have the possibility to remove this protection at any time, by using:


t.setReadOnlyColumn(col, False)

The importAs argument can have the following values:

0.

Table.NewColumns: data values are added as new columns.

1.

Table.NewRows: data values are added as new rows.

2.

Table.Overwrite: all existing values are overwritten (default value).

If the number format in the imported ASCII file does not match the currently used decimal separators convention, you can specify the correct format via the locale argument, like in the example bellow:


t.importASCII("test.txt", ';', 5, True, False, False, True, "", False, Table.Overwrite, QLocale(QLocale.German))

The endLine argument specifies the end line character convention used in the ascii file. Possible values are: 0 for line feed (LF), which is the default value, 1 for carriage return + line feed (CRLF) and 2 for carriage return only (usually on Mac computers).

The last parameter maxRows allows you to specify a maximum number of imported lines. Negative values mean that all data lines should be imported.

Importing Excel sheets

It is possible to import a sheet from an Excel .xls file file to a table, using:


t = importExcel(file, sheet)

Please note that the integer variable sheet starts at 0 and must be lower than the number of sheets in the Excel workbook. If the sheet index is not specified, all non-empty sheets in the Excel workbook are imported into separate tables and a reference to the table containing the data from the last sheet is returned.

Importing ODF spreadsheets

It is possible to import a sheet from an ODF spreadsheet .ods file to a table, using:


t = importOdfSpreadsheet(file, sheet)

Please note that the integer variable sheet starts at 0 and must be lower than the number of sheets in the file. If the sheet index is not specified, all non-empty sheets in the spreadsheet are imported into separate tables and a reference to the table containing the data from the last sheet is returned.

Export Tables

You can export values from a table to an ASCII file, using sep as separator character. The ColumnLabels option allows you to export or ignore the column labels, ColumnComments does the same for the comments displayed in the table header and the SelectionOnly option makes possible to export only the selected cells of the table.


t.exportASCII(file,sep="\t",ColumnLabels=False,ColumnComments=False,SelectionOnly=False)
Other settings that you can modify are the text displayed as a comment in the header of a column...

t.setComment(col, newComment)
... or the expression used to calculate the column values. Please beware that changing the command doesn't automatically update the values of the column; you have to call recalculateexplicitly. Calling it with just the column as argument will recalculate every row. Forcing muParser can speed things up.

t.setCommand(col, newExpression)
t.recalculate(col, startRow=1, endRow=-1, forceMuParser=False, notifyChanges=True)
You can also modify the width of a column (in pixels) or hide/show table columns:

t.setColumnWidth(col, width)
t.hideColumn(col, True)
If one or several table columns are hidden you can make them visible again using:

t.showAllColumns()
You can ensure the visibility of a cell with:

t.scrollToCell(col, row)
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()
While the above is easy to understand, there is a faster and more pythonic way of doing the same:

t = table("table1")
t.setColData(1, [i*i for i in range(len(t))])
t.notifyChanges()
You can check if a column or row of a table is selected by using the following functions:

t.isColSelected(col)
t.isRowSelected(row)

NumPy interface

Using this built-in interface it is possible to retrieve the values from a table column as a NumPy array:


print(table("Table1").column("A").numPyArray())

The script bellow demonstrates how to set the values of a table column using a NumPy array as input. Please note that only floating point values are allowed:


import numpy as np
table("Table1").column("A").setNumPyArrayValues(np.array([1., 2., 3.]))

R interface

If RPy2is available, the default initialization filesets up the helper functions qti.Table.toRDataFrameand qti.app.newTableFromRDataFrameto convert back and forth between R data frames and QtiPlot tables. Here is a little example of an R session...

df <- read.table("/some/path/data.csv", header=TRUE)
m <- mean(df)
v <- var(df)
source("/some/path/my_func.r")
new_df <- my_func(df, foo=bar)
... and now the same from within QtiPlot:

df = table("Table1").toRDataFrame()
print R.mean(df), R.var(df)
R.source("/some/path/my_func.r")
new_df = R.my_func(df, foo=bar)
newTableFromRDataFrame(new_df, "my result table")

Linear Color Maps

Linear color maps are widely used in QtiPlot: for matrices, 2D and 3D plots. The script bellow defines a custom color map that is later on used to set the palette of a matrix:


map = LinearColorMap(Qt.red, Qt.blue)
map.setMode(LinearColorMap.FixedColors) # default mode is LinearColorMap.ScaledColors
map.addColorStop(0.2, Qt.magenta)
map.addColorStop(0.7, Qt.cyan)
matrix("Matrix1").setColorMap(map)

Custom color maps can be saved to XML files which can be used afterwards to quicky initialize new color maps:


map = LinearColorMap(Qt.yellow, Qt.blue)
map.addColorStop(0.3, Qt.magenta)
map.addColorStop(0.5, Qt.red)
map.addColorStop(0.7, Qt.cyan)
map.setIntensityRange(1, 100)
map.save("/Users/ion/Desktop/rainbow.xml")

mapClone = LinearColorMap.loadFromFile("/Users/ion/Desktop/rainbow.xml")

QtiPlot also provides a default rainbow color map:


map = LinearColorMap.rainbowMap()

Working with Matrices

Matrix objects have a dual view mode: either as images or as data tables. Assuming that you have assigned some matrix to the variable m, you can change its display mode via the following function:

m.setViewType(Matrix.TableView)
m.setViewType(Matrix.ImageView)
If a matrix is viewed as an image, you have the choice to display it either as gray scale or using a predefined color map:

m.setGrayScale()
m.setRainbowColorMap()
m.setDefaultColorMap() # default color map defined via the 3D plots tab of the preferences dialog
You can also define custom color maps:

map = LinearColorMap(QtCore.Qt.yellow, QtCore.Qt.blue)
map.setMode(LinearColorMap.FixedColors) # default mode is LinearColorMap.ScaledColors
map.addColorStop(0.2, QtCore.Qt.magenta)
map.addColorStop(0.7, QtCore.Qt.cyan)
m.setColorMap(map)
You have direct access to the color map used for a matrix via the following functions:

map = m.colorMap()
col1 = map.color1()
print col1.green()
col2 = map.color2()
print col2.blue()
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)
An alternative solution to assign values to a Matrix, would be to define a formula and to calculate the values using this formula, like in the following example:

m.setFormula("x*y*sin(x*y)")
m.calculate()
You can also specify a column/row range in the calculate() function, like this:

m.calculate(startRow, endRow, startColumn, endColumn)
Before setting the values in a matrix you might want to define the numeric precision, that is the number of significant digits used for the computations:

m.setNumericPrecision(prec)
You can change the dimensions of a matrix:

m.setDimensions(rows, columns)
m.setNumRows(rows)
m.setNumCols(columns)
Also, like with tables, you can access the number of rows/columns in a matrix:

rows = m.numRows()
columns = m.numCols()
It is also possible to change the dimensions of a matrix by resampling it, using bilinear or bicubic interpolation:

m.resample(rows, cols) # bilinear interpolation by default
m.resample(rows, cols, 1) # bicubic interpolation
You can also smooth the matrix data. Internally this method resamples the matrix to half size or doubles its dimensions and after that it resamples it back to the initial size using bilinear interpolation:

m.smooth()
Matrix objects allow you to define a system of x/y coordinates that will be used when plotting color/contour maps or 3D height maps. You can manipulate these coordinates using the following functions:

xs = m.xStart()
xe = m.xEnd()
ys = m.yStart()
ye = m.yEnd()
m.setCoordinates(xs + 2.5, xe, ys - 1, ye + 1)
It is also possible to define non-linear X/Y coordinates for matrices using data stored in table columns:

m = matrix("Matrix1")
m.setXCoordinates(table("Table1").column(2))
m.setYCoordinates(table("Table2").column(1))
print m.xColumnName()
for x in m.xCoordinates():
	print(x)

print m.yColumnName()
for y in m.yCoordinates():
	print(y)
The column used for the X coordinates must contain a number of valid, non empty cells at least equal with the number of columns in the matrix. Similarly, the column used for the Y coordinates must contain a number of valid, non empty cells at least equal with the number of rows in the matrix. QtiPlot sorts these data values in ascending order before assigning them as matrix coordinates. These non-linear coordinates can be reset using the following methods:

m.resetXCoordinates()
m.resetYCoordinates()
or by specifying a new linear range:

m.setXCoordinates(1.5, 10.5)
m.setYCoordinates(10.3, 20.3)
It is possible to get pointers to the column objects that store the non-linear coordinates using:

xCol = m.xColumn()
yCol = m.yColumn()
print xCol.fullName(), yCol.fullName()
As shown above, it is possible to get a list with all the X or Y coordinates using:

for x in m.xCoordinates():
	print(x)
for y in m.yCoordinates():
	print(y)
Alternatively you can get the values of the individual X/Y coordinates as shown in the script bellow:

for i in range (0, m.numCols()):
	print m.x(i)
for i in range (0, m.numRows()):
	print m.y(i)
You can also define labels, units and comments for the X, Y or Z axis that can be used in plots:

m.setXLabel("Width")
print m.xLabel()
m.setXUnit("cm")
print m.xUnit()
m.setXComment("X axis comment")
print m.xComment()

m.setYLabel("Height")
print m.yLabel()
m.setYUnit("mm")
print m.yUnit()
m.setYComment("Y axis comment")
print m.yComment()

m.setZLabel("Intensity")
print m.zLabel()
m.setZUnit("a.u.")
print m.zUnit()
m.setZComment("Z axis comment")
print m.zComment()
The horizontal and vertical headers of a matrix can display either the x/y coordinates or the column/row indexes:

m.setHeaderViewType(Matrix.ColumnRow)
m.setHeaderViewType(Matrix.XY)
There are several built-in transformations that you can apply to a matrix object. You can transpose or invert a matrix and calculate its determinant, provided, of course, that the conditions on the matrix dimensions, required by these operations, are matched:

m.transpose()
m.invert()
d = m.determinant()
Some other operations, very useful when working with images, like 90 degrees rotations and mirroring, can also be performed. By default rotations are performed clockwise. For a counterclockwise rotation you must set the clockwiseparameter to False.

m.flipVertically()
m.flipHorizontally()
m.rotate90(clockwise = True)
It is possible to perform a Fast Fourier Transform on a matrix:

m.fft() # forward FFT
m.fft(True) # inverse FFT
It is also possible to apply a FFT filter to a matrix:

m.fftFilter(FFTFilter.LowPass, 0.5)
m.fftFilter(FFTFilter.HighPass, 0.5)
m.fftFilter(FFTFilter.BandPass, 0.5, 1.5)
m.fftFilter(FFTFilter.BandBlock, 0.75, 0.9)
Please note that sometimes, after a change in the matrix settings, you need to use the following function in order to update the display:

m.resetView()
Also, it's worth knowing that you can easily import image files to matrices, that can be used afterwards for plotting (see the next section for more details about 2D plots):

m1 = importImage("C:/poze/adi/PIC00074.jpg")
m2 = newMatrix()
m2.importImage("C:/poze/adi/PIC00075.jpg")
The algorithm used to import the image returns a gray value between 0 and 255 from the (r, g, b) triplet corresponding to each pixel. The gray value is calculated using the formula: (r * 11 + g * 16 + b * 5)/32

For custom image analysis operations, you can get a copy of the matrix image view, as a QImage object, via:


image = m.image()
You can export matrices to all raster image formats supported by Qt or to any of the following vectorial image format: EPS, PS, PDF or SVG using:

m.export(fileName)
This is a shortcut function which uses some default parameters in order to generate the output image. If you need more control over the export parameters you must use one of the following functions:

m1.exportRasterImage(fileName, quality = 100, dpi = 0, compression = 0)
m2.exportVector(fileName, color = True)
where the qualityparameter influences the size of the output file. The higher this value (maximum is 100), the higher the quality of the image, but the larger the size of the resulting files. The dpiparameter represents the export resolution in pixels per inch (the default is screen resolution). The compressionparameter can be 0 (no compression) or 1 (LZW) and is only effectif for .tif/.tiff images. It is neglected for all other raster image formats.

You can also import an ASCII data file, using sep as separator characters, ignoring ignore lines at the head of the file and all lines starting with a comment string:


m.importASCII(file, sep="\t", ignore=0, stripSpaces=True, simplifySpace=False, comment="#",
				importAs=Matrix.Overwrite, locale=QLocale(), endLine=0, maxRows=-1)

The importAs flag can have the following values:

0.

Matrix.NewColumns: data values are added as new columns.

1.

Matrix.NewRows: data values are added as new rows.

2.

Matrix.Overwrite: all existing values are overwritten (default value).

The locale parameter can be used to specify the convention for decimal separators used in your ASCII file.

The endLine flag specifies the end line character convention used in the ascii file. Possible values are: 0 for line feed (LF), which is the default value, 1 for carriage return + line feed (CRLF) and 2 for carriage return only (usually on Mac computers).

The last parameter maxRows allows you to specify a maximum number of imported lines. Negative values mean that all data lines must be imported.

Also, you can export values from a matrix to an ASCII file, using sep as separator characters. The SelectionOnly option makes possible to export only the selected cells of the matrix.


m.exportASCII(file, sep="\t", SelectionOnly=False)

NumPy interface

Using this built-in interface it is possible to retrieve the values from a matrix as a bidimensional NumPy array:


print(matrix("Matrix1").numPyArray())

The script bellow demonstrates how to set the values of a matrix using a bidimensional NumPy array as input. Please note that only floating point values are allowed:


import numpy as np
a = np.array([[1., 2., 3.], [4., 5., 6.]])
matrix("Matrix1").setNumPyArrayValues(a)

Table/Matrix conversion

If you need to get data from a table, in order to use it in a matrix (or vice-versa), you can avoid time consuming copy/paste operations and speed up the whole process by simply converting the table into a matrix:

m = tableToMatrix(table("Table1"))
t = matrixToTable(m)
For the production of contour or surface plots, you can convert a regular XYZ data table ("regular" meaning that cells in the X and Y columns of the table define a regular 2D grid) into a matrix. A tolerance of 15% is used for the X and Y positions when parsing the 2D grid.

m1 = table("Table1").convertToMatrixRegularXYZ("Data")

In the example above "Data" is the name of the Z column. You can also specify the Z column by its number and choose a limited cell range for the conversion like in the example bellow where 0 is the index of the start row and 15 the index of the end row. If no cell range is specified the whole column is used for the conversion.


m2 = table("Table1").convertToMatrixRegularXYZ(2, 0, 15)

You can also convert a random XYZ data table into a matrix ("random" meaning that cells in the X and Y columns of the table do not define a regular 2D grid). In the example bellow "Data" is the name of the Z column, 0 is the number of the start row and 100 is the index of the end row. The resulting matrix will be 400 rows by 500 columns. The last parameter (the radius) is used by the modified Shepard interpolation algorithm to select data points: only the closest 3D nodes whithin the user-specified radius will be used.


m1 = table("Table1").convertToMatrixRandomXYZ("Data", 0, 100, 400, 500, 2.5)
The last five arguments of this method are optional and have "reasonable" default values. You only need to specify the name or the number of the Z column like in the examples bellow:

t = table("Table1")
m1 = t.convertToMatrixRandomXYZ("Data")
m2 = t.convertToMatrixRandomXYZ(2)

Stem Plots

A stem-plot (or stem-and-leaf plot), in statistics, is a device for presenting quantitative data in a graphical format, similar to a histogram, to assist in visualizing the shape of a distribution. A basic stem-plot contains two columns separated by a vertical line. The left column contains the stems and the right column contains the leaves. See Wikipedia for more details.

QtiPlot provides a text representation of a stem-plot. The following function returns a string of characters representing the statistical analysis of the data:


text = stemPlot(Table *t, columnName, power = 1001, startRow = 0, endRow = -1)
where the power variable is used to specify the stem unit as a power of 10. If this parameter is greater than 1000 (the default behavior), than QtiPlot will try to guess the stem unit from the input data and will pop-up a dialog asking you to confirm the automatically detected stem unit.

Once you have created the string representation of the stem-plot, you can display it in any text editor you like: in a note within the project or even in the results log:


resultsLog().append(stemPlot(table("Table1"), "Table1_2", 1, 2, 15))

2D Plots

Graph windows usually display several plot layers. It is possible to get a reference to the active layer:

l = g.activeLayer()
but you can also select a layer by its number:

l = g.layer(num)
The default background color of a graph is white, but it is possible to fill it with a different background color or with a linear gradient brush, using:

g = graph("Graph1")
g.setBackgroundColor(QtGui.QColor(Qt.gray))
g.setGradientBrush(2, 100) # one color gradient
g.setGradientBrush(3, QtGui.QColor(Qt.blue)) # two colors gradient

The plot title


l.setTitle("My beautiful plot")
l.setTitleFont(QtGui.QFont("Arial", 12))
l.setTitleColor(Qt.red)
l.setTitleAlignment(QtCore.Qt.AlignLeft)
The alignment parameter can be any combination of the Qt alignment flags (see the Qt documentationfor more details).

If you want you can remove the plot title using:


l.removeTitle()

You can access the plot title properties using the following getter functions:


print l.titleString()
font = l.titleFont()
color = l.titleColor()
alignment = l.titleAlignment()
Here's how you can add greek symbols in the plot title or in any other text in the plot layer: axis labels, legends:

l.setTitle("normal text <font face=\"Symbol\">greek text</font>")

Using the font specifications, you can also change the color of some parts of the title only:

l=newGraph().activeLayer()
l.setTitle("<font color = red>red</font> <font color = yellow>yellow</font> <font color = blue>blue</font>")

Customizing the axes

Layer axes can be shown/hidden using the following function:


l.enableAxis(int axis, on = True)

where axis can be any integer value between 0 and 3 or the equivalent reserved word:

0.

Layer.Left

1.

Layer.Right

2.

Layer.Bottom

3.

Layer.Top

The X-Y axes of a plot layer can be exchanged using the following function:

l.exchangeXYAxes()

The layout of the axes can be customized using the function bellow:


l.setAxesLayout(Layer.BottomVerticalLayout, 50, 30)

There are four predefined axes layouts available:

Layer.SideAxesLayout (0)

It is the classical layout, the axes are displayed alongside the canvas drawing area.

Layer.CrossingAxesLayout (1)

Two crossing axes, one vertical and one horizontal, are drawn inside the layer canvas. Their positions can be customized using the 2nd and 3rd arguments of the setAxesLayout() method as a percentage of the canvas size.

Layer.BottomVerticalLayout (2)

Two axes are displayed: the bottom axis and a vertical axis inside the layer canvas. The position of the vertical axis can be customized using the 3rd argument of the setAxesLayout() method which represents a percentage of the length of the bottom axis.

Layer.LeftHorizontalLayout (3)

Two axes are displayed: the left axis and a horizontal axis inside the layer canvas. The position of the horizontal axis can be customized using the 2nd argument of the setAxesLayout() method which represents a percentage of the length of the left axis.

If an axis is enabled, you can fully customize it via a Python script. For example you can set its title:

l.setAxisTitle(Layer.Bottom, "time (ms)")
l.setAxisTitleFont(Layer.Bottom, QtGui.QFont("Arial", 11))
l.setAxisTitleColor(Layer.Bottom, Qt.blue)
l.setAxisTitleAlignment(Layer.Bottom, Qt.AlignRight)
l.setAxisTitleDistance(Layer.Bottom, 20)
its color and the font used for the tick labels:

l.setAxisColor(Layer.Left, Qt.green)
l.setAxisFont(Layer.Left, QtGui.QFont("Arial", 10))

You can access the axis title properties using the following getter functions:


print l.axisTitleString(Layer.Bottom)
font = l.axisTitleFont(Layer.Top)
color = l.axisTitleColor(Layer.Right)
alignment = l.axisTitleAlignment(Layer.Left)
dist = l.axisTitleDistance(Layer.Left)

The other axis properties can be retrieved using the following methods:


font = l.axisFont(Layer.Top)
color = l.axisColor(Layer.Right)
formula = l.axisFormula(Layer.Bottom)
The tick labels of an axis can be enabled or disabled, you can set their color and their rotation angle:

l.enableAxisLabels(axis, on = True)
l.setAxisLabelsColor(Layer.Bottom, Qt.red)
l.setAxisLabelRotation(Layer.Bottom, 90)
anglecan be any integer value between -90 and 90 degrees.

The properties of the axis labels can be retrieved using:


color = l.axisLabelsColor(Layer.Left)
angle = l.axisLabelsRotation(Layer.Left)

The numerical format of the labels can be set using:


l.setAxisNumericFormat(axis, format, precision = 6, formula)

where format can have the following values:

0.

Automatic: the most compact numeric representation is chosen.

1.

Decimal: numbers are displayed in floating point form.

2.

Scientific lower case exponential notation, e.g.: 1.0e+04.

3.

Superscripts: like Scientific, but the exponential part is displayed as a power of 10.

4.

Engineering format, e.g: 10k.

5.

Superscripts: like Superscripts above, but the multiplication sign is replaced by a dot sign.

6.

Scientific upper case exponential notation, e.g.: 1.0E+04.

precision is the number of significant digits and formula is a mathematical expression that can be used to link opposite scales. It's argument must be x for horizontal axes and y for vertical axes. For example, assuming that the bottom axis displays a range of wavelengths in nanometers and that the top axis represents the equivalent energies in eV, with the help of the code below all the wavelengths will be automatically converted to electron-volts and the result will be displayed in floating point form with two significant digits after the decimal dot sign:


l.setAxisNumericFormat(Layer.Top, 1, 2, "1239.8419/x")
print l.axisFormula(Layer.Top)

The axis ticks can be customized via the following functions:


l.setTicksLength(minLength, majLength)
print l.minorTickLength()
print l.majorTickLength()

l.setMajorTicksType(axis, majTicksType)
l.setMinorTicksType(axis, minTicksType)
l.setAxisTicksLength(axis, majTicksType, minTicksType, minLength, majLength)

where the majTicksType and minTicksType parameters specify the desired orientation for the major and minor ticks, respectively:

0.

Layer.NoTicks

1.

Layer.Out: outward orientation for ticks, with respect to the plot canvas

2.

Layer.InOut: both inward and outward ticks

3.

Layer.In: inward ticks

minLength specifies the length of the minor ticks, in pixels and majLength the length of the major ticks.

It is possible to define custom labels for the major ticks of an axis scale. The ticks are identified by their numerical values and it is also possible to specify a custom color for these labels, see the example below:


l = newGraph().activeLayer()
l.setAxisSpecialTickLabel(Layer.Bottom, 400.0, "test1")
l.setAxisSpecialTickLabel(Layer.Left, 600.0, "test2", Qt.red)

These "special" tick labels can be removed like shown below:


l = graph("Graph1").activeLayer()
l.removeSpecialTickLabels(Layer.Bottom)
l.removeSpecialTickLabels(Layer.Left)
l.repaint()

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 type specifies the desired scale type:

0.

Layer.Linear

1.

Layer.Log10

2.

Layer.Ln

3.

Layer.Log2

4.

Layer.Reciprocal

5.

Layer.Probability

6.

Layer.Logit

and 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.

It is possible to access the scale type of an axis via the following "getter" function:


print l.axisScaleType(Layer.Bottom)

The type of an axis (Numerical, Day, Month, Date/Time, etc...) can be obtained using:


print l.axisType(Layer.Bottom)

It is possible to get information about the scale division of an axis via the following functions:


div = graph("Graph1").activeLayer().axisScaleDiv(Layer.Left)
print div.lowerBound()
print div.upperBound()
print div.range()
Defining a scale range for an axis doesn't automatically disable autoscaling. This means that if a curve is added or removed from the layer, the axes will still automatically adapt to the new data interval. This can be avoided by disabling the autoscaling mode, thus making sure that your scale settings will always be taken into account:

l.enableAutoscaling(False)
l.setSynchronizedScaleDivisions(False)
If you want to rescale the plot layer so that all the data points are visible, you can use the following utility function:

l.setAutoScale()
The same setScalefunction above, with a longer list of arguments, can be used to define an axis break region:

l.setScale(axis, start, end, step=0.0, majorTicks=5, minorTicks=5, type=0, inverted=False,
	left=-DBL_MAX, right=DBL_MAX, breakPosition=50, stepBeforeBreak=0.0, stepAfterBreak=0.0,
	minTicksBeforeBreak=4, minTicksAfterBreak=4, log10AfterBreak=False, breakWidth=4, breakDecoration=True)
where leftspecifies the left limit of the break region, rightthe right limit, breakPositionis the position of the break expressed as a percentage of the axis length and breakWidthis the width of the break region in pixels. The names of the other parameters should be self-explanatory.

You can also invert the scale of an axis using the following direct method:


l.invertScale(Layer.Bottom)
print l.hasInvertedScale(Layer.Bottom)

Finally, you can specify the width of all axes and enable/disable the drawing of their backbone line, using:


l.setAxesLinewidth(2)
l.drawAxesBackbones(True)

The canvas

You can display a rectangular frame around the drawing area of the plot (the canvas) and fill it with a background color, a linear gradient brush or with an image, using:

l.setCanvasFrame(2, QtGui.QColor("red"))
l.setCanvasColor(QtGui.QColor("lightGrey"))
l.setCanvasGradientBrush(3, QtGui.QColor(Qt.blue))
l.setCanvasBackgroundImage("C:/qtiplot/qtiplot/qtiplot_logo.png")
As you have seen above it is possible to fill the canvas with a linear gradient brush. There are sixteen predefined gradient types. The first argument of the "setter" functions is the gradient type and must be a value between 0 and 15. The second argument can be either a color or an integer value. If the second argument is a color the linear gradient interpolates between the canvas background color and the specified color. If you specify an integer value (the lightness) the second color of the linear gradient is a lighter/darker color with respect to the background color. The lightness must be an integer value between 0 (black) and 100 (white).

l.setCanvasGradientBrush(3, QtGui.QColor(Qt.blue))
l.setCanvasGradientBrush(3, 90)
The following access methods are available for the canvas background image:

pic = l.backgroundPixmap() # QPixmap
path = l.canvasBackgroundFileName()
Drawing the canvas frame and disabling the axes backbone lines is the only possible solution for the issue of axes not touching themselves at their ends.

The layer frame

You can display a rectangular frame around the whole layer and fill it with a background color or with a linear gradient brush, using:

l.setFrame(2, QtGui.QColor("blue"))
l.setBackgroundColor(QtGui.QColor("grey"))
l.setGradientBrush(3, QtGui.QColor(Qt.blue))
The default spacing between the layer frame and the other layer elements (axes, title) can be changed via:

l.setMargin(10)

Customizing the grid

You can display or hide the grid associated to a layer axis or the whole grid using the functions bellow:

l.showGrid()
l.hideGrid()
l.showGrid(axis)
l.hideGrid(axis)
l.setGridOnTop(on = True, update = True) # draw grid on top of data
This will display the grid with the default color, width and pen style settings. If you need to change these settings, as well as to enable/disable certain grid lines, you can use the following functions:

grid = l.grid()
grid.setMajPenX(QtGui.QPen(QtCore.Qt.red, 1))
grid.setMinPenX(QtGui.QPen(QtCore.Qt.yellow, 1, QtCore.Qt.DotLine))
grid.setMajPenY(QtGui.QPen(QtCore.Qt.green, 1))
grid.setMinPenY(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.DashDotLine))
grid.enableXMaj()
grid.enableXMin()
grid.enableYMaj(False)
grid.enableYMin(False)
grid.enableZeroLineX(True)
grid.enableZeroLineY(False)
grid.setXZeroLinePen(QtGui.QPen(QtCore.Qt.black, 2))
grid.setYZeroLinePen(QtGui.QPen(QtCore.Qt.black, 2))
l.replot()
All the grid functions containing an Xrefer to the vertical grid lines, whereas the Yletter indicates the horizontal ones. Also, the Majword refers to the main grid lines and Minto the secondary grid.

The plot legend

Plot legends can be accessed as shown in the script bellow. You should be aware of the fact that not all plot layers have legends. If the legend object was deleted, the instance returned by the legend() function might be null:


layer = graph("Graph1").activeLayer()
legend = layer.legend()
if legend: # the current legend might be NULL
	legend.setOrigin(100, 80)
else:
	print "This layer doesn't have a legend!"

You can add a new legend to a plot using:


legend = l.newLegend()
#or
legend = l.newLegend("enter your text here")

The legend of a plot layer can be removed using:


l.removeLegend()

Plot legends are special text objects which are updated each time you add or remove a curve from the layer. They have a special auto-update flag which is enabled by default. The following function returns True for a legend object:


legend.isAutoUpdateEnabled()
You can disable/enable the auto-update behavior of a legend/text object using:

legend.setAutoUpdate(False/True)
You can add common texts like this:

text = l.addText(legend)
text.setOrigin(legend.x(), legend.y()+50)
Please notice that the addTextfunction returns a different reference to the new text object. You can use this new reference later on in order to remove the text:

l.remove(text)
Once you have created a legend/text, it's very easy to customize it. If you want to modify the text you can use:

legend.setText("Enter your text here")
All other properties of the legend: rotation angle, text color, background color, frame style, font and position of the top-left corner can be modified via the following functions:

legend.setAngle(90)
legend.setTextColor(QtGui.QColor("red"))
legend.setBackgroundColor(QtGui.QColor("yellow"))
legend.setFrameStyle(Frame.Shadow)
legend.setFrameColor(QtCore.Qt.red)
legend.setFrameWidth(3)
legend.setFrameLineStyle(QtCore.Qt.DotLine)
legend.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold, True))
# set top-left position using scale coordinates:
legend.setOriginCoord(200.5, 600.32)
# or set top-left position using pixel coordinates:
legend.setOrigin(5, 10)
legend.repaint()
Some of the properties of the legend can be accessed via the following "getter" functions:

angle = legend.angle()
color = legend.textColor()
font = legend.font()
x = legend.xValue()# top position in scale coordinates
y = legend.yValue()# left position in scale coordinates
Other frame styles available for legends are: Legend.Line, which draws a rectangle around the text and Legend.None(no frame at all). There is also a function allowing you to add an automatically built time stamp:

timeStamp = l.addTimeStamp()

Text objects have a default name, which can be customized:


t = graph("Graph1").activeLayer().text(0)
print(t.name())
t.setName("Text1")
print(t.name())

It is possible to get a reference to a text object using its index in the list of texts or its name:


l = graph("Graph1").activeLayer()
t1 = l.text(0)
t2 = l.text("Legend2")

If a text object is already selected in a plot layer, it is possible to get a reference to it like shown below:


l = graph("Graph1").activeLayer()
t = l.activeText()
t.setText("My Text")
l.replot()

Finally, it is possible to iterate over the list of all texts/legends in a plot layer:


layer = graph("Graph1").activeLayer()
for t in layer.textsList():
	print(t.text())

Antialiasing

Antialiasing can be enabled/disabled for the drawing of the curves and other layer objects, but it is a very resources consuming feature:

l.setAntialiasing(True, bool update = True)

Resizing layers

A layer can be resized using the methods below, where the first argument is the new width, the second is the new height and sizes are defined in pixels:

l.resize(200, 200)
l.resize(QSize(w, h))
If you also need to reposition the layer, you can use the following functions, where the first two arguments specify the new position of the top left corner of the canvas:

l.setGeometry(100, 100, 200, 200)
l.setGeometry(QRect(x, y, w, h))
By default, when resizing 2D plot windows, QtiPlot adapts the sizes and aspect of the layers to the new size of the plot window. You can override this behavior and keep the aspect and size of the graph layers unchanged:

g = newGraph("test", 2, 1, 2)
g.arrangeLayers()
g.setScaleLayersOnResize(False)
Also, the default behavior of 2D plot layers with respect to the resizing of the graph window is to adapt the sizes of the fonts used for the various texts, to the new size of the plot window. You can override this behavior and keep the size of the fonts unchanged:

l.setAutoscaleFonts(False)

Resizing the drawing area

The drawing area of a layer (the canvas) can be resized using the methods below, where the first argument is the new width, the second is the new height and sizes are defined in pixels:

l.setCanvasSize(200, 200)
l.setCanvasSize(QSize(w, h))
If you also need to reposition the canvas, you can use the following functions, where the first two arguments specify the new position of the top left corner of the canvas:

l.setCanvasGeometry(100, 100, 200, 200)
l.setCanvasGeometry(QRect(x, y, w, h))
Please keep in mind that the fonts of the layer are not rescaled when you resize the layer canvas using the above methods.

Working with 2D curves

If you want to create a new Graph window for some data in table Table1, you can use the plot command:

t = table("Table1")
g = plot(t, column, type)
columncan be either the name of the column (string value) or the column index (integer value) and typespecifies the desired plot type and can be one of the following numbers or the equivalent reserved word:

0

Layer.Line

1

Layer.Scatter

2

Layer.LineSymbols

3

Layer.VerticalBars

4

Layer.Area

5

Layer.Pie

6

Layer.VerticalDropLines

7

Layer.Spline

8

Layer.HorizontalSteps

9

Layer.Histogram

10

Layer.HorizontalBars

13

Layer.Box

15

Layer.VerticalSteps

23

Layer.BSpline

24

Layer.Bezier

33

Layer.AkimaSpline

39

Layer.IntervalPlot

40

Layer.Violin

41

Layer.ViolinBox

42

Layer.ScatterInterval

43

Layer.BoxDataOverlap

44

Layer.Ridgeline

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

g1 = plot(table("Table1"), (2,4,7), 2)
g2 = plot(table("Table1"), ("Table1_2","Table1_3"), Layer.LineSymbols)
There are some curve types that specifically request at least two Y columns, the first column in the tuple being used as baseline:

26

Layer.FloatingColumn

27

Layer.FloatingBar

29

Layer.FillArea


l.addCurves(table("Table1"), (2, 3), Layer.FloatingColumn)
l.addCurves(table("Table1"), (2, 3), Layer.FillArea)
Some predefined plot types that can be used to stack the plot curves by adding an offset to their Y values:

21

Layer.StackBar

22

Layer.StackColumn

25

Layer.StackArea

28

Layer.StackLine

30

Layer.StackBarPercent

31

Layer.StackColumnPercent

32

Layer.StackAreaPercent


l.addCurves(table("Table1"), (2, 3), Layer.StackColumn)
The stack offset can be customized using one of the following modes:

0

Layer.NoOffset

1

Layer.CumulativeOffset

2

Layer.ConstantOffset

3

Layer.AutoOffset

4

Layer.IndividualOffset


l.addCurves(table("Table1"), (2, 3), Layer.StackLine)
l.stackCurves(Layer.AutoOffset, 0.1)
In the example above the relative gap between the plot curves was set to 10% (0.1). The offset can be set to a constant value if the stack mode is set to Layer.ConstantOffset:

l.addCurves(table("Table1"), (2, 3), Layer.Line)
l.stackCurves(Layer.ConstantOffset, 1000)
or you can specify a different offset for each plot curve:

l.addCurves(table("Book1"), ("B", "C"), Layer.Area)
l.stackCurves(Layer.IndividualOffset)
c = l.dataCurve(1)
c.setOffset(2, 500)
print c.xOffset(), c.yOffset()
In the case of the following plot types: StackAreaPercent, StackBarPercentand StackColumnPercentthe stack mode is set to CumulativeOffsetand the data values are percent normalized. A special hasPercentNormalizedStackflag is set for these plot types:

print l.hasPercentNormalizedStack()
which of course can be disabled:

l.setPercentNormalizedStack(False)
l.stackCurves(Layer.CumulativeOffset)
You can add or remove curves to or from a plot layer:

l.insertCurve(table, Ycolumn, type=Layer.Scatter, int startRow = 0, int endRow = -1)# returns a reference to the inserted curve
l.insertCurve(table, Xcolumn, Ycolumn, type=Layer.Scatter, int startRow = 0, int endRow = -1)# returns a reference to the inserted curve
l.addCurve(table, column, type=Layer.Line, lineWidth = 1, symbolSize = 3, startRow = 0, endRow = -1)# returns True on success
l.addCurves(table, (2,4), type=Layer.Line, lineWidth = 1, symbolSize = 3, startRow = 0, endRow = -1)# returns True on success
l.removeCurve(curveName)
l.removeCurve(curveIndex)
l.removeCurve(curveReference)
l.deleteFitCurves()
It is possible to copy all the curves from a source plot layer, lSrc, to a destination layer lDst:

lSrc = graph("Graph1").activeLayer()
lDst = newGraph().activeLayer()
lDst.copyCurves(lSrc)
Of course, you can also fully clone an entire plot layer:

lDst.copy(lSrc)
Please note that it is possible to copy only the format of a plot layer without affecting the data source of the curves:

lDst.copy(lSrc, False) # copy only the format of the curves, not the data
It is possible to change the order of the curves inserted in a layer using the following functions:

l.changeCurveIndex(int oldIndex, int newIndex)
l.reverseCurveOrder()
You can also change the Z order of the curves:

l.switchCurveZ(int oldIndex, int newIndex);
l.reverseCurveZOrder()
If the table column used to create a plot curve has empty cells, you can choose whether the curve line is drawn connected across the missing data or not. This behavior can be specified using:

l.showMissingDataGap(on = True, replot = True)
Sometimes, when performing data analysis, one might need the curve title. It is possible to obtain it using the method below:

title = l.curveTitle(curveIndex)
It is possible to get a reference to a curve on the layer l using it's index or it's title, like shown below:

c = l.curve(curveIndex)
c = l.curve(curveTitle)
dc = l.dataCurve(curveIndex)

Please, keep in mind the fact that the above methods might return an invalid reference if the curve with the specified index/title is not a PlotCurve or a DataCurve object, respectively. For example, an analytical function curve is a PlotCurve but not a DataCurve and spectrograms are a completely different type of plot items which are neither PlotCurves nor DataCurves.

Once you have a reference to a PlotCurve cit is possible to get its type and title using the methods bellow:

c = graph("Graph1").activeLayer().curve(0)
print c.type()
print c.name()

Use the following functions to change the axis attachment of a curve:


l.setCurveAxes(index, xAxis, yAxis)
c.setXAxis(xAxis)
c.setYAxis(yAxis)

where index is the index of the curve (starting at zero), xAxis can be either Layer.Bottom or Layer.Top and yAxis can be either Layer.Left or Layer.Right:


l.setCurveAxes(0, Layer.Top, Layer.Right)# modify the first curve in the layer
c.setXAxis(Layer.Bottom)
c.setYAxis(Layer.Right)#attach curve c to the right Y axis

The following functions give information about the axes to which a curve is attached:


print c.xAxis()
print c.yAxis()
In case you need the number of curves on a layer, you can get it with

l.numCurves()
It is possible to change the number of symbols to be displayed for a curve using the function below. This option can be very usefull for very large data sets:

c.setSkipSymbolsCount(3)
print c.skipSymbolsCount()
Once you have added a curve to a plot layer you can fully customize it's appearance:

l = newGraph().activeLayer()
l.setAntialiasing()
c = l.insertCurve(table("Table1"), "Table1_2", Layer.LineSymbols)
c.setPen(QPen(Qt.red, 3))
c.setBrush(QBrush(Qt.darkYellow))
c.setFillAreaColor(Qt.green)
c.setGradientBrush(3, QColor(Qt.blue))
c.setSymbol(PlotSymbol(PlotSymbol.Hexagon,QBrush(Qt.cyan),QPen(Qt.blue,1.5),QSize(15,15)))
c.setLineStyle(PlotCurve.AkimaSpline)
A curve can be customized using one of the following line styles:

0

PlotCurve.NoCurve: the line is not drawn

1

PlotCurve.Lines: a simple line

2

PlotCurve.Sticks: vertical drop lines

3

PlotCurve.HorizontalSteps: a horizontal stepped line

4

PlotCurve.Dots: the line is drawn as a series of dots

5

PlotCurve.Spline: a normal spline

6

PlotCurve.VerticalSteps: a vertical stepped line

7

PlotCurve.BSpline:a B-spline (or basis spline)

8

PlotCurve.Bezier: Bezier spline

9

PlotCurve.AkimaSpline: the line is drawn as an Akima interpolation

An alternative way of customizing a curve in terms of color, line width and line style is by using the functions below, where the first argument is the curve index:

l.setCurveLineColor(0, QtGui.QColor(Qt.red))
# or set color by its index in the default color list: 0=black, 1=red, 2=green,...:
l.setCurveLineColor(0, 1)
l.setCurveLineStyle(0, Qt.DashLine)
l.setCurveLineWidth(0, 0.5) # set the linewidth to 0.5
Here's a short script showing these functions at work:

t = newTable("test", 30, 4)
for i in range(1, t.numRows()+1):
	t.setCell(1, i, i)
	t.setCell(2, i, i)
	t.setCell(3, i, i+2)
	t.setCell(4, i, i+4)

l = qti.app.plot(t, (2,3,4), Layer.Line).activeLayer() # plot columns 2, 3 and 4
for i in range(0, l.numCurves()):
	l.setCurveLineColor(i, 1 + i)
	l.setCurveLineWidth(i, 0.5 + i)
	l.setCurveLineStyle(1, 1 + i)
It is possible to define a global color policy for the plot layer using the following convenience functions:

l.setGrayScale()
# use colors from a linear gradient
l.setGradientColorScale(Qt.yellow, Qt.cyan)
# use colors from the default color list: 0 = black, 1 = red, 2 = green, etc...:
l.setIndexedColors()
# use a linear color map
map = LinearColorMap.rainbowMap()
map.setIntensityRange(1, l.curveCount())
l.setColorMap(map)
Other useful functions allowing to edit the style of all the curves in a plot layer with a single call are:

l.incrementLineStyle()
l.incrementFillPattern()
If you need to change the range of data points displayed in a DataCurve you can use the following methods:

c.setRowRange(int startRow, int endRow)
c.setFullRange()
Also, you can hide/show a plot curve via:

c.setVisible(bool on)

Curve baseline

When you define a filling brush for the area beneath a plot curve the baseline of the filling area is by default the line y = 0. Nevertheless you can specify a different y value to be the baseline reference:

c = newGraph().activeLayer().addFunction("sin(x)", 0, 2*pi, 100)
c.setBrush(QColor(255, 0, 0, 100))
c.setBaseline(0.5)
print c.baseline()
You can also specify a different plot curve to act as a custom baseline:

l = newGraph().activeLayer()
c1 = l.addFunction("sin(x)", 0, 2*pi, 100)
c2 = l.addFunction("cos(x)", 0, 2*pi, 100)
c2.setBrush(QBrush(Qt.darkYellow))
c2.setBaselineCurve(c1)
print c2.baselineCurve().name()
In the case of DataCurves you can also specify a data column as the custom baseline:

c.setBaselineColumn("Table1_2") # the data source of curve c is table "Table1"
print c.baselineColumnName()

Curve values

In case you need information about the data stored in a plot curve, the functions bellow return Python lists containing the abscissas and the ordinates of all the data points:

abscissaList = c.xValues()
ordinateList = c.yValues()
Or you have at your disposal functions that give access to the values of the individual data points:

points = c.dataSize()
for i in range (0, points):
	print i, "x = ", c.x(i), "y = ", c.y(i)
You can also get information about the range (minimum and maximum values) of the data stored in a 2D plot curve:

print c.minXValue()
print c.maxXValue()
print c.minYValue()
print c.maxYValue()
It is also possible to get references to the data table and data column objects used to create a DataCurve. Please note that the reference to the column containing the abscissas may be null for some data curves:

dc = graph("Graph1").activeLayer().dataCurve(0)
t = dc.table()
xCol = dc.xColumn() # abscissas column
yCol = dc.yColumn() # ordinates column
print xCol.index(), xCol.fullName(), yCol.index(), yCol.fullName()
print t.objectName()

Curve symbols

Here's how you can customize the plot symbol used for a 2D plot curve c:

s = c.symbol()
s.setSize(QtCore.QSize(7, 7))# or s.setSize(7)
s.setBrush(QtGui.QBrush(Qt.darkYellow))
s.setPen(QtGui.QPen(Qt.blue, 3))
s.setStyle(PlotSymbol.Diamond)
l.replot() # redraw the plot layer object
The symbol styles available in QtiPlot are:

-1

PlotSymbol.NoSymbol

0

PlotSymbol.Ellipse

1

PlotSymbol.Rect

2

PlotSymbol.Diamond

3

PlotSymbol.Triangle

4

PlotSymbol.DTriangle

5

PlotSymbol.UTriangle

6

PlotSymbol.LTriangle

7

PlotSymbol.RTriangle

8

PlotSymbol.Cross

9

PlotSymbol.XCross

10

PlotSymbol.HLine

11

PlotSymbol.VLine

12

PlotSymbol.Star1

13

PlotSymbol.Star2

14

PlotSymbol.Hexagon

15

PlotSymbol.Pentagon

16

PlotSymbol.Star3

If the the shape of the symbol is an ellipse (a circle in fact), the setCircleSizeTypemethod lets you choose how the area of the symbols is determined from their defined size. Three options are available:

0

PlotCurve.BasedOnSquare: The area is calculated as the area of the square enclosing the circle (A = Size*Size).

1

PlotCurve.BasedOnDiameter: The diameter of the circle equals the value defined for Size (A = Pi*Size*Size/4).

2

PlotCurve.BasedOnArea: The area of the circle equals the value defined for the Size (A = Size).


g = graph("Graph1").activeLayer()
c = g.curve(0)
c.setCircleSizeType(PlotCurve.BasedOnArea)
g.replot()
It is worth knowing that you can define a custom image as the plot symbol for a curve:

g = newGraph().activeLayer()
c = g.addFunction("cos(x)", 0, 10, 20)
c.setSymbol(ImageSymbol("qtiplot/manual/html/icons/help.png"))
Here's a short script showing how to draw a custom plot symbol and assign it to a curve:

pix = QtGui.QPixmap(QtCore.QSize(11, 11))
pix.fill(Qt.transparent)
p = QtGui.QPainter(pix)
r = QtCore.QRect(0, 0, 10, 10)
p.drawEllipse(r)
p.setPen(QtGui.QPen(Qt.red))
p.drawLine(5, 0, 5, 10)
p.drawLine(0, 5, 10, 5)
p.end()

g = newGraph().activeLayer()
c = g.addFunction("sin(x)", 0, 10, 20)
c.setSymbol(ImageSymbol(pix))
You can also use Unicode characters as plot symbols for a curve:

g = newGraph().activeLayer()
c = g.addFunction("sin(x)", 0, 10, 20)
symbol = UnicodeSymbol(0x273D)
symbol.setFontSize(22)
symbol.setPen(Qt.red)
c.setSymbol(symbol)
It is also possible to define variable sizes for the symbols of a plot curve by indexing the cell values from a table column. This special column can be specified via the setSymbolSizeColumnmethod. Additionally you may set a scaling factor for the symbol size like in the small script bellow:

t = table("Table1")
g = qti.app.plot(t, "Table1_2", Layer.Scatter).activeLayer()
c = g.curve(0)
c.setSymbolSizeColumn(t.column(3))
c.setSymbolScalingFactor(15)
print c.symbolSizeColumn().fullName(), c.symbolScalingFactor()
An equivalent way of obtaining the same result as with the script above is to use the specialized function plotBubbles. You need to specify two Y columns as argument of this function. The first Y column in the tuple is used for the positions of the plot symbols and the next one determines their sizes, see the example bellow:

c = newGraph().activeLayer().plotBubbles(table("Table1"), ["2", "3"])
print c.symbolSizeColumn().fullName(), c.symbolScalingFactor()
It is also possible to define a default color map for the plot symbols using the values from a table column. Both the filling color and the edge color can be customized, using the functions bellow:

g = graph("Graph1").activeLayer()
c = g.curve(0)
t = table("Table1")
c.setSymbolEdgeColorColumn(t.column(2))
c.setSymbolFillColorColumn(t.column(3))
g.replot()

print c.symbolEdgeColorColumn().fullName(), c.symbolFillColorColumn().fullName()
The default color map of a plot curve can be modified like shown in the script bellow:

g = graph("Graph1").activeLayer()
c = g.curve(0)
map = c.colorMap()
map.setColorInterval(Qt.blue, Qt.yellow)
map.addColorStop(0.5, Qt.red)
c.setColorMap(map)
g.replot()
The convenience function setColorMapIntensityRangeFromColumncan be used in order to modify the intensity range of the color map. This function finds the minimum and maximum values in the specified column and confines the intensity range between this interval:

g = graph("Graph1").activeLayer()
c = g.curve(0)
c.setColorMapIntensityRangeFromColumn(table("Table1").column(3))
g.replot()
The specialized functions plotColorMappedSymbolsand plotColorMappedBubblescan also be used in order to get a 2D plot curve displaying color mapped symbols. You need to specify at least two Y columns for these functions: the first is used for the positions of the plot symbols and the second determines their color. QtiPlot finds the minimum and maximum values in the second Y column, creates eight evenly sized ranges of values between the minimum and maximum values, and then associates a color with each range of values. The color of each data point is determined by finding the color associated with the second Y column value in the color map.

g = newGraph().activeLayer()
c = g.plotColorMappedSymbols(table("Table1"), ["2", "3"])
c.symbol().setSize(10)

The plotColorMappedBubbles function also accepts a tuple of three columns: the first is used for the positions of the plot symbols, the second determines their sizes and the third the color of their edges. This function automatically sets the filling color of the plot symbols to white.


g = newGraph().activeLayer()
c = g.plotColorMappedBubbles(table("Table1"), ["2", "3", "4"])
print c.symbolScalingFactor()
Alternatively, in order to get the same graphs, you can use the addCurvesfunction and the following curve types:

34

Layer.Bubble

34

Layer.ColorMappedSymbols

36

Layer.ColorMappedBubbles

But instead of a direct reference to the newly created plot curve this function returns a boolean result:

g = newGraph().activeLayer()
ok = g.addCurves(table("Table1"), (2, 3, 4), Layer.ColorMappedSymbols, 0, 10)
print ok # prints True on success

Curve labels

It is possible to display labels for each data point in a DataCurve. The labels can be set using the method bellow:

c.setLabelsForm(DataCurve.YValues)
and may have one of the following predefined forms:

0

DataCurve.XValues

1

DataCurve.YValues

2

DataCurve.RowIndices

3

DataCurve.XYValues

You may also specify the labels using the texts in a table column:

c.setLabelsColumnName("Table1_2")
If the labels have numerical values, you can change their display format and precision:

c.setLabelsNumericFormat(2, 3)# display labels with scientific format and three significant digits
c.setLabelsNumericPrecision(2)
The available formats are:

0.

Automatic: the most compact numeric representation is chosen

1.

Decimal: numbers are displayed in floating point form (10.000)

2.

Scientific: numbers are displayed using the exponential notation (1e4)

The position of the labels can be set using the method bellow:

c.setLabelsAlignment(DataCurve.Right)
The following predefined positions can be used:

0

DataCurve.Center

1

DataCurve.Left

2

DataCurve.Right

3

DataCurve.Above

4

DataCurve.Bellow

If the data curve is a column/bar/histogram curve the following predefined positions should be used instead:

0

DataCurve.Center

1

DataCurve.InsideEnd

2

DataCurve.InsideBase

3

DataCurve.OutsideEnd

Other properties of the labels can be customized using the functions bellow:

c.setLabelsOffset(50, 50)
c.setLabelsColor(Qt.red)
c.setLabelsFont(QtGui.QFont("Arial", 14))
c.setLabelsRotation(45)
You can disable the labels of a data curve using:

c.clearLabels()
l.replot() # redraw the plot layer object

Curve drop shadow

It is possible to enable/disable the display of drop shadows for all curves in a plot layer at once:

g = graph("Graph1").activeLayer()
# set shadow color to light gray, offset to 5 pixels and blur radius to 8
g.setDropShadowProperties(Qt.lightGray, 5, 8.0)
g.enableCurveDropShadows(True)
It is equally possible to enable/disable the drop shadow effect for each curve individually:

g = graph("Graph1").activeLayer()
c = g.curve(0)
if (c.hasDropShadow()):
	c.enableDropShadow(False)
g.replot()

Selecting a data range

You can activate the "Select Data Range" tool for a plot layer and select the active curve using the following method:

g = graph("Graph1").activeLayer()
g.enableRangeSelectors()
g.setSelectedCurve(g.curve(0))
g.setActivePoint(20)
g.switchActiveMarker()
g.setActivePoint(90)
g.replot()
If a plot curve is selected you can get the coordinates of the selected data points like shown bellow:

c = g.selectedDataCurve()
ap = g.selectionActivePoint()
ip = g.selectionInactivePoint()
print c.x(ap), c.y(ap), c.x(ip), c.y(ip)
If the selected plot curve is also a DataCurve, meaning that it has a valid data source table, unlike analytical function curves, you can have access to the row indices of the selected data points from the source table, using the tableRowmethod, like shown in the script bellow. Please note that if the selected curve is an analytical function curve and you call the tableRowmethod this will result in a crash.

c = g.selectedDataCurve()
print c.tableRow(g.selectionActivePoint()) + 1
print c.tableRow(g.selectionInactivePoint()) + 1
All 2D plot tools can be disabled using:

g.disableTools()

Analytical Functions

You can also add analytical function curves to a plot layer:

f = l.addFunction("x*sin(x)", 0, 3*pi, points = 100)
f.setTitle("x*sin(x)")
f.setPen(Qt.green)
f.setBrush(QtGui.QColor(0, 255, 0, 100))

l.addParametricFunction("cos(m)", "sin(m)", 0, 2*pi, points = 100, variableName = "m")
l.addPolarFunction("t", "t", 0, 2*pi, points = 100, variableName = "t")
It is possible to get a reference to an analytical function curve on the layer l using it's index, like shown below:

f = l.functionCurve(curveIndex)
When dealing with analytical function curves, you can customize them using the following methods:

f.setRange(0, 2*pi)
f.setVariable("t")
f.setComment("My function")
f.setFormulas("sin(t)", "cos(t)")
f.setFunctionType(FunctionCurve.Polar) # or c.setFunctionType(FunctionCurve.Parametric)
f.loadData(1000, xLog10Scale = False)

f.setFunctionType(FunctionCurve.Normal)
f.setFormula("cos(x)")
f.loadData()
If you need to access the values and names of a function's parameters, you have at your disposal the following methods:

i = f.parametersCount() # the number of parameters in your function formula
name = f.parameterName(index) # the name of the parameter of rang index as a QString
p1 = f.parameterValue(index) # the value of the parameter of rang index as a double
p2 = f.parameterValue(name) # the value of a parameter using its name string
The abscissae range for which the function is calculated/displayed, can be obtained/modified via the methods below:

x1 = f.startRange()
x2 = f.endRange()
f.setRange(0.5, 15.2)

Error Bars

Having a plot layer l, you can add error bars to a data curve c, named curveName, using the following methods:

err1 = l.addErrorBars(c, Table *t, QString errColName, int type = 1, double width = 1, int capLength = 8, color = Qt.black, throughSymbol = True, minusSide = True, plusSide = True)
err2 = l.addErrorBars(curveName, Table *t, QString errColName, int type = 1, double width = 1, int capLength = 8, color = Qt.black, throughSymbol = True, minusSide = True, plusSide = True)
Each data curve, c, can have attached a list of error bars:

errors = c.errorBarsList()
The properties of an error bar curve can be accesses, via the following methods:

err = c.errorBarsList()[0]
for i in range(0, err.dataSize()):
	print err.errorValue(i)

err.capLength()
err.width()
err.color()
err.direction()
err.xErrors()
err.throughSymbol()
err.plusSide()
err.minusSide()
c = err.masterCurve() # reference to the master curve to which the error bars curve is attached.
err.detachFromMasterCurve() # equivalent to c.removeErrorBars(err)
... and can be modified, via the following methods:

err.setCapLength(12)
err.setWidth(3)
err.setColor(Qt.red)
err.setDirection(ErrorBarsCurve.Vertical)
err.setXErrors(True) # equivalent to err.setDirection(ErrorBarsCurve.Horizontal)
err.drawThroughSymbol(True)
err.drawPlusSide(True)
err.drawMinusSide(False)
err.setMasterCurve(c)
You can remove all error bars attached to a curve using:

c.clearErrorBars()

Image and Contour Line Plots (Spectrograms)

As you have seen in the previous section, it is possible create 2D plots from matrices. Here's how you can do it in practice:

m = importImage("C:/poze/adi/PIC00074.jpg")
g1 = plot(m, Layer.ColorMap)
g2 = plot(m, Layer.Contour)
g3 = plot(m, Layer.GrayScale)
g4 = plot(m, Layer.HeatMap)
The plot functions above return a reference to the multilayer plot window. If you need a reference to the spectrogram object itself, you can get it as shown in the example below:

m = newMatrix("TestMatrix", 1000, 800)
m.setFormula("x*y")
m.calculate()
g = plot(m, Layer.ColorMap)
s = g.activeLayer().spectrogram(m)
s.setColorBarWidth(20)
It is possible to fine tune the plots created from a matrix:

m = newMatrix("TestMatrix", 1000, 800)
m.setFormula("x*y")
m.calculate()

s = newGraph().activeLayer().plotSpectrogram(m, Layer.ColorMap)
s.setContourLevels((20.0, 30.0, 60.0, 80.0))
s.setDefaultContourPen(QtGui.QPen(Qt.yellow)) # set global pen for the contour lines
s.setLabelsWhiteOut(True)
s.setLabelsColor(Qt.red)
s.setLabelsFont(QtGui.QFont("Arial", 14))
s.setLabelsRotation(45)
s.showColorScale(Layer.Top)
s.setColorBarWidth(20)
As you have seen earlier, you can set a global pen for the contour lines, using:

s.setDefaultContourPen(QtGui.QPen(Qt.yellow))
You can also assign a specific pen for each contour line, using the function below:

s.setContourLinePen(index, QPen)
or you can automatically set pen colors defined by the color map of the spectrogram:

s.setColorMapPen(bool on = True)
It is possible to customize the alpha channel of the image plot using the following functions:

# set alpha channel using an integer value between 0 (transparent) and 255 (opaque)
s.setAlpha(127)
print s.alpha()

# set alpha channel using a float value between 0.0 (transparent) and 1.0 (opaque)
s.setAlphaF(0.65)
print s.alphaF()
You can also use any of the following functions:

s.setMatrix(Matrix *, bool useFormula = False)
s.setUseMatrixFormula(bool useFormula = True)# calculate data to be drawn using matrix formula (if any)
s.setLevelsNumber(int)
s.showColorScale(int axis, bool on = True)
s.setGrayScale()
s.setDefaultColorMap()
s.setCustomColorMap(LinearColorMap map)
s.showContourLineLabels(bool show = True) # enable/disable contour line labels
s.setLabelsOffset(int x, int y) # offset values for all labels in % of the text size
s.updateData()

Analytical Spectrograms

It is possible to create 2D image and contour plots from analytical functions of two variables (x and y), without having to create a matrix window first. Here's how you can do it in practice:


g = newGraph().activeLayer()
f = "sin(x)*sin(y)"

s1 = g.addFunction2D(f, -2*pi, 0, 0, 2*pi, -1, 1)
s1.setTitle("Image and Contour Plot")

s2 = g.addFunction2D(f, 0, 2*pi, 0, 2*pi, -1, 1, Layer.ColorMappedContour)
s2.setTitle("Color Mapped Contour Plot")

s3 = g.addFunction2D(f, -2*pi, 0, -2*pi, 0, -1, 1, Layer.GrayScale)
s3.setTitle("Gray Scale Plot")

s4 = g.addFunction2D(f, 0, 2*pi, -2*pi, 0, -1, 1, Layer.HeatMap)
s4.setTitle("Heat Map")

Histograms

As you have seen in the previous section, it is possible to create 2D histograms from matrices or tables. The script bellow shows a straightforward way of plotting a histogram from a table column:

h = newGraph().activeLayer().addHistogram(table("Table1"), "Table1_2")
h.setBinCount(5, 1, 30)# the number of bins is set to 5, data range is set to [1, 30]
or from a matrix:

g = plotHistogram(matrix("Matrix1")).activeLayer()
A more complex plot type that displays a histogram together with the corresponding cumulative probabilities can be created:

g1 = plotHistogramProbabilities(table("Table1"), "Table1_2").activeLayer()
g2 = plotHistogramProbabilities(matrix("Matrix1")).activeLayer()
It is also possible to get a reference to a previously created histogram and to modify it according to your needs:

g = graph("Graph1").activeLayer()
h = g.histogram(0)# g is a plot containing one histogram
h.setBinCount(8, 1, 30)
g.replot()
Here's a small script showing how to customize a histogram and how to get access to the statistical information in the histogram (bin positions, counts, mean, standard deviation, etc...):

m = newMatrix("TestHistogram", 1000, 800)
m.setFormula("x*y")
m.calculate()

g = newGraph().activeLayer()
h = g.addHistogram(m)
h.setBinSize(10, 1, 90) # the bin size is set to 10, data range is set to [1, 90]

# print histogram values:
for i in range (0, h.dataSize()):
	print 'Bin {0}: start = {1}, counts = {2}'.format(i + 1, h.x(i), h.y(i))

# print statistic information:
print "Standard deviation = ", h.standardDeviation()
print "Mean = ", h.mean()
print "Maximum = ", h.maximum()
print "Minimum = ", h.minimum()
print "Number of bins = ", h.binCount()
print "Bin size = ", h.binSize()
You can also enable autobinning (a default number of ten bins will be used):

h.setAutoBinning()

Box and whiskers plots

The following script shows how to create and customize a box and whiskers plot:


g = newGraph().activeLayer()
g.plotBox(table("Table1"), ["B", "C", "D"])
for i in range (0, g.numCurves()):
	box = g.boxCurve(i)
	box.setX(i + 1) # define X coordinate
	box.setGap(25) # set gap between boxes in percentages
	box.setDataDisplayMode(BoxCurve.BoxRightDataLeft)
	box.setDataType(BoxCurve.DotsAndBarsData)
	box.setMergeDataBars(True)
	box.setSnapDataToBin()
	box.setDataAlignment(Qt.AlignRight)
	box.setBoxStyle(BoxCurve.Notch)
	box.setBoxWidth(80)
	box.setWhiskersRange(BoxCurve.MinMax)
	box.setCapsLength(0.5)
	box.setBrush(QBrush(Qt.red, Qt.BDiagPattern))
	s1 = box.percentileSymbol()
	s1.setPen(QPen(Qt.blue, 2))
	s1.setSize(12)
	s1.setBrush(Qt.white)
	box.setPercentileSymbol(s1)
	s2 = PlotSymbol(PlotSymbol.Diamond, QBrush(Qt.gray), QPen(Qt.black), QSize(6, 6))
	box.setSymbol(s2) # symbol for data points
	box.setMeanStyle(PlotSymbol.Ellipse)
	box.showMeanLine()
	box.setMeanLinePen(QPen(Qt.magenta, 2))
	box.setLabelsDisplayPolicy(BoxCurve.Value)
	g.replot()
	box.showMeanLabel()

The following data display modes are available for box plots:

0

BoxCurve.Box

1

BoxCurve.Data

2

BoxCurve.BoxData

3

BoxCurve.BoxRightDataLeft

4

BoxCurve.BoxLeftDataRight

The following data types are available for box plots:

0

BoxCurve.DotsData

1

BoxCurve.BarsData

2

BoxCurve.DotsAndBarsData

The following functions give access to the statistics information for the data series displayed with a box and whiskers plot:

s = box.statistics() # returns information as a string
m = box.median()
q = box.quantile(f) # f must be a fraction between 0 and 1
For an exhaustive review of the methods giving access to the properties of a box plot, please consult the QtiPlot/Python API.

Connecting percentile symbols

The following script shows how to connect with lines different percentile symbols within a group of box plots:

g = plot(table("Table1"), ("A","B","C","D","E"), Layer.Box).activeLayer()

bg = g.boxChartGroup()
bg.setDrawLinesOnTop(False)

bg.setConnectMeans(True)
c1 = bg.meanCurve()
c1.setPen(QPen(Qt.red, 2))

bg.setConnectMedians(True)
c2 = bg.medianCurve()
c2.setPen(QPen(Qt.green, 2))

c3 = bg.addPercentileCurve(5)
c3.setPen(QPen(Qt.blue, 2))

c4 = bg.addPercentileCurve(95)
c4.setPen(QPen(Qt.magenta, 2))

Distribution Curves

For histograms and box and whiskers plots it is possible to overlay a distribution curve on the binned data. These curves are not "fitted" to your data. Instead, QtiPlot determines the data mean, then overlays the curve so that means coincide. If it is a two parameter curve, QtiPlot takes into account the standard deviation of your data. The following distribution types can be used:

0

DataCurve.NoCurve

1

DataCurve.Normal

2

DataCurve.Lognormal

3

DataCurve.Poisson

4

DataCurve.Exponential

5

DataCurve.Laplace

6

DataCurve.Lorentz

7

DataCurve.Weibull

8

DataCurve.KernelSmooth

9

DataCurve.Gamma

10

DataCurve.Binomial

In the case of the Kernel Smooth distribution QtiPlot provides two methods to calculate an optimal bandwidth:

DataCurve.Scott

This is the default method. The optimal bandwidth of the Kernel Smooth distribution is calculated using the formula: w = 0.9*A*N-0.2, where N is the number of bins, A = min(SD, IQR/1.349), SD is the standard deviation and IQR is the interquartile range of the bins data set.

DataCurve.Silverman

The optimal bandwidth of the Kernel Smooth distribution is: w = 1.059*A*N-0.2, where N is the number of bins, A = min(SD, IQR/1.349), SD is the standard deviation and IQR is the interquartile range of the bins data set.

The following script creates a histogram plot over which overlays a KernelSmooth distribution curve with an optimal bandwidth calculated using the Silverman method. The distribution curve is scaled to 50% of the maximum bin value:


h = newGraph().activeLayer().addHistogram(table("Table1"), "2")
h.setDistributionCurve(DataCurve.KernelSmooth, 50, DataCurve.Silverman)
h.loadData()

The following functions give access to the distribution curve and its properties:


dc = h.distributionCurve()
dc.setPen(Qt.red)
print(h.distributionScaleFactor())
print(h.kernelBandwidth())

When working with box and whiskers plots you can enable the distribution curve in the same way:


g = newGraph().activeLayer()
g.plotBox(table("Table1"), ["B"])
box = g.boxCurve(0)
box.setDataDisplayMode(BoxCurve.BoxData)
box.setSnapDataToBin()
box.setDistributionCurve(DataCurve.KernelSmooth)
box.updateHistogram()
box.distributionCurve().setPen(Qt.blue)

Curve Intersections

It is possible to find the intersections between the curves of a 2D plot layer using the intersections() method, which returns a QPolyonF. The script bellow creates a new plot layer with three analytical functions and prints the coordinates of the intersection points:


g = newGraph().activeLayer()
g.addFunction("x*sin(x)", 0, 10, 50)
g.addFunction("x/3.0", 1, 10, 2)
g.addFunction("sin(x)", 0, 10, 30)

poly = g.intersections()
for i in range (0, poly.size()):
	p = poly[i]
	print(p.x(), p.y())

The intersections() method accepts several arguments: the interpolation method used by the search algorithm, the number of sampling points, the coordinates of the search region. The last argument is a reference to an existing table that is used by QtiPlot to output the coordinates of the intersection points:


t = newTable()
poly = g.intersections(Interpolation.Linear, 10000, 3.0, 8.0, -0.2, 2.0, t)
if (poly.size() > 0):
	c = g.insertCurve(t, t.column(2).fullName(), Layer.Scatter)
	c.setSymbol(PlotSymbol(PlotSymbol.XCross, QBrush(), QPen(Qt.blue), QSize(7, 7)))

Ridgeline plots

The following script shows how to create a ridgeline plot also displaying a list of quantile lines:


g = plot(table("Book1"), ("A", "B", "C", "D", "E", "F"), Layer.Ridgeline).activeLayer()
for i in range (0, g.numCurves()):
	r = g.ridgelineCurve(i)
	r.setQuantiles("5 25 75 95")
	r.setPen(QPen(Qt.darkGray, 1.5))# set pen for quantile lines
	r.setDistributionScaleFactor(120)
	r.updateHistogram()

The following display types are available for ridgeline curves:

RidgelineCurve.Distribution

This is the default mode: data is shown as a KernelSmooth distribution curve.

RidgelineCurve.DistributionAndData

Also draws the data symbols over the distribution curve.


g = plot(table("Book1"), ("A", "B", "C", "D", "E", "F"), Layer.Ridgeline).activeLayer()
for i in range (0, g.numCurves()):
	r = g.ridgelineCurve(i)
	r.setDisplayMode(RidgelineCurve.DistributionAndData)
	r.setSymbol(PlotSymbol(PlotSymbol.Diamond, QBrush(Qt.black), QPen(Qt.black), QSize(4, 4)))

RidgelineCurve.DistributionAndRug

Also displays data as a rug beneath the distribution curve. It is possible to customize the rug pen, the size of the rug (in %) and the gap between the rug and the distribution curve (also in percentages).


g = plot(table("Book1"), ("A", "B", "C", "D", "E", "F"), Layer.Ridgeline).activeLayer()
for i in range (0, g.numCurves()):
	r = g.ridgelineCurve(i)
	r.setDisplayMode(RidgelineCurve.DistributionAndRug)
	r.setRugSize(15)
	r.setRugPen(QPen(r.distributionCurve().fillAreaColor(), 1.5))
	r.setSpacing(2)
	r.setDistributionScaleFactor(80)
	r.updateHistogram()

RidgelineCurve.SingleBlockBars

Displays the data under the form of a histogram.


g = plot(table("Book1"), ("A", "B", "C", "D", "E", "F"), Layer.Ridgeline).activeLayer()
for i in range (0, g.numCurves()):
	r = g.ridgelineCurve(i)
	r.setDisplayMode(RidgelineCurve.SingleBlockBars)
	r.setBinWidth(80)

Pie Plots

The following script shows how to create and customize a 3D pie plot:

g = newGraph().activeLayer()
pie = g.plotPie(table("Table1"), "2")
pie.setRadius(70)
pie.setViewAngle(40)
pie.setThickness(20)
pie.setStartAzimuth(45)
pie.setLabelsEdgeDistance(50)
pie.setCounterClockwise(True)
pie.setBrushStyle(Qt.Dense3Pattern)
pie.setIncrementPattern(True)
pie.setFirstColor(3)
pie.setPen(QtGui.QPen(Qt.red, 2))
pie.setLabelValuesFormat(True)
Here's how you can create a 2D black and white pie chart:

pie2D = newGraph().activeLayer().plotPie(table("Table1"), "2")
pie2D.setViewAngle(90.0);
pie2D.setFirstColor(-1);
pie2D.setIncrementPattern();
For an exhaustive review of the methods giving access to the properties of a pie plot, please consult the QtiPlot/Python API.

Doughnut Plots

The following script shows how to quickly create a doughnut plot from a single data set:


doughnut = newGraph().activeLayer().plotPie(table("Table1"), "Table1_A")
doughnut.setViewAngle(90.0)
doughnut.setHoleSize(50)

The script bellow demonstrates how to create multiple doughnut plots from several data sets. The first parameter of the setDoughnutLayout function is the radius of the largest doughnut chart expressed as a percentage of the plot width. Its default value is set to 90%. The second parameter is the size of the doughnut hole. By default it is set to 50% of the radius for each doughnut plot.


t = table("Table1")
g = newGraph().activeLayer()
g.plotPie(t, "Table1_A")
g.plotPie(t, "Table1_B")
g.plotPie(t, "Table1_C")
g.setDoughnutLayout(80, 40)

Vector Plots

You can create a vector plot by giving four columns in a Python tuple as an argument and the plot type as Layer.VectXYXY (11) or Layer.VectXYAM (14), depending on how you want to define the end point of your vectors: using (X, Y) coordinates or (Angle, Magnitude) coordinates.


g = qti.app.plot(table("Table1"), (2,3,4,5), Layer.VectXYXY)

In the case of XYAM vector plots you can set a constant angle and/or magnitude, but you still need to specify at least two column names in order to define the origin of the vectors. The following script shows how to create and customize XYAM vector curves:


g = newGraph().activeLayer()
t = table("Table1")

v = g.plotVectors(t, ["Table1_1", "Table1_2"], Layer.VectXYAM)
v.setConstantAngle(135)
v.setConstantMagnitude(15)
v.setPosition(VectorCurve.Head)

v = g.plotVectors(t, ["Table1_1", "Table1_2", "Table1_3"], Layer.VectXYAM)
v.setConstantMagnitude(10)
v.setMagnitudeInPixels()
v.setVectorPen(Qt.red)

v = g.plotVectors(t, ["Table1_1", "Table1_2", "", "Table1_3"], Layer.VectXYAM)
v.setConstantAngle(90)
v.setVectorPen(Qt.blue)

v = g.plotVectors(t, ["Table1_1", "Table1_2", "Table1_3", "Table1_4"], Layer.VectXYAM)
v.setVectorPen(Qt.green)
v.fillArrowHead(False)
v.setHeadAngle(15)
v.setHeadLength(20)
v.setMagnitudeMultiplier(10)
v.setPosition(VectorCurve.Middle)

By default the magnitudes are interpreted in real world coordinates and are used to compute the x, y values of the end point of the vector. The magnitudes displayed in the graph will change when the X or Y axes scales are changed, but they will remain constant in real world coordinates. By using the method setMagnitudeInPixels with the default True argument, the magnitudes will be calculated in pixels and will remain constant when the X or Y axes scales are changed. The same effect can be obtained by using the setRealWorldMagnitude(False) method.

For an exhaustive review of the methods giving access to the properties of a vector curve, please consult the QtiPlot/Python API.

Arrows


arrow = ArrowMarker()
arrow.setStart(210.5, 212.5)
arrow.setEnd(400, 400.51)

#customize line
arrow.setStyle(QtCore.Qt.DashLine)
arrow.setColor(Qt.red)
arrow.setWidth(1)

#customize start arrow head
arrow.setStartArrowShape(Layer.VerticalLine)
arrow.setStartHeadLength(15)
arrow.setStartHeadAngle(25)

#customize end arrow head
arrow.setEndArrowShape(Layer.FilledAngle)
arrow.setEndHeadLength(20)
arrow.setEndHeadAngle(15)

l = newGraph().activeLayer()
arrow1 = l.addArrow(arrow)

arrow.setStart(120.5, 320.5)
arrow.setColor(Qt.blue)
arrow2 = l.addArrow(arrow)

l.remove(arrow1)

As you might notice from the sample code above, the addArrow function returns a reference to a new arrow object that can be used later on to modify this new arrow or to delete it with the remove function.

The shape of an arrow head can take one of the following values:

0.

Layer.NoArrow

1.

Layer.FilledTriangle

2.

Layer.Angle

3.

Layer.FilledAngle

4.

Layer.EmptyTriangle

5.

Layer.Diamond

6.

Layer.VerticalLine

7.

Layer.HalfLineDown

8.

Layer.HalfLineUp

9.

Layer.XCross

10.

Layer.Rectangle

11.

Layer.Ellipse

The end points of a line/arrow can be retrieved like shown bellow:


a = newGraph().activeLayer().addArrow(ArrowMarker())
a.setEnd(200.5, 400.5)

start = a.startPointCoord() # start point in scale coordinates
end = a.endPointCoord() # end point in scale coordinates

print(start.x(), start.y())
print(end.x(), end.y())

start = a.startPoint() # start point in pixels
end = a.endPoint() # end point in pixels

print(start.x(), start.y())
print(end.x(), end.y())

It is possible to modify the properties of all the lines/arrows in a plot layer, see the short example below:


l = graph("Graph1").activeLayer()
for arrow in l.arrowsList():
	arrow.setColor(Qt.green)
l.replot()

The number of lines/arrows in a plot layer can be retrieved using the function numArrows:


l = graph("Graph1").activeLayer()
print l.numArrows()

It is possible to get a reference to the selected line/arrow in a plot layer using the function selectedArrow:


a = graph("Graph1").activeLayer().selectedArrow()

Arrow objects have a default name, which can be customized:


print(arrow.name())
arrow.setName("MyFirstArrow")
print(arrow.name())

It is also possible to get a reference to a line/arrow using its name or index:


l = graph("Graph1").activeLayer()
a1 = l.arrow(0)
a2 = l.arrow("MyArrow")
Starting with version 0.9.9.3 the functions bellow are deprecated. These methods are still provided in order to keep old source code working but they are no longer maintained. We strongly advise against using them in new code.

drawStartArrow(bool)
drawEndArrow(bool)
fillArrowHead(bool)
setHeadLength(int)
setHeadAngle(int)

Reference lines

The sample script bellow shows how to add a horizontal reference line at a constant axis value in a newly created plot layer and how to customize it's main properties like: line pen, fill brush, baseline value and linked label.


g = newGraph().activeLayer()

hline = g.addReferenceLine(600.0)
hline.setLinePen(QPen(Qt.red, 1.5, Qt.DashLine))
hline.setFillBrush(QColor(255, 0, 0, 50))
hline.setBaseline(200.0)

label = g.legend()
label.setText("$(v, *5)")

hline.setLegend(label)
hline.setLabelAlignment(Qt.AlignBottom)

g.replot()
hline.updateLegendPosition()

It is possible to add a reference line that is attached to an existing plot curve and is displayed at a predefined statistical value:


g = newGraph().activeLayer()
g.addFunction("sin(x)", 0, 10, 100)

vline = g.addReferenceLine(g.curve(0), LineMarker.Maximum)
vline.setLinePen(QPen(Qt.red, 1.5))
vline.setFillBrush(QColor(255, 0, 0, 50))
vline.setBaseline(-1.0)
vline.setLabelAlignment(Qt.AlignBottom)

l = vline.newLegend()
l.setFrameStyle(0)
l.setTextColor(Qt.red)
l.setBackgroundColor(QColor(255, 255, 255, 0))

The predefined statistical types are:

LineMarker.Mean

Mean value of the curve data points.

LineMarker.Median

Median value of the curve data points.

LineMarker.MeanPlusSD

Mean value plus the standard deviation of the curve data points.

LineMarker.MeanMinusSD

Mean value minus the standard deviation of the curve data points.

LineMarker.MeanPlusSE

Mean value plus the standard error of the curve data points.

LineMarker.MeanMinusSE

Mean value minus the standard error of the curve data points.

LineMarker.MeanPlus95CI

The upper limit of the 95% confidence interval for the mean of the curve data points.

LineMarker.MeanMinus95CI

The lower limit of the 95% confidence interval for the mean of the curve data points.

LineMarker.Minimum

Minimum value of the curve data points.

LineMarker.Maximum

Maximum value of the curve data points.

A reference line may serve as baseline for another one. Of course, the two reference lines should have the same orientation (horizontal or vertical):


g = newGraph().activeLayer()

vline1 = g.addReferenceLine(400.0, Qt.Vertical)
vline1.setLinePen(QPen(Qt.blue, 1))

vline2 = g.addReferenceLine(600.0, Qt.Vertical)
vline2.setLinePen(vline1.linePen())
vline2.setFillBrush(QColor(0, 0, 255, 50))
vline2.setBase(vline1)

Furthermore, it is possible to draw only the color filling between two reference lines creating thus a plot region:


g = newGraph().activeLayer()

vline1 = g.addReferenceLine(400.0, Qt.Vertical)
vline1.setDrawLine(False)

vline2 = g.addReferenceLine(600.0, Qt.Vertical)
vline2.setFillBrush(QColor(0, 0, 255, 50))
vline2.setDrawLine(False)
vline2.setBase(vline1)

A reference line has a non-empty default name. The default generated names are: line1, line2, etc... It is possible, of course, to change the default name as shown bellow:


g = newGraph().activeLayer()
line = g.addReferenceLine(400.0)
line.setName("MyLine")
print(line.name())

The name of a line, as well as its index, can be used in order to get a pointer to it. The indices start at zero:


l = g.referenceLine("MyLine")
#l = g.referenceLine(0)
if not l:
	print("No line!")
else:
	l.setPosition(500)

A reference line can be removed from a graph like shown in the script bellow:


g.remove(g.referenceLine(0))

Adding images to a layer


l = newGraph().activeLayer()
image = l.addImage("C:/poze/adi/PIC00074.jpg")
image.setCoordinates(200, 800, 800, 200)
image.setFrameStyle(Frame.Shadow)
image.setFrameColor(QtCore.Qt.green)
image.setFrameWidth(3)
l.replot()
The setCoordinatesfunction above can be used to set the geometry of the image using scale coordinates. If you need to specify the image geometry in pixel coordinates, independently of the plot axes values, you may use the following functions:

image.setOrigin(x, y)
image.setSize(width, height)
image.setRect(x, y, width, height)
l.replot()
You can remove an image using its reference:

l.remove(image)

Images have a default name, which can be customized:


print(image.name())
image.setName("MyImage")

It is possible to get a reference to an image using its name or its index:


l = graph("Graph1").activeLayer()
image1 = l.image(0)
image2 = l.image("MyImage")

It is also possible to iterate over all images in a plot layer, see a short example below:


l = graph("Graph1").activeLayer()
for i in range(0, l.numImages()):
	image = l.image(i)
	image.setFrameColor(Qt.blue)
	image.repaint()

LaTeX equations

The Python API gives access only to the built-in LaTeX parser.


l = newGraph().activeLayer()

eq = l.addEquation("$\\left[-\\frac{\\hbar^2}{2m}\\frac{\\partial^2}{\\partial x^2}+V(x)\\right]\\Psi(x)=\\mathrm{i}\\hbar\\frac{\\partial}{\\partial t}\\Psi(x)$")
eq.setFontPointSize(20)
eq.setTextColor(Qt.blue)
eq.setBackgroundColor(Qt.lightGray)
eq.setFrameStyle(Frame.Line)
eq.setFramePen(QPen(Qt.blue))
eq.setOrigin(80, 40)
eq.setBestSize()

eq.exportVector("/Users/ion/Desktop/Schroedinger.pdf")
eq.pixmap().save("/Users/ion/Desktop/Schroedinger.png")

Equations have a default name, which can be customized:


print(eq.name())
eq.setName("Schroedinger")

It is possible to get a reference to an equation using its name or its index:


l = graph("Graph1").activeLayer()
eq1 = l.equation(0)
eq2 = l.equation("Schroedinger")

An equation can be removed from a plot layer using its reference:


l = graph("Graph1").activeLayer()
l.remove(l.equation("Schroedinger"))

The short example below demonstrates how to iterate over all equations in a plot layer:


l = graph("Graph1").activeLayer()
for i in range(0, l.numEquations()):
	eq = l.equation(i)
	eq.setFrameColor(Qt.red)
	eq.repaint()

Rectangles


l = newGraph().activeLayer()

r = Rectangle(l)
r.setSize(100, 100)
r.setOrigin(100, 200)
r.setBackgroundColor(QtCore.Qt.yellow)
r.setFrameColor(QtCore.Qt.red)
r.setFrameWidth(3)
r.setFrameLineStyle(QtCore.Qt.DotLine)
r.setBrush(QtGui.QBrush(QtCore.Qt.green, QtCore.Qt.FDiagPattern))

r1 = l.add(r)
You can remove a rectangle using its reference:

r2 = l.add(r)
r2.setOrigin(200, 200)
l.remove(r1)

Rectangles have a default name, which can be customized:


print(r.name())
r.setName("MyRectangle")

It is possible to get a reference to a rectangle using its name or its index:


l = graph("Graph1").activeLayer()
r1 = l.rectangle(0)
r2 = l.rectangle("MyRectangle")

It is also possible to iterate over all rectangles in a plot layer, see a short example below:


l = graph("Graph1").activeLayer()
for i in range(0, l.numRectangles()):
	r = l.rectangle(i)
	r.setBrush(QBrush(Qt.darkGreen))
	r.setFrameColor(QtCore.Qt.red)
l.replot()

Circles/Ellipses


l = newGraph().activeLayer()

e = Ellipse(l)
e.setSize(100, 100)
e.setOrigin(100, 200)
e.setBackgroundColor(QtCore.Qt.yellow)
e.setFrameColor(QtCore.Qt.red)
e.setFrameWidth(0.8)
e.setFrameLineStyle(QtCore.Qt.DotLine)
e.setBrush(QtGui.QBrush(QtCore.Qt.green, QtCore.Qt.FDiagPattern))

l.add(e)

Ellipses have a default name, which can be customized:


print(e.name())
e.setName("MyEllipse")

It is possible to get a reference to an ellipse using its name or its index:


l = graph("Graph1").activeLayer()
e1 = l.ellipse(0)
e2 = l.ellipse("MyEllipse")

It is also possible to iterate over all ellipses in a plot layer, see a short example below:


l = graph("Graph1").activeLayer()
for i in range(0, l.numEllipses()):
	e = l.ellipse(i)
	e.setBrush(QBrush(Qt.green))
	e.setFrameColor(QtCore.Qt.red)
l.replot()

Plot Tables

Plot tables are special 2D plot objects that can be used for presentation of data. They have nothing in common with the table windows used as data sources for the plot curves. You can add a plot table to a multilayer graph window and customise it like shown in the example bellow:


cols = 4
rows = 3
l = newGraph().activeLayer()
t = l.add(PlotTable(l, rows, cols))
t.resize(250, 120)
t.setGridColor(Qt.blue)
t.setGridStyle(Qt.DashLine)

tw = t.tableWidget()# tw is a QTableWidget
# customise the table header
for j in range (0, cols):
		cell = tw.item(0, j)# cell is a QTableWidgetItem
		cell.setText("Header")
		cell.setFont(QFont("Arial", 14, QFont.Bold, True))
		cell.setForeground(Qt.darkRed)
		cell.setBackground(Qt.gray)
# customise table cells
for i in range (1, rows):
	for j in range (0, cols):
		cell = tw.item(i, j)
		cell.setText("cell")
		cell.setForeground(Qt.darkGreen)
		cell.setBackground(Qt.lightGray)

The alignment of the text in the table cells, the color of the text, the font and the background color can also be customised using the methods bellow:


t = newGraph().activeLayer().addTable()
t.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
t.setTextColor(Qt.blue)
t.setTextFont(QFont("Courier", 12, QFont.Bold, False))
t.setBackground(Qt.lightGray)

The dimensions of an existing plot table can be modified using the following functions:


t = newGraph().activeLayer().addTable()
t.setNumRows(5)
t.setNumCols(6)
t.resize(250, 120)

The grid of a plot table can only be customised in terms of color and pen style:


t = newGraph().activeLayer().addTable()
t.setGridColor(Qt.red)
t.setGridStyle(Qt.DotLine)

By default the columns of a plot table have equally sized columns. It is possible to change this default behaviour to a different functioning mode where all columns automatically resize to the dimensions of their content except for the last column which will always stretch to occupy the remaining space, by using the setStretchModeEnabled function:


t = newGraph().activeLayer().addTable()
t.setStretchModeEnabled(False)

tw = t.tableWidget()# tw is a QTableWidget
for i in range (0, tw.rowCount()):
	for j in range (0, tw.columnCount()):
		cell = tw.item(i, j)
		cell.setText("cell")

Plot tables have a default name, which can be customized:


print(t.name())
t.setName("MyTable")

It is possible to get a reference to a plot table using its name or its index:


l = graph("Graph1").activeLayer()
t1 = l.plotTable(0)
t2 = l.plotTable("MyTable")

It is also possible to iterate over all plot tables in a plot layer, see a short example below:


l = graph("Graph1").activeLayer()
for i in range(0, l.numPlotTables()):
	t = l.plotTable(i)
	t.setGridColor(Qt.blue)
	t.repaint()

Color Scale Legends

You can add a color scale legend like in the example bellow:

cs = graph("Graph1").activeLayer().addColorScaleLegend()
cs.setOrigin(500, 20)
cs.setSize(100, 200)

By default a new color scale is attached to the first plot curve/matrix available in the plot layer, meaning that the color map displayed by the legend is the color map of this plot item. It is possible to change the plot item to which the color scale is attached using the index of the item (starting at zero), like shown bellow:


cs = graph("Graph1").activeLayer().addColorScaleLegend()
cs.setPlotItem(1)
cs.setOrigin(500, 20)

Please note that if the index argument of the setPlotItem function has a negative value (no plot item attached), than the color scale legend displays a default color map, that can be customized via the setDefaultColorMap function:


map = LinearColorMap(Qt.yellow, Qt.blue)
map.addColorStop(0.2, Qt.magenta)
map.addColorStop(0.5, Qt.red)
map.addColorStop(0.7, Qt.cyan)

cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setPlotItem(-1)
cs.setDefaultColorMap(map)

The colorScaleList() function provides a list containing the references of all color scale legends available in a plot layer. The colorScaleWidget() function returns a reference to the color scale at a given index in the list. You can use it in order to get a reference to an already existing color scale legend and to customize it according to your needs. You can change for example its default layout, like shown in the script bellow:


cs = graph("Graph1").activeLayer().colorScaleWidget(0)
cs.setColorBarWidth(30)
# Align the tick labels to the left and vertically centered
cs.setLabelAlignment(Qt.AlignLeft | Qt.AlignVCenter)
cs.setAlignment(2) # left scale layout
# Set the distance between the legend frame and the color bar ends to 15 pixels
cs.setBorderDist(15)
# Set left, top, right and bottom margins, respectively
cs.setMargins(15, 10, 15, 10)
# Set the distance between the ticks and the tick labels to 10 pixels
cs.setLabelSpace(10)
cs.setInvertedScale(True)

The alignment of a color scale legend can take one of the following values:

0.

Bottom scale

1.

Top scale

2.

Left scale

3.

Right scale. This is the default layout.

By default the color bar of the legend has a black frame. It is possible to remove it:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setDrawColorBarFrame(False)
cs.repaint()

or to customize its appearance:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
# reenable the drawing of the color bar frame
cs.setDrawColorBarFrame()
# modify the color and line width of the frame
cs.setColorBarFramePen(QPen(Qt.blue, 2.5))
cs.repaint()

The main line ("backbone") of the axis of a color scale legend is hidden by default (the getter function hasBackbone() returns False). It is possible to enable it and customize its appearance, using the functions bellow:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.enableBackbone(True)
cs.setAxisColor(Qt.blue)
cs.setAxisLineWidth(1)
# Set the distance between the axis and the color bar to 2 pixels
cs.setAxisMargin(2)

By default the title of a color scale legend (returned by the function axisTitle()) is an empty string, but it is possible to define a custom title:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setAxisTitle("My color scale lagend!")
cs.setAxisTitleColor(Qt.blue)
cs.setAxisTitleFont(QFont("Courier", 12))
cs.setAxisTitleAlignment(Qt.AlignRight)

The layout of the major tick labels can be completely customized using the following functions:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setLabelAlignment(Qt.AlignHCenter| Qt.AlignVCenter)
# Set the distance between the ticks and the tick labels to 12 pixels
cs.setLabelSpace(12)
cs.setLabelRotation(-45)
cs.repaint()# makes the changes visible

The font and color of the major tick labels can be also customized using the following functions:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setLabelFont(QFont("Courier", 12))
cs.setLabelColor(Qt.red)

The numerical format of the labels can be set using the function setLabelsNumericFormat(format, precision), where format can have the following values:

0.

Automatic: the most compact numeric representation is chosen.

1.

Decimal: numbers are displayed in floating point form.

2.

Scientific lower case exponential notation, e.g.: 1.0e+04.

3.

Superscripts: like Scientific, but the exponential part is displayed as a power of 10.

4.

Engineering format, e.g: 10k.

5.

Superscripts: like Superscripts above, but the multiplication sign is replaced by a dot sign.

6.

Scientific upper case exponential notation, e.g.: 1.0E+04.

and precision is the number of significant digits (the default value is 6):


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setLabelsNumericFormat(3, 1)
cs.updateScale()

Additionally it is possible to define a prefix and/or a suffix for the major tick labels using the functions bellow:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setLabelPrefix("Value: ")
cs.setLabelSuffix(" mm")
cs.repaint()

Finally it is possible to disable the display of the major tick labels using the function enableLables():


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.enableLables(False)

The default policy for color scale legends is to display a major tick for each level in the color map. This can be changed using the setLevelsDisplayMode function. Its argument can take one of the following values:

0 or ColorScale.AllMajorLevels:

A major tick is displayed for each level in the color map.

1 or ColorScale.PartialTotalLevels:

A major tick is displayed for every n-th color level, where n can be defined using the setSkipMajorTicksCount function.


				cs = graph("Graph1").activeLayer().colorScaleList()[0]
				cs.setLevelsDisplayMode(ColorScale.PartialTotalLevels)
				cs.setSkipMajorTicksCount(1)
				cs.updateScale()
				
2 or ColorScale.CustomStepLevels:

A major tick is displayed at regular intervals starting with the lowest color level in the map. The step can be defined using the setAxisStep function.


				cs = graph("Graph1").activeLayer().colorScaleList()[0]
				cs.setLevelsDisplayMode(ColorScale.CustomStepLevels)
				cs.setAxisStep(4.5)
				cs.updateScale()
				
3 or ColorScale.CustomCountLevels:

The total number of major tick can be defined using the setMajorTicksCount function.


				cs = graph("Graph1").activeLayer().colorScaleList()[0]
				cs.setLevelsDisplayMode(ColorScale.CustomCountLevels)
				cs.setMajorTicksCount(10)
				cs.updateScale()
				

By default a color scale legend has no minor ticks (the getter function minorTicksCount returns zero), but it is possible to modify their number using the function setMinorTicksCount:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setMinorTicksCount(4)
cs.updateScale()

The length of the ticks (both minor and major) can be customized using the function setTicksLength:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.setTicksLength(5, 10) # minor ticks have 5 pixels, major ticks 10

It is also possible to disable the display of the ticks:


cs = graph("Graph1").activeLayer().colorScaleList()[0]
cs.enableTicks(False)

Color scale legends have a default name, which can be customized:


print(cs.name())
cs.setName("MyColorScale")

It is possible to get a reference to a color scale using its name or its index:


l = graph("Graph1").activeLayer()
cs1 = l.colorScaleWidget(0)
cs2 = l.colorScaleWidget("MyColorScale")

It is also possible to iterate over all color scales in a plot layer, see a short example below:


l = graph("Graph1").activeLayer()
for i in range(0, l.numColorScales()):
	cs = l.colorScaleWidget(i)
	cs.setLabelFont(QFont("Courier", 12))
	cs.setLabelColor(Qt.red)
l.replot()

Exporting graphs

Layers and whole graph windows can be printed and exported from within Python. The fastest way to export a graph window or single layers is the following:

l.export(fileName)
This function uses some default parameters for the properties of the image. If you need more control over the exported images you can use one of the following specialized functions:

l.exportVector(fileName, size = QtCore.QSizeF(), unit = Frame.Pixel, fontsFactor = 1.0, color = True)
l.exportImage(fileName, quality = 100, transparent = False, dpi = 0, size = QtCore.QSizeF(), unit = Frame.Pixel, fontsFactor = 1.0, compression = 0)
l.exportTex(fileName, color = True, escapeStrings = True, fontSizes = True, size = QtCore.QSizeF(), unit = Frame.Pixel, fontsFactor = 1.0)

When exporting graph windows there is an extra boolean parameter that can be used in order to clip the space around plot layers. This last parameter has no effect if used in conjunction with a custom export size. An example of how you should call these functions can be found bellow:


g = graph("Graph1")
g.exportVector("test.pdf", QtCore.QSizeF(), Frame.Pixel, 1.0, True, True)
g.exportImage("test.png", 100, True, 0, QtCore.QSizeF(), Frame.Pixel, 1.0, 0, True)
g.exportTex("test.tex", True, True, True, QtCore.QSizeF(), Frame.Pixel, 1.0, True)

The function exportVector can export the plot/layer to the following vector formats: .eps, .emf, .ps, .pdf, .svg and .wmf (this last format is only supported on Windows platforms). The use of the color parameter only makes sense when exporting to .eps, .pdf and .ps formats. For all other formats it is ignored.

The function exportImage can be used if you need to export to one of the Qt supported raster image formats (.bmp, .png, .jpg, etc...). The transparent option can only be used in conjunction with the file formats supporting transprency: .png and .tif (.tiff). The quality parameter influences the size of the output file. The higher this value (maximum is 100), the higher the quality of the image, but the larger the size of the resulting files. The dpi parameter represents the export resolution in pixels per inch (the default is screen resolution), size is the printed size of the image (the default is the size on screen) and unit is the length unit used to express the custom size and can take one of the following values:

0.

Inch

1.

Millimeter

2.

Centimeter

3.

Point: 1/72th of an inch

4.

Pixel

The fontsFactor parameter represents a scaling factor for the font sizes of all texts in the plot (the default is 1.0, meaning no scaling). If you set this parameter to 0, the program will automatically try to calculate a scale factor.

The compression parameter can be 0 (no compression) or 1 (LZW) and is only effectif for .tif/.tiff images. It is neglected for all other raster image formats.

It is also possible to get a QPixmap from a 2D plot layer that can be used later on for saving to a raster format image, like shown bellow:


g = graph("Graph1").activeLayer()

pix1 = g.pixmap()
pix1.save("/Users/ion/Desktop/Graph1.png")

pix2 = g.pixmap(QSize(1000, 800)) # custom image size in pixels
pix2.save("/Users/ion/Desktop/Graph2.png")

pix3 = g.pixmap(QSize(1000, 800), 1.5) # 1.5 scaling fonts factor
pix3.save("/Users/ion/Desktop/Graph3.png")

pix4 = g.pixmap(QSize(1000, 800), 1.5, True) # enable transparency
pix4.save("/Users/ion/Desktop/Graph4.png")

pix5 = g.pixmap(QSize(1000, 800), 0.0, False, 0.0) # automatic scaling factors
pix5.save("/Users/ion/Desktop/Graph5.png")

The function exportTex can be used if you need to export to a TeX file. The escapeStrings parameter enables/disables the escaping of special TeX characters like: $, {, }, ^, etc... If True, the fontSizes parameter triggers the export of the original font sizes in the plot layer. Otherwise all exported text strings will use the font size specified in the preamble of the TeX document.

All the export functions rely on the file name suffix in order to choose the image format.

Arranging Layers

When you are working with many layers in a 2D plot window, setting the layout of these layers manually can be a very tedious task. With the help of a simple Python script you can make this task very easy and automatically manage the layout of the plot window. For example, here's how you can create a two rows by two columns matrix of layers, each plot layer having a canvas size (the drawing area) of 400 pixels wide and 300 pixels in height:

g = newGraph("Test", 4, 2, 2)
g.setLayerCanvasSize(400, 300)
g.arrangeLayers(False, True)
The arrangeLayers()function takes two parameters. The first one specifies if the layers should be arranged automatically, using a best-layout algorithm, or if the numbers of rows and columns is fixed by the user. If the value of the second parameter is True, the size of the canvas is fixed by the user and the plot window will be enlarged or shrunken, according to the user settings. Otherwise the size of the plot window will be kept and the canvas area of each layer will be automatically adapted to fit this size. Here's how you can modify the graph created in the previous example, in order to display a row of three layers, while keeping the size of the plot window unchanged:

g.setNumLayers(3)
g.setRows(1)
g.setCols(3)
g.arrangeLayers(False, False)
By default, the space between two neighboring layers as well as the distance between the layers and the borders of the plot window is set to five pixels. You can change the spacing between the layers and the margins using the following functions:

g.setSpacing(x, y)
g.setMargins(left, right, top, bottom)
Another aspect of the layout management is the alignment of the layers. There are three alignment flags that you can use for the horizontal alignment (HCenter, Left, Right) and another three for the vertical alignment (VCenter, Top, Bottom) of the layers. The following code line aligns the layers with the right edge of the window and centers them vertically in the available space:

g.setAlignment(Graph.Right, Graph.VCenter)
The alignment of the layers can be done with respect to the drawing area between the axes (Graph.AlignCanvases) or with respect to the whole layer widget (Graph.AlignLayers) and you can specify the alignment policy to use via the following method:

g.setAlignPolicy(Graph.AlignCanvases)
A very often needed layout is the one with shared layer axes having linked abscissae (modifying the x scale for one layer will automatically adjust the scales for all plot layers). Here's how you can simply create such a 2x2 layers grid, with only a few lines of code:

g = newGraph("", 4, 2, 2)
g.setSpacing(0, 0)
g.setAlignPolicy(Graph.AlignCanvases)
g.setCommonLayerAxes()
g.arrangeLayers()
g.linkXLayerAxes(True)
All the examples above suppose that the layers are arranged on a grid, but of course you can add layers at any position in the plot window. In the examples below the x, y coordinates, in pixels, refer to the position of the top-left corner of the layer. The origin of the coordinates system coincides with the top-left corner of the plot window, the y coordinate increasing towards the bottom of the window. If the width and height of the layer are not specified they will be set to the default values. The last argument specifies if the default preferences, specified via the Preferences dialog, will be used to customize the new layer (default value is False):

g = newGraph()
l1 = g.addLayer()
l2 = g.addLayer(215, 20)
l3 = g.addLayer(10, 20, 200, 200)
l4 = g.addLayer(10, 20, 200, 200, True)
You can remove a plot layer using:

l = g.layer(num)
g.removeLayer(l)
g.removeActiveLayer()
As you have already seen, in a plot window the active layer is, by default, the last layer added to the plot, but you can change it programatically:

l = g.layer(num)
g.setActiveLayer(l)
In case you need to perform a repetitive task on all the layers in a plot window, you need to use a for loop and of course you need to know the number of layers existing on the plot. Here's a small example showing how to custom the titles of all the layers in the plot window:

g = graph("Graph1")
layers = g.numLayers()
for i in range (1, layers+1):
	l = g.layer(i)
	l.setTitle("Layer"+QtCore.QString.number(i))
	l.setTitleColor(QtGui.QColor("red"))
	l.setTitleFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold, True))
	l.setTitleAlignment(QtCore.Qt.AlignLeft)
Finally, sometimes it might be useful to be able to swap two layers. This can be done with the help of the following function:

g.swapLayers(layerNum1, layerNum2)

Waterfall Plots

Waterfall plots are ideal for comparing variations between multiple data sets created under similar conditions. A pseudo 3D effect is generated by applying an offset to all the data curves in a 2D plot layer, enabling you to see variations in the Z direction. You can create and customize them using the functions below:

g = waterfallPlot(table("Table1"), (2, 3, 4))
l = g.activeLayer()
l.setWaterfallOffset(50, 20)# x/y offsets as % of the layer drawing area width/height
l.setWaterfallSideLines(True) # draw side lines for all the data curves
l.setWaterfallFillColor(Qt.lightGray)
g.reverseWaterfallOrder() # reverse the order of the displayed curves

3D Plots

Creating a 3D plot

You can plot 3D analytical functions or parametric surfaces. For the 3D functions, the only parameters allowed are xfor the the abscissae values and yfor the ordinates:

g = plot3D("sin(x*y)", -10.0, 10.0, -10.0, 10.0, -2.0, 2.0)
For the parametric surfaces the only parameters allowed are the latitude and the longitude: uand v. Here's, for example, how you can plot a sphere:

g = plot3D("sin(u)*cos(v)", "sin(u)*sin(v)", "cos(u)", 0, 2*pi, 0, pi)
You can also create 3D height maps using data from matrices and, of course, you can plot table columns:

g = plot3D(matrix("Matrix1"), style = 5)
g = plot3D(table("Table1"), "3", style)
In the case of 3D plots created from matrix data sources the styleparameter can take any integer value from 0 to 5, with the following signification:

Qti3D.NOPLOT (0)

No visible data

Qti3D.WIREFRAME (1)

Wireframe style

Qti3D.HIDDENLINE (2)

Hidden Line style.

Qti3D.FILLED (3)

Color filled polygons without edges (no visible mesh).

Qti3D.FILLEDMESH (4)

Color filled polygons with separately colored edges (visible mesh).

Qti3D.POINTS (5)

Scattered points (the default style)

For 3D plots created from tables the styleparameter can take any integer value from 0 to 9 or the equivalent style values from the following list:

0.

Graph3D.Scatter

1.

Graph3D.Trajectory

2.

Graph3D.Bars

3.

Graph3D.Ribbon (only usable for XYY plots)

4.

Graph3D.LineSymbols

5.

Graph3D.StackBar

6.

Graph3D.StackBarPercent

7.

Graph3D.InlineBars

8.

Graph3D.StackArea

9.

Graph3D.StackAreaPercent

10.

Graph3D.Area (only usable for XYY plots)

11.

Graph3D.Wall (only usable for XYY plots)

12.

Graph3D.StackWall (only usable for XYY plots)

13.

Graph3D.StackWallPercent (only usable for XYY plots)

Customizing the view

When a new 3D plot is created, the scene view parameters are set to default values. Of course, QtiPlot provides functions to customize each aspect of the view. For example, you can set rotation angles, in degrees, around the X, Y and Z axes, respectively, using:


g.setRotation(45, 15, 35)
The following function allows you to shift the plot along the world X, Y and Z axes, respectively:

g.setShift(3.0, 7.0, -4.0)
You can also zoom in/out the entire plot as a whole, or you can zoom along a particular axis:

g.setZoom(10)
g.setScale(0.1, 0.05, 0.3)
Also, you can automatically detect the zoom values that fit best with the size of the plot window:

g.findBestLayout()

Once a plot is created, you can modify the scales and set the data range to display, using, for example:


g.setScales(-1.0, 1.0, -10.0, 11.0, -2.0, 3.0)
g.setAxisScale(Qti3D.X1, 0, 11) # set scale for X axis
g.setAxisScale(Qti3D.Y1, 0.0, 1.5, 0.5, 4, 0) # set scale limits, step and ticks for Y axis

It is possible to set a custom background color:


g.setBackgroundColor(QColor("lightYellow"))

Finally, it is possible to animate the view. The rotation angles around the three axes at each step of the animation can be customized like shown in the example bellow:


g.setAnimationSteps(0, 0, 1)
g.animate(True)

It is possible to copy a source plot window gSrc to a destination 3D plot gDst, like in the example bellow:


gSrc = plot3D("Graph1")
gDst = newPlot3D("testClone3D")
gDst.copy(gSrc)

Please note that it is also possible to copy only the format of a 3D plot window without affecting the data source of its curves:


gSrc = plot3D("Graph1")
gDst = plot3D("Graph2")
gDst.copy(gSrc, False) # copy only the format of the curves, not the data

Adding curves

An alternative method to create a 3D plot is to create an empty plot window and to fill it with curves/surfaces from various data sources. As you have already seen, a data source can be an analytical function or a parametric surface, a matrix or a table.


g = newPlot3D("test3D")
g.setTitle("My 3D Plot", Qt.blue, QFont("Arial",14))

c1 = g.addMatrix(matrix("Matrix1"))
c1.setMeshColor(Qt.gray)

c2 = g.addFunction("sin(x*y)", -2.0, 2.0, -2.0, 2.0, -1.0, 1.0)
c2.setMeshColor(Qt.black)

c3 = g.addParametricSurface("sin(u)*cos(v)", "sin(u)*sin(v)", "cos(u)", 0, 2*pi, 0, pi)
c3.setMeshColor(Qt.blue)
c3.setDataColors(Qt.cyan)

It is allowed to add two or more 3D surfaces to the same plot window using a single data source matrix. In order to visualise them as distinct surfaces, one can enable a shift along the Z axis for each of them. The shift value is expressed as a percentage of the Z axis range. A zero value for the shift means that the minimum value of the surface is displayed at the minimum/bottom value of the Z scale. A value of 1 for the shift means that the minimum value of the surface is displayed at the maximum/top value of the Z scale.


m = matrix("Matrix1")
g = newPlot3D()

c1 = g.addMatrix(m)
c1.setPlotStyle(Qti3D.HIDDENLINE)
c1.setMeshColor(Qt.blue)
c1.enableZShift(True)
c1.setZShift(0.0)

c2 = g.addMatrix(m)
c2.setPlotStyle(Qti3D.FILLED)
c2.enableZShift(True)
c2.setZShift(0.5)

g.setAxisScale(Qti3D.Z1, -150.0, 150.0, 50.0)
g.findBestLayout()

It is possible to add several curves at once to the same 3D plot, provided that the data sets belong to the same table:


t = table("Table1")
newPlot3D().plotXYY(t, ("C", "D", "E", "F"), Graph3D.Bars)
newPlot3D().plotXYY(t, ("C", "D", "E", "F"), Graph3D.InlineBars)
newPlot3D().plotXYZ(t, ("C", "D", "E", "F"), Graph3D.StackBar)
newPlot3D().plotXYZ(t, ("C", "D", "E", "F"), Graph3D.StackArea)
newPlot3D().plotXYY(t, ("C", "D", "E", "F"), Graph3D.Wall)
newPlot3D().plotXYY(t, ("C", "D", "E", "F"), Graph3D.StackWallPercent)

It is also possible to add data sets from different source tables to the same 3D plot. In the script bellow "A", "B" and "C" are the names of the X, Y and Z columns used to define the XYZ curves. The script adds two XYZ curves to a new 3D plot window and customises their appearance:


g = newPlot3D()

c1 = g.addData(table("Table1"), "A", "B", "C",  Graph3D.LineSymbols)
c1.setDataColors(Qt.red)
c1.setMeshColor(Qt.red)
c1.setMeshLineWidth(1.5)

c2 = g.addData(table("Table2"), "A", "B", "D",  Graph3D.LineSymbols)
c2.setDataColors(Qt.green)
c2.setMeshColor(Qt.green)
c2.setMeshLineWidth(1.5)

g.setScales(0, 11, 0, 11, 0, 1, False)

The script bellow adds two XYY bar curves to a new 3D plot window. For each curve you need to specify two column names for the X and Y values and an extra Y coordinate. If the table does not contain a X column, simply enter an empty string for the X column name. In this case QtiPlot uses the row indices as X coordinates.


g = newPlot3D()

c1 = g.addData(table("Table1"), "A", "C", 0.5, Graph3D.Bars)
c1.setDataColors(Qt.red)
c1.setMeshColor(Qt.white)
c1.setMeshLineWidth(1.0)

c2 = g.addData(table("Table2"), "", "D", 1.0, Graph3D.Bars) # no X column
c2.setDataColors(Qt.green)
c2.setMeshColor(Qt.white)
c2.setMeshLineWidth(1.0)

g.setAxisScale(Qti3D.X1, 0, 11) # set scale for X axis
g.setAxisScale(Qti3D.Y1, 0.0, 1.5, 0.0, 2, 0) # set scale and ticks for Y axis
g.findBestLayout()

The example bellow adds two ribbon curves to a newly created plot and customises their appearance:


g = newPlot3D()

c1 = g.addData(table("Table1"), "A", "B", 0.0, Graph3D.Ribbon) # Y coordinate = 0.0
c1.setYWidth(0.3)
c1.setDataColors(Qt.red)
c1.setMeshColor(Qt.white)
c1.setMeshLineWidth(1.0)

c2 = g.addData(table("Table2"), "", "C", 1.0, Graph3D.Ribbon) # no X column, Y coordinate = 1.0
c2.setYWidth(0.3)
c2.setDataColors(Qt.green)
c2.setMeshColor(Qt.white)
c2.setMeshLineWidth(1.0)

g.setAxisScale(Qti3D.Y1, -0.5, 1.5, 0.0, 2, 0) # set scale and ticks for Y axis
g.findBestLayout()

Working with curves

It is possible to iterate through the curves in a 3D plot like shown in the script bellow:


g = newPlot3D()
g.plotXYY(table("Table1"), ("C", "D", "E", "F"), Graph3D.Bars)
g.setSpecialLabelDisplay(Graph3D.ColumnName)
for i in range(0, g.curveCount()):
	c = g.curve(i)
	c.setMeshColor(Qt.white)
	c.setMeshLineWidth(1.5)

For 3D plots displaying XYY curves, the method setSpecialLabelDisplay, used in the example above, specifies how the tick labels for the Y axis are handled by QtiPlot. There are five options available:

0.

Graph3D.TickValue

1.

Graph3D.ColumnName

2.

Graph3D.ColumnComment

3.

Graph3D.ColumnLongName

4.

Graph3D.ColumnFullName

3D curves can be removed from a plot window using either their index or their reference:


g = newPlot3D()
g.plotXYY(table("Table1"), ("C", "D", "E", "F"), Graph3D.Bars)# plot 4 curves
g.removeCurve(0) # 3 curves left
g.removeCurve(g.curve(0)) # 2 curves left

It is possible to remove all curves created from the same source data table (or matrix) like shown bellow:


g = newPlot3D()
t = table("Table1")
g.plotXYY(t, ("C", "D", "E"), Graph3D.Bars)# plot 3 curves
g.removeData(t) # no curves left

For large data sets you can increase the drawing speed by reducing the number of points taken into account. The lower the resolution parameter, the higher the number of points used: for an integer value of 1, all the data points are drawn.


g = newPlot3D()
c1 = g.addMatrix(matrix("Matrix1"))
c1.setResolution(2)
c2 = g.addMatrix(matrix("Matrix2"))
c2.setResolution(3)

Curve colors

The default color map of a 3D curve/surface is defined using a rainbow color map. It is possible to change the default colors:


c.setDataColors(Qt.blue)
c.setDataColors(Qt.black, Qt.green)

Of course, one can define more complex color maps, using LinearColorMap objects:


map = LinearColorMap(Qt.green, Qt.black)
map.setMode(LinearColorMap.FixedColors) # default mode is LinearColorMap.ScaledColors
map.addColorStop(0.2, Qt.yellow)
map.addColorStop(0.5, Qt.red)
map.addColorStop(0.7, Qt.blue)
c.setDataColorMap(map)

Also, it is possible to use predefined color maps stored in .map files. A .map file consists of a of 255 lines, each line defining a color coded as RGB values. A set of predefined color map files can be downloaded from QtiPlot web site.


c.setDataColorMap("/Users/ion/qtiplot/colormaps/PEACH.MAP")

Finally, one can customize the transparency of a curve. The transparency parameter must lie within the range (0.0, 1.0):


c.setTransparency(0.75)

Curve labels

In the case of 3D curves created from tables, QtiPlot can display symbol labels like in the example bellow:


g = newPlot3D()
g.plotXYY(table("Table1"), ("C", "D", "E"), Graph3D.Bars)
g.setSpecialLabelDisplay(Graph3D.ColumnName)
for i in range(0, g.curveCount()):
	c = g.curve(i)
	c.setLabelsForm(Curve3D.XYZValues);
	c.setVisibleLabels()
	c.setLabelsColor(Qt.blue)
	c.setLabelsFont(QFont("Arial", 12, QFont.Bold))
	c.setLabelsRotation(90.0)
	c.setLabelsNumericPrecision(3)
	c.setLabelsOffset(20, 0, 50)

The labels may have one of the following predefined forms:

0

Curve3D.XValues

1

Curve3D.YValues

2

Curve3D.ZValues

3

Curve3D.RowIndices

4

Curve3D.XYValues

5

Curve3D.XYZValues

You may also specify the labels using the texts in a table column:


c.setLabelsColumnName("Table1_2")

Curve style

The style of the 3D curve/surface can be modified using the following functions:

c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setResolution(3)
c.setPlotStyle(Qti3D.FILLED)
c.setDataColors(Qt.black, Qt.white) # set gray scale color map
c.setShading(Qti3D.CONTOURS)

The following styles are available for the shading:

Qti3D.FLAT (0)

The GL_FLAT shading mode from OpenGL: the color of a polygon in the 3D surface is given by only one of its vertices.

Qti3D.GOURAUD (1)

The GL_SMOOTH shading mode from OpenGL: the colors of all vertices composing a polygon are interpolated, assigning different colors to each resulting pixel fragment of the rendered surface.

Qti3D.CONTOURS (2)

The polygons composing the rendered surface are filled to the isoline contours.

Qti3D.CONTOURS2 (3)

Same as previous method: the polygons composing the rendered surface are filled to the isoline contours. This method is less accurate than CONTOURS and may fail for very complex data, but is a litle bit faster and gives nicer results for smooth surfaces.

Floor projection

By default, the floor projection of a 3D surface is disabled. One can enable a full 2D projection or only display the isolines using the following floor projection styles:

Qti3D.NOFLOOR (0)

Empty floor.

Qti3D.FLOORISO (1)

The isoline projections are visible.

Qti3D.FLOORDATA (2)

The projected polygons visible.

If the floor style is set to Qti3D.FLOORISO, the number of projected isolines can be customised using the function setIsolines:


c = newPlot3D().addFunction("x*y", -1.0, 1.0, -1.0, 1.0, -1.0, 1.0)
c.setFloorStyle(Qti3D.FLOORISO)
c.setIsolines(30)
c.setFloor(Qti3D.CustomPlane, 0.5)

By default, the floor plane is the Z plane that contains the minimum vertex. It is possible to change this setting using one of values bellow:

Qti3D.ZeroPlane (0)

Zero plane.

Qti3D.MinVertex (1)

The Z plane that contains the minimum data vertex.

Qti3D.MaxVertex (2)

The Z plane that contains the maximum data vertex.

Qti3D.CustomPlane (3)

A custom Z value.

Surface normals

In three dimensions, a surface normal, or simply normal, to a surface at point P is a vector perpendicular to the tangent plane of the surface at P. In QtiPlot the normals can be enabled only for 3D plots created from matrices or from mathematical functions. The display of the surface normals can be enabled and customised like in the example bellow:


c = newPlot3D().addFunction("x*y", -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 20, 20)
c.setPlotStyle(Qti3D.HIDDENLINE)
c.setNormalLength(0.07) # length of the arrow line
c.setNormalColor(Qt.red) # color of the arrow line
c.setNormalWidth(1.5) # width of the arrow line
c.setNormalQuality(4) # number of arrow sides
c.setNormalArrowAngle(10.0)
c.setNormalArrowLength(0.5) # length of the arrow head
c.setFilledNormalArrow(True)
c.setSmoothNormalArrow(False)
c.showNormals(True)

Curve symbols

The symbols available for 3D scatter plots are: dots, bars, cones, crosses and cubes. These symbols can be enabled using the functions bellow:


c.setDotOptions(5, True)
c.setBarOptions(0.5, True, True, 0.2)
c.setConeOptions(0.1, 3, True, False, 0.0)
c.setCrossOptions(1.5, 1.5)
c.setCubeOptions(0.8, 1.5, True, False, Qt.blue)

Dot symbols can be enabled using the function setDotOptions. The first parameter of this function specifies the size of the points and the second determines their shape: circles if True (the default), rectangles otherwise:


c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setDotOptions(5, False) # display rectangles

The getter functions for the properties of the dots are listed bellow:


c.pointSize()
c.hasSmoothPoints()

The first parameter of the function setBarOptions is the width of the bar in the X direction, the second specifies whether the bar edges should be drawn, the third enables/disables collor filling and the forth specifies the width of the bar in the Y direction:


c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setBarOptions(0.5, True, True, 0.2)

The getter functions for the properties of the bars are listed bellow:


c.barRadiusX()
c.barRadiusY()
c.hasBarEdges();
c.hasFilledBars()

The first parameter of the function setConeOptions determines the width of the base, the second is the number of sides of the base polygon, the third enables/disables collor filling, the forth specifies if the cones are drawn upward or downward and the last parameter is the height of the cones:


c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setConeOptions(0.1, 3, True, False, 0.0) # display filled triangles

The getter functions for the properties of the cones are the following:


c.coneRadius()
c.coneHeight()
c.coneQuality()
c.hasFilledCones()
c.hasUpwardCones()

The first parameter of the function setCrossOptions determines the size of the cross symbol, the second the width of the line and the third specifies whether the crosses should be surrounded or not by a box frame (the default value for this parameter is False):


c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setCrossOptions(1.5, 1.5)

The getter functions for the properties of the crosses are listed bellow:


c.crossRadius()
c.crossLineWidth()
c.hasBoxedCrosses()

The first parameter of the function setCubeOptions determines the size of the cube, the second the width of the line, the third specifies whether the cube edges should be drawn or not, the forth enables/disables the color filling of the cube sides and the last one sets the color of the cube edges:


c = newPlot3D().addMatrix(matrix("Matrix1"))
c.setCubeOptions(0.8, 1.5, True, False)

The getter functions for the properties of the cubes are listed bellow:


c.cubeRadius()
c.cubeLineWidth()
c.hasCubeEdges()
c.hasFilledCubes()
c.cubeEdgeColor()

3D scatter plots can also be created using data stored in table columns, like in the example bellow, which creates for curves with different 3D symbols:


g = newPlot3D()

c1 = g.addData(table("Table1"), "A", "B", "C")
c1.setDataColors(Qt.red)
c1.setMeshColor(Qt.red)
c1.setMeshLineWidth(1.5)
c1.setDotOptions(10, False) #display rectangles
c1.setLineSymbolStyle(Curve3D.Dots)

c2 = g.addData(table("Table2"), "A", "B", "D")
c2.setDataColors(Qt.green)
c2.setMeshColor(Qt.green)
c2.setMeshLineWidth(1.5)
c2.setCubeOptions(0.5, 1.5, True, False)
c2.setLineSymbolStyle(Curve3D.Cubes)

c3 = g.addData(table("Table2"), "A", "B", "E")
c3.setDataColors(Qt.blue)
c3.setMeshColor(Qt.blue)
c3.setMeshLineWidth(1.5)
c3.setCrossOptions(0.5, 1.5)
c3.setLineSymbolStyle(Curve3D.Cross)

c4 = g.addData(table("Table2"), "A", "B", "F", Graph3D.Bars)
c4.setDataColors(Qt.darkMagenta)
c4.setMeshColor(Qt.white)
c4.setMeshLineWidth(1.5)
c4.setBarOptions(0.5, True, True, 0.5)

g.setScales(0, 11, 0, 11, 0, 1, False)

Drop Lines

For 3D curves created from data tables, it is possible to display drop lines:


c = newPlot3D().addData(table("Table1"), "A", "B", "C",  Graph3D.LineSymbols)
c.enableDropLines(True)
c.setDropLineColor(Qt.red)
c.setDropLineWidth(2)
c.setDropLineStyle(Qti3D.SHORTDASH)

3D Bars

When working with 3D bars, there are a few functions that might be very useful. The first one is setBasePlane. It allows to specify the base plane for the bars. Its argument can be either Qti3D.Zero (base plane is Z = 0) or Qti3D.MinVertex (the base plane is set to the minimum value of the Z axis).

The function setOffset can be used in order to impose an offset in both X and Y directions. The arguments of this function are percentages of the X and Y sizes of the bars, respectively.

The third function, setSquareBars, can be used in order to set equal sizes in X and Y directions. You must note that the dimensions of the bars depend on the X and Y dimensions of the 3D plot. If the length of the Y axis is visibly larger than the length of the X axis, than the bars will not have a square form, even if you have used this function with a True argument.


bars = newPlot3D().addData(table("Table2"), "A", "B", "F", Graph3D.Bars)
bars.setDataColors(Qt.red)
bars.setBasePlane(Qti3D.MinVertex)
bars.setOffset(0.5, 0.2)
bars.setSquareBars(True)
bars.setBarOptions(0.4)

Color Scale Legends

Depending on your user settings, QtiPlot creates or not a 3D color scale legend for each 3D curve. The script bellow shows how to get a reference to an existing color scale object or how to create a new one:


g = newPlot3D()
c = g.addMatrix(matrix("Matrix1"))
l = g.colorScale(c) # get a reference to the color legend object
if not l:
	l = g.addColorScaleLegend() # create new legend
	l.attachTo(c) # attach new legend to curve

An existing 3D color scale legend can be modified using the following functions:


l.setVisible(True) # make legend visible if hidden
#l.setGeometry(QRect(450, 20, 10, 100)) # set geometry of the color bar in pixel coordinates
l.setGeometry(90, 5, 3, 30)# set geometry using percentages of the plot dimensions

l.setOrientation(ColorScale3D.BottomTop)
l.setAxisPosition(ColorScale3D.Right)

l.setTitleString("My Legend")
l.setTitleColor(Qt.darkGreen)
l.setTitlePosition(ColorScale3D.Top)
l.setTitleGap(2) # set gap between title and color legend
l.setTitleFont("Courier", 12, QFont.Bold, True)

l.drawNumbers(True) # enable scale numbers
l.setBorderEnabled(True) # enable border
l.setBorderColor(Qt.darkGray)
l.setBorderWidth(1.0)
l.setColorBarEnabled(True) # the color bar will be drawn

a = l.axis() # get a reference to the axis object
a.setNumberColor(Qt.blue)
a.setNumberFont("Courier", 11, QFont.Bold, True)
a.setNumbersGap(2) # set gap between axis and numbers
a.setNumericFormat(Qti3D.Decimal, 1)

a.enableTicks(True) # turn ticks drawing on
a.enableBackbone(False) # disable axis backbone
a.setLineWidth(1.0)
a.setColor(Qt.darkGray)
a.setStep(0.1); # set scale step
a.setMinorIntervals(2) # display one minor tic (2 minor intervals)
a.setTickLength(1, 0.5)

There are two options for the orientation of the color bar: ColorScale3D.BottomTop (0), the color bar is drawn vertically, the lowest color index is on the bottom and ColorScale3D.LeftRight (1), the color bar is drawn horizontally, the lowest color index is on the left side.

The following positions can be used for the axis and/or the title of the color scale legend:

ColorScale3D.Top (0)

Axis/Title on top.

ColorScale3D.Bottom (1)

Axis/Title at the bottom.

ColorScale3D.Left (2)

Axis/Title on the left side.

ColorScale3D.Right (3)

Axis/Title on the right side.

The script bellow shows how to access the properties of an existing color scale legend:


visible = l.isVisible()
hasAxis = l.isAxisEnabled() # True if the legend scale is displayed
border = l.isBorderEnabled() # True if the legend border is displayed
bw = l.borderWidth()
bc = l.borderColor()
filled = l.isColorBarEnabled() # True if the color bar is displayed
o = l.orientation() # legend orientation (vertical or horizontal)
axisPos = l.axisPosition() # position of the axis
titlePos = l.titlePosition() # position of the title

a = l.axis() # get a reference to the legend axis

drawTicks = a.hasTicks() # whether scale drawing is on or off
drawNumbers = a.hasNumbers() # whether number drawing is on or off
drawBackbone = a.hasBackbone() # whether backbone drawing is enabled

step = a.step() # get step value
majLength = a.majorTickLength() # length of major ticks
minLength = a.minorTickLength() # length of minor ticks

majors = a.majorIntervals() # number of major intervals
minors = a.minorIntervals() #  number of minor intervals

font = a.numberFont() # font used for tick labels
gap = a.numbersGap() # distance axis - tick labels

prec = a.numericPrecision() # numeric precision for tick labels
fmt = a.numericFormat() # numeric format for tick labels
lw = a.lineWidth();

Color scale legends can be removed from a 3D plot like in the example bellow:


g = plot3D("Graph1")
l1 = g.colorScale(0)
l2 = g.colorScale(1)
g.removeColorScale(l1)
g.removeColorScale(l2)

3D Legends

It is very easy to add a legend to a 3D plot:


g = newPlot3D()
g.plotXYY(table("T275K"), ("C", "D", "E", "F"), Graph3D.Trajectory)
g.newLegend()

The legends in a 3D plot are in fact groups of texts and color scale objects and can be removed using their reference or their index. Please keep in mind the fact that removing a group does't also remove its child elements.


g = newPlot3D()
group = g.newLegend()
g.removeGroup(group)

g.newLegend()
g.removeGroup(0)

print(g.groupCount())

The example bellow demonstrates how to build a custom legend object for a 3D plot:


g = newPlot3D()
g.plotXYY(table("T275K"), ("C", "D", "E", "F"), Graph3D.Trajectory)

group = g.addGroup()

t = g.addText("My Legend")
t.move(85, 5)
group.addText(t)

for i in range(0, g.curveCount()):
	l = g.addColorScaleLegend()
	l.attachTo(g.curve(i)) # attach color scale to a curve
	l.drawNumbers(False) # disable scale numbers
	l.setTitleString("%(" + str(i + 1) + ")")
	l.setTitlePosition(ColorScale3D.Right)
	l.move(85, 11 + 5*i)
	group.addColorScale(l)

group.setFrameEnabled(True)
group.setFrameColor(Qt.blue)
group.setFrameWidth(1.5)
group.setBackgroundColor(Qt.lightGray)
group.setMargin(7)
group.setShadow(True, Qt.blue, 4)
group.setTextColor(Qt.blue)
group.setFont(QFont("Arial", 12))
group.setSymbolSize(QSize(20, 10)) # set color scale sizes
group.setSymbolBorder(False) # disable color scale borders
group.setFilledSymbols(True)

Lighting

Lighting can also be enabled programmatically for 3D plots. The light parameters can be customized like in the example bellow:


g = newPlot3D()
c = g.addMatrix(matrix("FUNC"))
c.setPlotStyle(Qti3D.FILLED)
c.setShading(Qti3D.CONTOURS)

g.enableLighting(True)
g.setAmbientLight(Qt.blue, 0.2)
g.setDiffuseLight(QColor("orange"), 0.93)
g.setSpecularLight(Qt.yellow, 0.94)
g.setShininess(56)
g.setLightEmission(0.03)
g.setLightDirection(90, 20)

The direction of the light, coming from a distant source, is given by two angles. The horizontal angle can take any value between 0 and 360 degrees and the vertical angle can take any value between -180 and 180 degrees.

The following getter funcions can be used to retrieve the lighting parameters of a 3D plot:


g.lightingEnabled()
g.ambientLightColor()
g.ambientLightIntensity()
g.diffuseLightColor()
g.diffuseLightIntensity()
g.specularLightColor()
g.specularLightIntensity()
g.shininess()
g.lightEmission()
g.horizontalLightAngle()
g.verticalLightAngle()

The coordinate system

One can fully customize the appearance of the coordinate system in a 3D plot, using the functions bellow:


g.setAutoDecorateAxes(False)
g.setCoordinateStyle(Qti3D.RHALFBOX, Qti3D.X1, Qti3D.Y1, Qti3D.Z3)
g.setOrthogonal(True)

The method setAutoDecorateAxes, used in the example above, specifies how the decorated axes are chosen by QtiPlot: automatically or using the axes specified by the setCoordinateStyle method. The setAutoDecorateAxes function should be called before the setCoordinateStyle method.

The following coordinate system styles are available:

Qti3D.NOCOORD (0)

The coordinate system is not visible.

Qti3D.BOX (1)

The coordinate system is drawn as a box: all 12 axes are visible, but only three of them are decorated with ticks and numbers.

Qti3D.FRAME (2)

Only three axes are visible.

Qti3D.RHALFBOX (3)

Three sides of the box are visible: the right side, the back and the floor.

Qti3D.LHALFBOX (4)

Three sides of the box are visible: the left side, the back and the floor.

The coordinates system can also be customized to display all the twelve axes, only three of them or none, respectively, with the help of the following functions:


g.setBoxed()
g.setFramed()
g.setNoAxes()

The twelve axes of the coordinate system can be accesed using the following indices or names:

Qti3D.X1 (0)

1st x axis

Qti3D.Y1 (1)

1st y axis

Qti3D.Z1 (2)

1st z axis

Qti3D.X2 (3)

2nd x axis

Qti3D.X3 (4)

3rd x axis

Qti3D.X4 (5)

4th x axis

Qti3D.Y4 (6)

4th y axis

Qti3D.Y3 (7)

3rd y axis

Qti3D.Y2 (8)

2nd y axis

Qti3D.Z2 (9)

2nd z axis

Qti3D.Z3 (10)

3rd z axis

Qti3D.Z4 (11)

4th z axis

The perspective view mode can be enabled/disabled using:


g.setOrthogonal(True)

If the axes are enabled, you can set their legends and the distance between the legend and the axis via:


g.setXAxisLabel("X axis legend")
g.setYAxisLabel("Y axis legend")
g.setZAxisLabel("Z axis legend")
g.setLabelsDistance(30)

It is possible to set the numerical format and precision of the axes using the function below:


g.setAxisNumericFormat(axis, format, precision)

where the first parameter is the index of the axis: 0 for X, 1 for Y and 2 for Z, the second one is the numerical format:

0.

Qti3D.Default: decimal or scientific, depending which is most compact

1.

Qti3D.Decimal: 10000.0

2.

Qti3D.Scientific: 1.0e+04 (lower case scientific format)

3.

Qti3D.Engineering: 10k

4.

Qti3D.Superscripts: 1x104

5.

Qti3D.SuperscriptsGER: 1⋅104

6.

Qti3D.ScientificUpperCase: 1.0E+04 (upper case scientific format)

and the last parameter is the precision (the number of significant digits).

The following convenience functions are also provided, where you don't have to specify the index of the axis anymore:


g.setXAxisNumericFormat(1, 3)
g.setYAxisNumericFormat(1, 3)
g.setZAxisNumericFormat(1, 3)

Also, you can set the length of the major and minor ticks of an axis:


g.setXAxisTickLength(2.5, 1.5)
g.setYAxisTickLength(2.5, 1.5)
g.setZAxisTickLength(2.5, 1.5)

The scale division of an axis can be customized like shown bellow:


g.setAxisScale(Qti3D.X1, 0, 11) # set scale for X axis
g.setAxisScale(Qti3D.Y1, 0.0, 1.5, 0.5, 4, 0) # set scale limits, step and ticks for Y axis

It is possible to fine tune the scale divisions of an axis by imposing the value of the first major tick:


g.setAxisFirstMajorTick(Qti3D.Y1, 0.1)
g.updateGL()

The colors of all the axes elements can be customized as shown below:


g.setAxesColor(Qt.darkGray)
g.setNumbersColor(Qt.blue)
g.setLabelsColor(QColor("darkRed"))

The properties of the twelve axes can be retrieved by first getting a reference to each axis, like shown bellow:


a = g.axis(Qti3D.Y1)
print(a.label().string())
print(a.lowerBound(), a.upperBound(), a.step(), a.firstMajorTick())
print(a.majorIntervals(), a.minorIntervals())

A reference to the title object of an axis can be retrieved and used in order to customise it, like shown bellow:


l = a.label()
l.setString("Axis title<br>has two lines!")
l.setAlignment(Qt.AlignCenter)
l.setColor(Qt.blue)
l.setFont(QFont("Arial", 12))

It is possible to set a color filling for the six sides of the coordinate system. Please note that transparency of these planes is not correctly handled when exporting 3D plots to vectorial image formats (*.EPS, *PDF or *.SVG files):


g = newPlot3D()
g.addFunction("sin(x*y)", -2.0, 2.0, -2.0, 2.0, -1.0, 1.0)

color = QColor(Qt.gray)
color.setAlphaF(0.7)
g.setLeftPlaneColor(color)

color = QColor(Qt.red)
color.setAlphaF(0.4)
g.setFrontPlaneColor(color)

color = QColor(Qt.blue)
color.setAlphaF(0.5)
g.setCeilPlaneColor(color)

g.setRightPlaneColor(Qt.yellow)
g.setBackPlaneColor(Qt.cyan)
g.setFloorPlaneColor(Qt.gray)

In order to disable the color filling for any of the sides, simply assign an invalid color to it:


g.setFrontPlaneColor(QColor())

The six colors of the coordinate system planes can be retrieved using the functions listed bellow:


leftColor = g.leftPlaneColor()
rightColor = g.rightPlaneColor()
backColor = g.backPlaneColor()
frontColor = g.frontPlaneColor()
floorColor = g.floorPlaneColor()
ceilColor = g.ceilPlaneColor()

It is also possible to define a colored border for the six sides of the coordinate system:


g = newPlot3D()
g.addFunction("sin(x*y)", -2.0, 2.0, -2.0, 2.0, -1.0, 1.0)

color = QColor(Qt.white)
color.setAlphaF(0.0)
g.setRightPlaneColor(color)
g.setBackPlaneColor(color)
g.setFloorPlaneColor(color)
g.setNoAxes()

g.setPlaneBorder(True, Qt.gray, 1.5, Qti3D.SHORTDASH)

The properties of the border can be retrieved using the functions listed bellow:


color = g.planeBorderColor()
lw = g.planeBorderWidth()
ls = g.planeBorderStyle()
on = g.isPlaneBorderEnabled()

The grid

If the coordinate system is visible, you can also display a grid around the surface plot. The example bellow shows how to fully customize the appearance of the grids in a 3D plot:


g.setGrid(Qti3D.RIGHT | Qti3D.BACK)

g.setMajorGridLines(Qti3D.X1, True, Qt.black, Qti3D.DASH, 1.0)
g.setMinorGridLines(Qti3D.X1, True, Qt.blue, Qti3D.SHORTDASH, 1.0)

g.setMajorGridLines(Qti3D.Y1, True, Qt.black, Qti3D.DASH, 1.0)
g.setMinorGridLines(Qti3D.Y1, True, Qt.green, Qti3D.SHORTDASH, 1.0)

g.setMajorGridLines(Qti3D.Z1, True, Qt.red, Qti3D.DASH, 1.0)
g.setMinorGridLines(Qti3D.Z1, False)

The following options are available for the setGrid method:

Qti3D.NOSIDEGRID (0)

The grid is not visible.

Qti3D.LEFT (1)

The left side of the grid.

Qti3D.RIGHT (2)

The right side of the grid.

Qti3D.CEIL (4)

The ceil.

Qti3D.FLOOR (8)

The floor.

Qti3D.FRONT (16)

The front side.

Qti3D.BACK (32)

The back side.

There are eight pen styles available for the customization of the grid lines:

Qti3D.SOLID (0)
Qti3D.DASH (1)
Qti3D.DOT (2)
Qti3D.DASHDOT (3)
Qti3D.DASHDOTDOT (4)
Qti3D.SHORTDASH (5)
Qti3D.SHORTDOT (6)
Qti3D.SHORTDASHDOT (7)

Additionally, each side of the grid can also be shown or hidden using the functions bellow:


g.setLeftGrid(True)
g.setRightGrid()
g.setCeilGrid()
g.setFloorGrid()
g.setFrontGrid()
g.setBackGrid(False)

The plot title

It is possible to add a title to a 3D plot like shown in the example bellow:


g = newPlot3D()
t = g.setTitle("My beautiful 3D plot", Qt.blue, QFont("Arial", 16, QFont.Bold))
t.setFrameColor(Qt.blue)
t.setMargin(5)
t.draw()

Ading texts

It is possible to add several text objects to a 3D plot, not only a title, using the addText method. By default, the new text objects are placed at the top left corner of the plot window. You may customise the position of a text using pixel coordinates or percentages of the plot dimensions:


g = newPlot3D()
g.addText("Test")

t = g.addText("My Text", QFont("Arial", 14), Qt.blue)
#t.move(QPoint(50, 100)) # set position using pixel coordinates
t.move(50, 10) # set position using percentages of the plot dimensions

Existing 3D text objects can be customized like shown bellow:


g = plot3D("Graph1")
l = g.text(0) # use text index to get a pointer
l.setString("Test")
l.setFont(QFont("Arial", 20))
l.setColor(Qt.blue) # set text color
l.setFrameColor(Qt.blue)
l.setFrameWidth(1.5)
l.setShadow(True, Qt.blue, 4)
l.setBackgroundColor(Qt.white)
l.setAngle(90.0)
l.setMargin(5)
g.updateGL() # update plot window

Text objects can be removed from a 3D plot like in the example bellow:


g = plot3D("Graph1")
t1 = g.text(0)
t2 = g.text(1)
g.removeText(t1)
g.removeText(t2)

3D Vectors

It is possible to plot 3D vectors using as data source a table that has at least six columns. The code bellow creates a 3D curve with vector type XYZ dXdYdZ and returns a reference to this curve:


t = table("Table1")
c = newPlot3D().addVectors(t, t.colNames(), True)

The second argument of the addVectors function is a string list that must contain six column names. If the third argument is not specified the type of the 3D vectors is by default set to XYZ XYZ.

Once created the type and the names of the data columns defining the end point of the 3D vector may be changed as shown bellow. The second argument of the setVectorEndZColName function is true in order to reload the data only once (the default value is False):


c.setDxDyDz(False)
c.setVectorEndXColName("Table1_A")
c.setVectorEndYColName("Table1_B")
c.setVectorEndZColName("Table1_C", True)

print c.isDxDyDz()
print c.vectorEndXColName()
print c.vectorEndYColName()
print c.vectorEndZColName()
The appearance of a 3D vectors curve c can be customized using the following functions:

c.setVectorColor(Qt.red)
c.setVectorLineWidth(2.0)
c.setVectorScalingFactor(0.75)

print c.vectorLineWidth()
print c.vectorScalingFactor()
The style of the arrow can be customized using the setArrowStylefunction. The first argument of this function is the head length, the second specifies the value in degrees of the head angle, the third is the quality, that is the number of facets used to draw the arrow head, the forth specifies weather the head is filled or not and the last argument sets the scaling policy for the arrow head. The value of the last argument may be: 0 - no scaling, 1 - logarithmic scaling or 2 - linear scaling.

c.setArrowStyle(0.5, 30, 4, False, 1)

print c.vectorHeadLength()
print c.vectorHeadAngle()
print c.arrowQuality()
print c.hasFilledArrowHead()
print c.vectorScaleHeadPolicy()

Exporting

In order to export a 3D plot you need to specify a file name containing a valid file format extension:


g.export(fileName)

This function uses some default export options. If you want to customize the exported image, you should use the following function in order to export to raster image formats:


g.exportImage(fileName, int quality = 100, bool transparent = False, dpi = 0, size = QSizeF(), unit = Frame.Pixel, fontsFactor = 1.0, compression = 0)

where quality is a compression factor: the larger its value, the better the quality of the exported image, but also the larger the file size. The dpi parameter represents the export resolution in pixels per inch (the default is screen resolution), size is the printed size of the image (the default is the size on screen) and unit is the length unit used to express the custom size and can take one of the following values:

0.

Inch

1.

Millimeter

2.

Centimeter

3.

Point: 1/72th of an inch

4.

Pixel

The fontsFactor parameter represents a scaling factor for the font sizes of all texts in the plot (the default is 1.0, meaning no scaling). If you set this parameter to 0, the program will automatically try to calculate a scale factor. The compression parameter can be 0 (no compression) or 1 (LZW) and is only effectif for .tif/.tiff images. It is neglected for all other raster image formats.

3D plots can be exported to any of the following vector formats: .eps, .ps, .pdf, .pgf and .svg, using the function below:


g.exportVector(fileName, textMode = 0, sortMode = 1, size = QSizeF(), unit = Frame.Pixel, fontsFactor = 1.0)
g.exportVector("/Users/ion/Desktop/test.pdf", Qti3D.PATH, Qti3D.BSPSORT, QSizeF(800, 600))

where textMode is an integer value, specifying how texts are handled. It can take one of the following values:

Qti3D.PIXEL (0)

All text will be converted to bitmap images (default).

Qti3D.NATIVE (1)

Text are exported using native fonts available in the output format. May result in incorrect positioning of the output texts.

Qti3D.TEX (2)

Text output in additional LaTeX file as an overlay.

Qti3D.PATH (3)

All texts will be converted to vector paths.

The sortMode parameter is also an integer value and can take one of the following values:

Qti3D.NOSORT (0)

No sorting at all.

Qti3D.SIMPLESORT (1)

A simple, quick sort algorithm (default).

Qti3D.BSPSORT (2)

BSP sort: best algorithm, but slow.

The other parameters have the same meaning as for the export of 2D plots.

Data Analysis

Introduction

As you will see in the following subsections, the data analysis operations available in QtiPlot are: convolution/deconvolution, correlation, differentiation, FFT, filtering, smoothing, fitting and numerical integration of data sets. Generally, you can declare/initialize an analysis operation using one of the following methods, depending on the data source, which can be a 2D plot curve or a table:


op = LogisticFit(graph("Graph1").activeLayer().curve(0), 15.2, 30.9)
op = FFTFilter(graph("Graph1").activeLayer(), "Table1_2", 1.5, 3.9)
op = LinearFit(table("Table1"), "colX", "colY", 10, 100)
In the first example the data source is a curve Table1_2, plotted in the active layer of the graph Graph1and the abscissae range is chosen between 1.5 and 3.9. In the second example the data source is a table Table1. The abscissae of the data set are stored in the column called colXand the ordinates in the column colY. The data range is chosen between the 10th row and the row with the index 100. If you don't specify the row range, by default the whole table will be used. Not all operations support curves as data sources, like for example: convolution/deconvolution and correlation. For these operations only table columns can be used as data sources for the moment.

Once you have initialized an operation, you can still change its input data via the following functions:


op.setDataFromCurve(graph("Graph2").activeLayer().curve(1), 10.5, 20.1)
op.setDataFromCurve("Table1_energy", 10.5, 20.1, graph("Graph2").activeLayer())
op.setDataFromTable(table("Table1"), "colX", "colY", 10, 100)

You don't have to specify a plot layer in the setDataFromCurve() function, if the analysis operation has already been initialized by specifying a curve on an existing graph and you just want to treat another curve from the same plot layer.

It is possible to sort the input data before performing the analysis operation:


op.setSortData(True)

Also, when performing analysis tasks via Python scripts, there are several utility functions that can be called for all operations. For example you can disable any graphical output from an operation or you can redirect the output to the plot layer of your choice:


op.enableGraphicsDisplay(False)
op.enableGraphicsDisplay(True, graph("Graph2").activeLayer())
Let's assume that you need to perform a specific operation op, which analyzes your data and at the end, displays a result curve. For this kind of operations, you can customize the number of points in the resulting curve and its color:

op.setOutputPoints(int)
op.setColor(int)
op.setColor("green")
Colors can be specified by their names or as integer values, from 0 to 23, each integer corresponding to a predefined color: 0 - "black", 1 - "red", 2 - "green", 3 - "blue", 4 - "cyan", 5 - "magenta", 6 - "yellow", 7 - "darkYellow", 8 - "navy", 9 - "purple", etc ...

The result plot curve generated after an anlysis operation can be accessed via the following function:


c = op.resultCurve()

Most of the time, a new table is also created as a result of a data analysis operation. This table stores the data displayed by the result curve and is hidden by default, but you can interact with it via the following function:


t = op.resultTable()
After the initialization of an analysis operation, which consists of setting the data source, the data range and some other properties, like color, number of points, etc..., you can execute it via a call to its run() function:

op.run()
For data fitting operations, there's an alias for the run() function which is: fit().

Analysis Filters

All data analysis operations are derived from the same base class, Filter, therefore we will generically call them filters. The type of an analysis filer can be obtained using the filterType() function. The folowing filter types are defined in the QtiPlot Python API:

0.

Filter.FitLinear

1.

Filter.FitLinearSlope

2.

Filter.FitPoly

3.

Filter.FitExp

4.

Filter.FitTwoExp

5.

Filter.FitThreeExp

6.

Filter.FitLogistic

7.

Filter.FitSigmoidal

8.

Filter.FitGaussAmp

9.

Filter.FitMultiPeak

10.

Filter.FitPlugIn

11.

Filter.FitUser

12.

Filter.Interpolate

13.

Filter.Smooth

14.

Filter.FindPeaks

15.

Filter.Fft

16.

Filter.FftFilter

17.

Filter.Integrate

18.

Filter.Differentiate

19.

Filter.Correlate

20.

Filter.Convolve

21.

Filter.Deconvolve

22.

Filter.MultipleLinearRegresion

23.

Filter.Pareto

24.

Filter.QQPlot

25.

Filter.BlandAltman

26.

Filter.ConvexHull

In a plot layer, as a result of the various analysis operations performed on the data curves, there might be several filter objects. It is possible to access each of these filters and to get information about them as shown in the sample script bellow:


g = graph("Graph1").activeLayer()
for i in range(0, g.filterCount()):
	f = g.filter(i)
	print f.filterType(), f.explanation()
	if f.filterType() == Filter.FitMultiPeak:
		print f.profile()

For all filters that display a result curve in a plot layer there is a mechanism available in QtiPlot that allows to trigger a recalculation when the data source is modified. The recalculate mode of a filter can be one of the following:

0

(NoRecalculate): no recalculation when data changes.

1

(AutoRecalculate): a recalculation is automatically performed when input data changes.

2

(ManualRecalculate): no recalculation is triggered when data changes, instead the user can perform a recalculation at any time using the recalculate() function.

The sample script bellow shows how to access and modify the recalculate mode and how to perform a recalculation of the data analysis operations from a plot layer:

g = graph("Graph1").activeLayer()
for i in range(0, g.filterCount()):
	f = g.filter(i)
	if f.recalculateMode() == Filter.AutoRecalculate:
		f.setRecalculateMode(Filter.ManualRecalculate)
	if f.hasModifiedData():
		f.recalculate()
A filter object fcan be added to a plot layer gusing the addFiltermethod:

g = graph("Graph1").activeLayer()
g.addFilter(f)

Please note that if the recalculation policy defined via the Fitting tab of the Preferences dialog is not set to No, data fit objects are added by default to the output graph layer. For all the other analysis filter types you need to use the addFilter function.

It is possible to remove all filter objects from a plot layer gwith a single line of code:

g.deleteFilters()
It is also possible to remove individual filter objects:

for i in range(0, g.filterCount()):
	g.deleteFilter(g.filter(i))

Correlation, Convolution/Deconvolution

Assuming you have a table named "Table1", here's how you can calculate the convolution of two of its columns, "Table1_B" and "Table1_C":

conv = Convolution(table("Table1"), "B", "C")
conv.setColor("green")
conv.run()
The deconvolution and the correlation of two data sets can be done using a similar syntax:

dec = Deconvolution(table("Table1"), "B", "C")
dec.run()

cor = Correlation(table("Table1"), "B", "C", 10, 200)
cor.setColor("green")
cor.run()

Convex Hull

Assuming you have a 2D plot window named "Graph1" containing one curve in its active layer, the example below shows how to find the convex hull of this data set within a defined x interval, in this case [0, 1]:


l = graph("Graph1").activeLayer()

hull = ConvexHullFilter(l.curve(0), 0.0, 1.0)
hull.enableArea(True)
hull.enablePerimeter(True)
hull.setResultCurvePen(QPen(Qt.blue, 2))
hull.run()

l.addFilter(hull)

print(hull.area())
print(hull.perimeter())

Differentiation

Assuming you have a graph named "Graph1" containing one curve (on its active layer), here's how you can differentiate this curve within a defined x interval, [2,10] in this case:


diff = Differentiation(graph("Graph1").activeLayer().curve(0), 2, 10)
diff.run()

The result of this code sequence would be a new plot window displaying the derivative of the initial curve. The numerical derivative is calculated using a five terms formula.

It is also possible to differentiate data directly from a table without displaying any graphical output:


diff = Differentiation(table("Table1"), "1", "2")
diff.enableGraphicsDisplay(False)
diff.run()
diff.resultTable().showNormal()

The result of the above code sequence is a new table window containing the derivative of the data from the initial table "Table1".

Pareto Charts

Pareto charts are implemented as data filtersin QtiPlot. There are two types of input data that can be used in order to generate a Pareto chart: binned data and raw data.

In the case of binned data the input typically consists of a text column containing the categories/items and one of numbers containing the corresponding counts or bins:


pf = ParetoFilter(table("Table1"), "Table1_Item", "Table1_Count")
pf.run()
pf.outputGraph().addFilter(pf)

In the case of raw data the input typically consists of a single text column, containing the categories/items:


pf = ParetoFilter(table("Table2"), "Table2_Items")
pf.run()
pf.outputGraph().addFilter(pf)

Bland-Altman Plots

Bland-Altman plots are implemented as data filters in QtiPlot. The easiest way to create a Bland-Altman plot, using the default settings, is shown bellow. In this design, each of the two measurement methods is used once on each subject. You need to provide two input data columns, one for each measurement method.


baf = BlandAltmanFilter(table("Table1"), "A", "B")
baf.run()
baf.outputGraph().addFilter(baf)

Of course, it is possible to customise the output settings:


baf = BlandAltmanFilter(table("Table1"), "A", "B", BlandAltmanFilter.Method1, BlandAltmanFilter.Ratio)
baf.showZeroLine(False)
baf.setLoaLinePen(QPen(Qt.red, 1.5))
baf.showLimitsOfAgreement(True, True)#enable LoA filling
baf.setLoaFillBrush(QColor(255, 255, 0, 25))# transparent yellow LoA filling
baf.run()
baf.outputGraph().addFilter(baf)

There are four display modes available for the X axis of a Bland-Altman plot:

Mean

The abscissas are calculated as the arithmetic mean of the paired values. This is the default mode.

Method1

The abscissas are the values measured using the first method.

Method2

The abscissas are the values measured using the second method.

GeometricMean

The abscissas are calculated as the geometric mean of the paired values.

There are three display modes available for the Y axis of a Bland-Altman plot:

DifferencePercent

The ordinates are calculated as the difference of the paired values, divided by their arithmetic mean and multiplied by 100.

Difference

The ordinates are calculated as the difference of the paired values. This is the default mode.

Ratio

The ordinates are calculated as the ratios of the paired values.

When repeated measurements are available, each subject being measured by each method several times, three columns of input data are required: one with the measurements of the first method, one with the measurements of the second method and one with the subject value. The easiest way to create a Bland-Altman plot in this case, using the default settings, is shown bellow:


baf = BlandAltmanFilter(table("Table1"), "A", "B", "C")
baf.run()
baf.outputGraph().addFilter(baf)

The default settings assume that the true value is instantaneously changing. If the true value is constant one should use the syntax bellow. In this case pairing of the measured values by each method may not be informative and the data columns for method 1 and method 2 may have a different number of cells:


baf = BlandAltmanFilter(table("Table1"), "A", "B", "C", True)
baf.run()
baf.outputGraph().addFilter(baf)

There are two methods for calculating the confidence interval for the limits of agreement (LoA): Delta method and MOVER (Method of Variance Estimates Recovery). By default QtiPlot uses the Delta method, but you may choose to use the MOVER method, like shown in the script bellow:


baf = BlandAltmanFilter(table("Table1"), "A", "B", "C", True)
baf.setLoaCiMethod(BlandAltmanFilter.MOVER)
baf.run()
baf.outputGraph().addFilter(baf)

Finally, it is possible to plot each subject as one bubble. Each subject is grouped and the corresponding bubble is displayed at mean of group (X axis) and mean difference (Y axis), the size of the bubble being proportional to the number of replicates:


baf = BlandAltmanFilter(table("Table1"), "A", "B", "C", True, True)
baf.run()
baf.outputGraph().addFilter(baf)

Q-Q Plots

Q-Q plots are implemented as data filters in QtiPlot:


qqf = QQPlotFilter(table("Table1"), "3")
qqf.setDistribution(QQPlotFilter.Weibull)
qqf.setScoreMethod(QQPlotFilter.Benard)
qqf.run()
qqf.outputGraph().addFilter(qqf)

distribution = qqf.distribution()
print("\nDistribution: ", qqf.distributionNames()[distribution])
print("Score method: ", qqf.scoreMethodNames()[qqf.scoreMethod()])
print("Parameters:")
lst = qqf.parameterNames(distribution)
print(lst[0], " = ", qqf.parameter1())
if (len(lst) > 1):
	print(lst[1], " = ", qqf.parameter2())

There are five distributions available for Q-Q plots: Normal, Lognormal, Exponential, Weibull and Gamma.

There are also five score methods available. The input data is first ordered ascendingly and after that the index of the sorted values is used to calculate a score using one of the formulas listed below, where N is the total number of valid values in the input data set:

Blom

(default): score = (index - 0.375)/(N + 0.25)

Benard

score = (index - 0.3)/(N + 0.4)

Hazen

score = (index - 0.5)/N

VanDerWaerden

score = index/(N + 1)

KaplanMeier

score = index/N

FFT

Assuming you have a graph named "Graph1" containing one curve on its active layer and having a periodicity of 0.1 in the time domain, a FFT will allow you to extract its characteristic frequencies. The results will be stored in a hidden table named "FFT1".

fft = FFT(graph("Graph1").activeLayer().curve(0))
fft.normalizeAmplitudes(False)
fft.shiftFrequencies(False)
fft.setSampling(0.1)
fft.run()
By default the calculated amplitudes are normalized and the corresponding frequencies are shifted in order to obtain a centered x-scale. If we want to recover the initial curve with the help of the inverse transformation, we mustn't modify the amplitudes and the frequencies. Also the sampling parameter must be set to the inverse of the time period, that is 10. Here's how we can perform the inverse FFT, using the "FFT1" table, in order to recover the original curve:

ifft = FFT(table("FFT1"), "Real", "Imaginary")
ifft.setInverseFFT()
ifft.normalizeAmplitudes(False)
ifft.shiftFrequencies(False)
ifft.setSampling(10)
ifft.run()

It is possible to apply a window function to the input data:


fft = FFT(graph("Graph1").activeLayer().curve(0))
fft.setWindow(FFT.GaussianWindow, 2.0)
fft.setSampling(0.001)
fft.run()

The window functions, w(i), available in QtiPlot are listed bellow, N being the total number of points in the input data sample and i the index of the data point, i = 0, 1,..., N - 1:

RectangularWindow

w(i) = 1. The input data is not altered.

WelchWindow

w(i) = 1 - [2(i - 0.5*(N - 1))/(N + 1)]2

TriangularWindow

w(i) = 2/N[N/2 - |i - N/2|]

BartlettWindow

w(i) = 2/(N - 1)*[(N - 1)/2 - |i - (N - 1)/2|]

HanningWindow

w(i) = 0.5*[1 - cos[2πi/(N - 1))]

HammingWindow

w(i) = 0.54 - 0.46 cos[2πi/(N - 1)]

BlackmanWindow

w(i) = 0.42 - 0.5 cos[2πi/(N - 1)] + 0.08 cos[4πi/(N - 1)]

GaussianWindow

w(i) = exp[-0.5(2α(i - N/2)/N)2], where α is the second argument of the setWindow method.

It is possible to customize the type of plot created as a result of the FFT operation, using the setPlotType method. The following default plot types are available: FFT.RealPlot, FFT.ImagPlot, FFT.MagnitudePlot, FFT.AmplitudePlot, FFT.PhasePlot, FFT.dBPlot, FFT.AmplitudePhasePlot and FFT.RealImagPlot.


fft = FFT(graph("Graph1").activeLayer().curve(0))
fft.setSampling(0.001)
fft.setPlotType(FFT.AmplitudePlot)
fft.run()

You can also perform 2D fast Fourrier transforms on matrices. The FFT routine takes in this case the following parameters: rm - specifies the real part input matrix, im - specifies the imaginary part input matrix, inverse - specifies the direction of the FFT, DCShift - if this is true, the DC component will be put in the center of the result matrix, otherwise, the DC component will locate at four corners of the result matrix, norm - specifies whether or not to normalize the amplitudes to 1, outputPower2Sizes - forces output matrices whose sizes are integer powers of 2 (zero padding is used to initialize the missing cells)


fft = FFT(Matrix *rm, Matrix *im = NULL, bool inverse = False, bool DCShift = True, bool norm = False, bool outputPower2Sizes = True)

Here's how you can perform the 2D FFT of a matrix called "Matrix1", and the inverse FFT in order to recover the original image:


from qti import *
m = matrix("Matrix1")
m.setViewType(Matrix.ImageView) # make sure the matrix is displayed as image

fft = FFT(m)
fft.run()
fft.amplitudesMatrix().resize(500, 400)

rMatrix = fft.realOutputMatrix()
rMatrix.hide()
iMatrix = fft.imaginaryOutputMatrix()
iMatrix.hide()

ifft = FFT(rMatrix, iMatrix, True)
ifft.run()
ifft.realOutputMatrix().hide()
ifft.imaginaryOutputMatrix().hide()

FFT Filters

In this section, it will be assumed that you have a signal displayed in a graph ("Graph1", on its active layer). This signal has a power spectrum with high and low frequencies. You can filter some of these frequencies according to your needs, using a FFTFilter. Here's how you can cut all the frequencies lower than 1 Hz:

filter = FFTFilter(graph("Graph1").activeLayer().curve(0), FFTFilter.HighPass)
filter.setCutoff(1)
filter.run()
Here's how you can cut all the frequencies lower than 1.5 Hz and higher than 3.5 Hz. In the following example the continuous component of the signal is also removed:

filter.setFilterType(FFTFilter.BandPass)
filter.enableOffset(False)
filter.setBand(1.5, 3.5)
filter.run()
Other types of FFT filters available in QtiPlot are: low pass (FFTFilter.LowPass) and band block (FFTFilter.BandBlock).

Fitting

Assuming you have a graph named "Graph1" displaying a curve entitled "Table1_2" on its active layer, a minimal Fit example would be:

f = GaussFit(graph("Graph1").activeLayer().curve(0))
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(curve)

  • LinearSlopeFit(curve)

  • PolynomialFit(curve, degree=2, legend=False)

  • ExponentialFit(curve, growth=False)

  • TwoExpFit(curve)

  • ThreeExpFit(curve)

  • GaussFit(curve)

  • GaussAmpFit(curve)

  • LorentzFit(curve)

  • LogisticFit(curve)

  • SigmoidalFit(curve)

  • PsdVoigt1Fit(curve)

  • PsdVoigt2Fit(curve)

  • MultiPeakFit(curve, xStart, xEnd, profile=MultiPeakFit.Gauss, peaks=1)

    
f = MultiPeakFit(graph("Graph1").activeLayer().curve(0),100,900,MultiPeakFit.PsdVoigt1,3)
    f.guessInitialValues()
    f.setPeakCurvesColor(Qt.green)
    f.generateFunction(True, 1000)
    f.run()
    			
    The following peak profile functions are available: Gauss, Lorentz, PsdVoigt1and PsdVoigt2.
  • NonLinearFit(curve)

    
f = NonLinearFit(layer, curve)
    f.setFormula(formula_string)
    f.save(fileName)
    			
  • PluginFit(curve)

    
f = PluginFit(curve)
    f.load("/Applications/qtiplot.app/Contents/MacOS/fitPlugins/libexp_saturation.dylib")
    			
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().curve(0), 2, 7)
f.fit()
An alternative way to initialize fit objects is to use directly a source data table instead of a plot curve:

f = LinearFit(table("Table1"), "xCol", "yCol", 2, 21) #from start row 2 to end row 21
f.fit()
It is possible to restrict the search range for any of the fit parameters:

f = NonLinearFit(graph("Graph1").activeLayer().curve(0))
f.setFormula("a0+a1*x+a2*x*x")
f.setParameterRange(parameterIndex, start, end)
All the settings of a non-linear fit can be saved to an XML file and restored later one, using this file, for a faster editing process. Here's for example how you can save the above fit function:

f.save("/fit_models/poly_fit.txt")
and how you can use this file during another fitting session, later on:

f = NonLinearFit(graph("Graph1").activeLayer(), "Table1_2")
f.load("/fit_models/poly_fit.txt")
f.fit()
If your script relies on a specific numbering of the fit parameters use setParameters() before setting the formula and switch of automatic detection of the fit parameters when the fit formula is set:

f.setParameters("a2","a0","a1")
f.setFormula("a0+a1*x+a2*x*x",0)

After creating the Fit object and before calling its fit() method, you can set a number of parameters that influence the fit:


f.setDataFromTable(table("Table4"), "w", "energy", 10, 200, True) #change data source (last parameter enables/disables data sorting)
f.setDataFromCurve(curve)			#change data source
f.setDataFromCurve(curveTitle, graph)		#change data source
f.setDataFromCurve(curve, from, to)		#change data source
f.setDataFromCurve(curveTitle, 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, Fit.Direct
f.setTolerance(tolerance)
f.setMaximumIterations(number)
f.scaleErrors(yes = True)
f.setColor("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.residuals()
f.dataSize()
f.numParameters()
f.iterations()
f.status()
print(f.statusInfo(f.status()))
f.parametersTable("params")
f.covarianceMatrix("cov")
Writing the fit results to log is a time consuming operation and, especially when you need to perform a large number of fits, you can disable/enable this functionality using:

setWriteFitResultsToLog(False) # the boolean parameter of this method is True by default
It is possible to customize the display of the fit results in terms of numerical format and precision:

f.setOutputFormat('f')
f.setOutputPrecision(2)

The value of the precision parameter for the setOutputPrecision() function has a different meaning depending on the numeric format. The following format and precision options are available:

'g'

- Decimal or scientific e-notation, whichever is the most concise. The precision represents the maximum number of significant figures in the output (trailing zeroes are omitted).

'f'

- Decimal notation. The precision represents the number of digits after the decimal point.

'e'

- Scientific e-notation where the letter e is used to represent "times ten raised to the power of" and is followed by the value of the exponent. The precision represents the number of digits after the decimal point.

There are a number of statistical functions allowing you to test the goodness of the fit:

f.chiSquare()
f.rSquare()
f.adjustedRSquare()
f.rmse() # Root Mean Squared Error
f.rss()  # Residual Sum of Squares
Also you can display confidence and prediction bands for the fit, based on a custom confidence level:

f.showPredictionLimits(0.95)
f.showConfidenceLimits(0.95)
It is possible to display both confidence and prediction bands with a single and more efficient function call, using the following form:

f.showConfidenceLimits(0.95, True)
The data for both the confidence and prediction bands is stored in the same hidden table. You can interact with this table using the following method:

t = f.confidenceBandsTable()
Alternatively, you can get pointers to the lower and upper confidence and prediction data curves, using the getter functions listed bellow:

lcl = f.lclCurve()
ucl = f.uclCurve()
lpl = f.lplCurve()
upl = f.uplCurve()
Confidence limits for individual fit parameters can be calculated using:

f.lcl(parameterIndex, confidenceLevel)
f.ucl(parameterIndex, confidenceLevel)
where parameterIndexis a value between zero and f.numParameters() - 1.

It is important to know that QtiPlot can generate an analytical formula for the resulting fit curve or a normal plot curve with data stored in a hidden table. You can choose either of these two output options, before calling the fit() instruction, using:


f.generateFunction(True, points=100)

If the first parameter of the above function is set to True, QtiPlot will generate an analytical function curve. If the points parameter is not specified, by default the function will be estimated over 100 points. You can generate a table with the data points of the result fit curve via a call to createTable():

t = f.createTable(False) # the table is hidden
If the first parameter of generateFunction() is set to False, QtiPlot will create a hidden data table containing the same number of points as the data set/curve to be fitted (same abscissae). You can interact with this table and extract the data points of the result fit curve using:

t = f.resultTable()

Some single peak fit functions (GaussFit, GaussAmpFit and LorentzFit) have a list of derived fit parameters that can be calculated after the fit is performed. Bellow there's a small script that demonstrates how to display these derived parameters in a new table:


f = GaussFit(graph("Graph1").activeLayer().curve(0))
f.guessInitialValues()
f.fit()

t = newTable(f.numDerivedParameters(), 3)
c1 = t.column(1)
c1.setName("Parameter")
c2 = t.column(2)
c2.setName("Function")
c3 = t.column(3)
c3.setName("Value")
for i in range(0, t.numRows()):
	c1.setText(i, f.derivedParameterName(i))
	c2.setText(i, f.derivedParameterFunction(i))
	c3.setValue(i, f.derivedParameterValue(i))

t.resizeToShowAll()

Data Fit Example

In the example bellow a hidden table is generated and filled with data and a 2D plot window is created from this table. Afterwards an exponential fit is performed on the displayed data curve:


rows = 100
t = newTable("test", rows, 3)
t.setHidden(True)
c1 = t.column(1)
c1.setName("time")
c2 = t.column(2)
c2.setName("data")
c3 = t.column(3)
c3.setName("weights")
c3.setPlotRole(Table.yErr)
c3.setNormalRandomValues()

for i in range (0, rows):
	ti = i*40.0/(rows - 1.0)
	yi = 1.5 + 5*exp(-0.1*ti) + 0.1*c3.value(i)
	c1.setValue(i, ti)
	c2.setValue(i, yi)
	c3.setValue(i,  0.1*yi)

g = plot(t, ("test_data", "test_weights"), Layer.LineSymbols).activeLayer()

f = ExponentialFit(g.curve(0))
f.setWeightingData(Fit.Instrumental)
f.guessInitialValues()
f.setOutputPrecision(3)
f.fit()
f.showLegend()

Muliple Linear Regression

The script bellow performs a multiple linear regression analysis on a data table:

t = table("Book1")
independentData = ("A", "B", "C")
dependentData = "D"

mlr = MultiLinearRegression(t, independentData, dependentData)
mlr.setWeightingData(Fit.Instrumental, "E")
mlr.scaleErrors()
mlr.fit()
The following script performs a multiple linear regression with the intercept value fixed to 1.5. After the fit operation the script also displays the plot of the fit residuals and information about the input data:

mlr = MultiLinearRegression(t, independentData, dependentData, True, 1.5)
mlr.fit()

# plot fit residuals
mlr.showResiduals()
mlr.showResidualsHistogram()
mlr.showResidualsVsIndependentData()

# plot predicted and input data values
mlr.showDependentDataVsIndex()
mlr.showDependentVsIndependentData()

#print information about the input data sets
print(mlr.inputDataInfo())

Integration

With the same assumptions as above, here's how you can integrate a curve within a given interval:

integral = Integration(graph("Graph1").activeLayer().curve(0), 2, 10)
integral.run()
result = integral.area()
The script bellow shows how to perform an integration on a data set from a table:

i = Integration(table("Table1"), "Table_1", "Table_2", 3, 20, True)# sorted data range from row 3 to 20
i.enableGraphicsDisplay(False)
i.run()
result = i.area()

As you can see from the above examples, the numerical value of the integral can be obtained via the area() function.

Interpolation

The interpolation is used to generate a new data curve with a high number of points from an existing data set. Here's an example:

interpolation = Interpolation(graph("Graph1").activeLayer().curve(0), 2, 10, Interpolation.Linear)
interpolation.setOutputPoints(10000)
interpolation.setColor("green")
interpolation.run()
The simplest interpolation method is the linear method. There are two other methods available: Interpolation.Akimaand Interpolation.Cubic. You can choose the interpolation method using:

interpolation.setMethod(Interpolation.Akima)
Please note that each of these methods require a minimum number of data points in the input curves which is 2, 3 and 5 respectively. If the input curve has fewer data points you might experience a crash, therfore it is strongly advised to perform a check before initializing an Interpolation object. To this end you may use the function Interpolation.minimumDataSize(method):

print Interpolation.minimumDataSize(Interpolation.Linear)
print Interpolation.minimumDataSize(Interpolation.Cubic)
print Interpolation.minimumDataSize(Interpolation.Akima)
It is possible to evaluate the resulting spline at a certain x value:

print interpolation.evalAt(1.5)
The script bellow shows how to perform an interpolation on a data set from an existing table using all three available methods. The script also creates a plot of the original data set together with the interpolated splines:

t = table("Table1") # existing source data table

rows = 600
t2 = newTable(rows, 4) # we create an output table
t2.setColName(1, "x")

interpol = Interpolation(t, "Table1_1", "Table1_2", Interpolation.Linear)
t2.setColName(2, interpol.objectName())

for i in range (1, rows + 1):
	x = 1 + 0.05*i
	t2.setCellData(1, i, x)
	t2.setCellData(2, i, interpol.evalAt(x))

interpol.setMethod(Interpolation.Cubic)
t2.setColName(3, interpol.objectName())

for i in range (1, rows + 1):
	t2.setCellData(3, i, interpol.evalAt(1 + 0.05*i))

interpol.setMethod(Interpolation.Akima)
t2.setColName(4, interpol.objectName())

for i in range (1, rows + 1):
	t2.setCellData(4, i, interpol.evalAt(1 + 0.05*i))

g = qti.app.plot(t, 2, Layer.LineSymbols).activeLayer() # plot input data set
g.addCurves(t2, (2, 3, 4), Layer.Line) # add interpolated splines to the plot layer

Average multiple curves

The example bellow shows how to average two curves having different abscissas, using linear interpolations:

t1 = newTable("Table1", 100, 2)
c = t1.column(1)
c.setRowValues()
c = t1.column(2)
c.setCommand("sin(0.2*i)")
t1.recalculate(c.index())

t2 = newTable("Table2", 50, 2)
c = t2.column(1)
c.setCommand("2*i")
t2.recalculate(c.index())
c = t2.column(2)
c.setCommand("cos(0.2*i)")
t2.recalculate(c.index())

l = plot(t1, 2, Layer.LineSymbols).activeLayer()
c2 = l.insertCurve(t2, "Table2_2",  Layer.LineSymbols)
c2.setPen(QPen(Qt.red))
c2.setSymbol(PlotSymbol(PlotSymbol.Rect, QBrush(Qt.red), QPen(Qt.red,1.5), QSize(7,7)))

interpol1 = Interpolation(l.curve(0), Interpolation.Linear)
interpol2 = Interpolation(c2, Interpolation.Linear)

rows = 1000
t3 = newTable("Table3", rows, 2)
t3.setColName(2, "Average")
for i in range (1, rows + 1):
	x = 0.1*i
	t3.setCellData(1, i, x)
	t3.setCellData(2, i, 0.5*(interpol1.evalAt(x) + interpol2.evalAt(x)))

c3 = l.insertCurve(t3, "Table3_Average",  Layer.Line)
c3.setPen(QPen(Qt.green, 2))

Smoothing

Assuming you have a graph named Graph1with an irregular curve entitled Table1_2(on its active layer). You can smooth this curve using a SmoothFilter:

smooth = SmoothFilter(graph("Graph1").activeLayer().curve(0), SmoothFilter.Average)
smooth.setSmoothPoints(10)
smooth.run()
The default smoothing method is the mowing window average. Other smoothing methods are the SmoothFilter.FFT, SmoothFilter.Lowessand SmoothFilter.SavitzkyGolay. Here's an example of how to use the last two methods:

smooth.setMethod(SmoothFilter.Lowess)
smooth.setLowessParameter(0.2, 2)
smooth.run()

smooth.setSmoothPoints(5,5)
smooth.setMethod(SmoothFilter.SavitzkyGolay)
smooth.setPolynomOrder(9)
smooth.run()

Find Peaks

A FindPeaksFiltercan be used to search the positions of the peaks in a data curve. Assuming you have an input data table named Table1, containing two columns, the following script demonstrates the usage of this type of filter:

g = qti.app.plot(table("Table1"), 2, Layer.Line).activeLayer() # plot input data set

f = FindPeaksFilter(g.curve(0))
f.setPeakFindingAlgorithm(FindPeaksFilter.FirstDerivative)
f.setDirection(FindPeaksFilter.Negative) # only search for negative peaks (local minima)

# smooth the 1st derivative before performing the search
f.setMethod(SmoothFilter.Average)
f.setSmoothPoints(3)

f.setThreshold(15) # set the detection threshold in percentages

f.setRecalculateMode(Filter.AutoRecalculate)
f.enableGraphicsDisplay(True, g)
f.run()

c = f.resultCurve()
c.setLineStyle(PlotCurve.Sticks)

g.addFilter(f)

#print the results
for i in range (0, f.peakCount()):
	print f.center(i), f.height(i)

From the example above you may notice that it is possible to smooth the input curve using the data smoothing methods described in the previous section (Smoothing) before performing the actual search for the local extrema.

The available algorithms for searching the peaks are: FindPeaksFilter.QuickSearch and FindPeaksFilter.FirstDerivative. A detection threshold for the local extrema can be set using the setThreshold function and represents a percentage of the difference between the maximum and minimum values of the input data. The default value is 10 %.

Statistics

Descriptive Statistics


stats = Statistics("Table1_2", 0, 20, True) # sorted data range
stats.run()

print "N = %d" % stats.dataSize()
print "Degrees of freedom = %d" % stats.dof()
print "Sorted = ", stats.hasSortedData()
print "Minimum = %f" % stats.minValue()
print "Maximum = %f" % stats.maxValue()
print "Mean = %f" % stats.mean()
print "Median = %f" % stats.median()
print "Q1 (1st quartile) = %f" % stats.q1()
print "Q3 (3rd quartile) = %f" % stats.q3()
print "Interquartile range (Q3 - Q1) = %f" % stats.iqr()
print "D1 (1st decile) = %f" % stats.quantile(0.1)
print "D9 (9th decile) = %f" % stats.quantile(0.9)
print "Variance = %f" % stats.variance()
print "Standard Deviation = %f" % stats.standardDeviation()
print "Standard Error = %f" % stats.standardError()
print "Kurtosis = %f" % stats.kurtosis()
print "Skewness = %f" % stats.skewness()

For all the statistic tests below, once you have created a test object, you can use the following methods:


test.showResultsLog(False) # disable the logging of the results
test.showDescriptiveStatistics(False) # disable the display of the descriptive statistics results
test.run()

print test.statistic()
print test.pValue()
print test.testValue()
print test.tail()
print test.logInfo()

t = test.resultTable("MyResultTable") # Returns a pointer to the table created to display the results (the table name is optional)

If the statistic test is performed on two datasets (samples), it is possible to get a reference to the Statistics object corresponding to the second sample:


s2 = test.sample2()
print s2.colName()
print s2.dataSize()

Hypothesis Testing - Student's t-Test


test = tTest(15.2, 0.05, "Table1_2") # One sample test, test mean = 15.2, significance level = 0.05
test.run()

test.setTestValue(15.0)
test.setSignificanceLevel(0.5)
test.setTail(StatisticTest.Left) # or StatisticTest.Right, or StatisticTest.Both (default value)
test.run()

print test.t() # same as test.statistic()
print test.dof() # degrees of freedom
print test.power(0.5)
print test.power(0.5, 50) # alternative sample size = 50
print test.pValue()
print test.lcl(90) # lower confidence limit for mean
print test.ucl(90) # upper confidence limit for mean

test = tTest(15.2, 0.05, "Table1_1", "Table1_2", True) # Two sample paired test
print test.logInfo()

test = tTest(15.2, 0.05, "Table1_1", "Table1_2") # Two sample independent test
test.run()

test.setSample1("Table2_1")
test.setSample2("Table3_2", True) # Two sample paired test
test.run()

One Sample Test for Variance (Chi-Square Test)


test = ChiSquareTest(88.2, 0.05, "Table1_2") # Test variance = 88.2, significance level = 0.05
test.run()

print test.chiSquare() # same as test.statistic()
print test.pValue()
print test.logInfo()

Two Sample Kolmogorov-Smirnov Test


test = KolmogorovSmirnovTest("Book1_B", "Book1_C")
test.setSignificanceLevel(0.01)
test.run()

print test.statistic()
print test.pValue() # probability
print test.logInfo()

Kruskal-Wallis Anova


test = KruskalWallisAnova(['Table1_1', 'Table2_2', 'Table3_3'])
test.run()

print test.statistic()
print test.pValue()

Kruskal-Wallis Anova


test = KruskalWallisAnova(['Table1_1', 'Table2_2', 'Table3_3'])# or
# test = KruskalWallisAnova(table("Table1").columns()) # add all table columns to the test
test.run()

print test.statistic()
print test.pValue()

Mood's Median Test


test = MoodsMedianTest(['Table1_1', 'Table2_2', 'Table3_3']) # or
# test = MoodsMedianTest(table("Table1").columns(Table.Y)) # run test on all Y columns in "Table1"
test.run()

print test.statistic()
print test.pValue()
print test.testMedian() # median of combined samples

Mann-Whitney Test


test = MannWhitneyTest("Book1_B", "Book1_C")
test.run()

print test.statistic()
print test.pValue()
print test.logInfo()

One Sample Wilcoxon Signed Rank Test


test = WilcoxonTest("Table_1")
test.setTestValue(3.7) # set test median
test.setTail(StatisticTest.Left) # perform a lower-tailed test
test.run()
print test.statistic() # W
print test.zValue() # Z
print test.pValue() # exact probability
print test.normalPValue() # approx. probability

Paired Sample Wilcoxon Signed Rank Test


test = WilcoxonTest("Table1_1", "Table1_2")
test.setTail(StatisticTest.Right) # perform an upper-tailed test
test.run()
print test.statistic() # W
print test.zValue() # Z
print test.pValue() # exact probability
print test.normalPValue() # approx. probability
print test.ties() # the number of equal values
print test.reducedDataSize() # sample size - ties
print test.negativeRanks() # the number of negative ranks
print test.statistic() -  test.negativeRanks() # the number of positive ranks

Sign Test


test = SignTest("Table1_A", "Table1_B")
test.setTail(StatisticTest.Right) # perform an upper-tailed test
test.run()
print test.statistic() # number of positive differences
print test.zValue()
print test.pValue() # probability
print test.ties() # number of equal values
print test.reducedDataSize() # sample size - ties

Normality Test (Shapiro - Wilk)


test = ShapiroWilkTest("Table3_1")
test.setSignificanceLevel(0.1)
test.run()

print test.w() # same as test.statistic()
print test.pValue()
print test.logInfo()

One-Way ANOVA


test = Anova()
test.setSignificanceLevel(0.1) # default level is 0.05
test.addSample("Table1_1")
test.addSample("Table1_2")
test.addSample("Table1_3")
test.enablePostHocTest(Anova.Tukey | Anova.Bonferroni)
test.enablePostHocTestTables(True) # generate result tables
test.run()
t1 = test.posthocTestTable(Anova.Tukey)
t1.show()
t2 = test.posthocTestTable(Anova.Bonferroni)
t2.show()
print test.fStat() # F statistic = ssm/sse (same as test.statistic())
print test.pValue()
print test.ssm() # "between-group" sum of squares
print test.sse() # "within-group" sum of squares
print test.sst() # total sum of squares

test.showDescriptiveStatistics(False)
print test.logInfo()

The folowing post-hoc tests are currently implemented in QtiPlot: Anova.Tukey, Anova.Bonferroni, Anova.Sidak, Anova.Fisher, Anova.Scheffe, Anova.HolmBonferroni and Anova.HolmSidak.

Two-Way ANOVA


test = Anova(True)
test.addSample("Table1_1", 1, 1) # Level factor A = 1, level factor B = 1
test.addSample("Table1_2", 1, 2) # Level factor A = 1, level factor B = 2
test.addSample("Table1_3", 2, 1) # Level factor A = 2, level factor B = 1
test.addSample("Table1_4", 2, 2) # Level factor A = 2, level factor B = 2
test.setAnovaTwoWayModel(2) # Fixed model = 0, Random model = 1, Mixed model = 2
test.enablePostHocTest(Anova.Tukey | Anova.Bonferroni | Anova.Fisher)
test.enablePostHocTestTables(True) # generate result tables
test.enableInteractions(False) # Disable calculation of interaction between factors
test.run()

test.posthocTestTable(Anova.Tukey).show()
test.posthocTestTable(Anova.Bonferroni).show()
test.posthocTestTable(Anova.Fisher).show()

print test.fStatA() # F statistic for factor A
print test.fStatB() # F statistic for factor B
print test.fStatAB() # F statistic for the interaction
print test.fStatModel() # F statistic for the model
print test.pValueA() # P value for factor A
print test.pValueB() # P value for factor B
print test.pValueAB() # P value for the interaction
print test.pValueModel() # P value for the model
print test.ssa() # sum of squares for factor A
print test.ssb() # sum of squares for factor B
print test.ssab() # sum of squares for the interaction
print test.ssm() # sum of squares for the model
print test.msa() # mean square value for factor A
print test.msb() # mean square value for factor B
print test.msab() # mean square value for the interaction
print test.msm() # mean square value for the model

print test.logInfo()

The folowing post-hoc tests are currently implemented in QtiPlot: Anova.Tukey, Anova.Bonferroni, Anova.Sidak, Anova.Fisher, Anova.Scheffe, Anova.HolmBonferroni and Anova.HolmSidak.

Working with Notes

The following functions are available when dealing with multi-tab notes:


setAutoexec(on = True)

text()
setText(text)

exportPDF(fileName)
saveAs(fileName)
importASCII(fileName)

showLineNumbers(on = True)

setFont(QFont f)
setTabStopWidth(int length)

tabs()
addTab()
removeTab(tabIndex)
renameTab(tabIndex, title)

e = editor(int index)
e = currentEditor()
It is possible to trigger the execution of a script in a Notes window tab:

e = note("Notes1").currentEditor()
e.executeAll()
# Check if the script is still running:
print e.isRunning()
It is also possible to program a script to be run periodically:

e = note("Notes1").currentEditor()
e.executePeriodically(3.5) # running interval is set to 3.5 seconds
print e.isRunningPeriodically() # should print True
print e.runningInterval() # should print 3.5
In order to stop the periodic execution of a script you may use:

note("Notes1").currentEditor().stopPeriodicExecution()

Using PyQt's dialogs and classes

Let's assume that you have a lot of ASCII data files to analyze. Furthermore, let's suppose that these files were created during several series of measurements, each measurement generating a set of files identified by a certain string in the file name, like for example: "disper1". In order to analyze these files, you need first of all to import them into tables. The following code snippet shows how to automate this task using PyQt dialogs and convenience classes:

# Pop-up a file dialog allowing to chose the working folder:
dirPath = QFileDialog.getExistingDirectory(qti.app, "Choose Working Folder", "/test/")
# Create a folder object using Qt's QDir class:
folder = QDir(dirPath)
# Pop-up a text input dialog allowing to chose the file naming pattern:
namePattern,ok = QInputDialog.getText(qti.app,"Pattern","Text:",QLineEdit.Normal,"disper1")
if ok: # get the list of file names in the working directory containing the above pattern
	fileNames = folder.entryList(["*" + namePattern + "*.dat"])
	for i in range (0, len(fileNames)): # import each file into a new project table
		newTable().importASCII(dirPath + "/" + fileNames[i],";")
For a detailed description of all the dialogs and utility classes provided by Qt/PyQt please take a look at the PyQt documentation.

Using Qt Designer for easy creation of custom user dialogs

Writing and designing user dialogs can be a very complicated task. The QtDesigner application provided by the Qt framework makes it easy and very pleasant. It allows you to design widgets, dialogs or complete main windows using on-screen forms and a simple drag-and-drop interface. Qt Designer uses XML .ui files to store designs. Once you have finished the design process you can load and use an .ui file in your Python scripts with the help of the uicmodule.

As an example, suppose that we have created a test dialog containing an input QDoubleSpinBox called "valueBox" and a QPushButton called "okButton". On pressing this button, we would like to create a new table displaying the input value in its first cell. We have saved this dialog to a file called "myDialog.ui". A minimalistic approach is shown in the small script below:


def createTable():
	t = newTable()
	t.setCell(1, 1, ui.valueBox.value())

ui = uic.loadUi("myDialog.ui")
ui.okButton.clicked.connect(createTable)
ui.show()
Once you understand how to create a custom dialog using the basic controls provided by Qt you can get rid of the Qt Designer tool. For example, here's how you create from scratch, in Python, the above "myDialog.ui" form:

class myDialog:
	def __init__(self):
		d = QDialog(qti.app)
		d.setWindowTitle("myDialog")
		grid = QHBoxLayout(d)

		self.valueBox = QDoubleSpinBox()
		grid.addWidget(self.valueBox)

		self.okButton = QPushButton("OK")
		self.okButton.clicked.connect(self.createTable)
		grid.addWidget(self.okButton)

		d.show()

	def createTable(self):
		t = newTable()
		t.setCell(1, 1, self.valueBox.value())

dialog = myDialog()
For more details about how to use .ui files in your Python scripts please read the PyQt documentation.

Task automation example

Below you can find a detailed example showing how to completely automatize tasks in QtiPlot. It can be used in order to verify the accuracy of the curve fitting algorithms in QtiPlot. The test data files that must be used with this example were retrieved from the Statistical Reference Datasets Project of the National Institute of Standards and Technology (NIST). In order to run this example, you first need to download und unzip the nonlinear regression test files from the QtiPlot website.

Please note that the script can be used as it is only if QtiPlot was built using the Qt 5 library.


import sys

# Pop-up a file dialog allowing to chose the data folder:
dirPath = QFileDialog.getExistingDirectory(qti.app, "Choose Strd-NIST Data Folder")

saveout = sys.stdout # backup default option for sys.stdout

# create a log file in the data folder
resultsPath = dirPath + "/" + "results.txt"
fsock = open(resultsPath, "w")
sys.stdout = fsock

# make sure that the decimal separator is the dot character
qti.app.setLocale(QtCore.QLocale.c())

# Create a folder object using Qt's QDir class:
folder = QDir(dirPath)
# get the list of *.dat files from the data folder
files = folder.entryList(["*.dat"])
count = len(files)
for i in range (0, count):
	name = files[i]
	path = dirPath + "/" + name
	print(str(i + 1) + ") Parsing data file: " + path + " ...")

	file = QFile(path)
	if file.open(QIODevice.ReadOnly):
		ts = QTextStream(file)
		name = name.replace(".dat", "")
		changeFolder(addFolder(name)) #create a new folder and move to it
		formula = ""
		parameters = 0
		initValues = list()
		certifiedValues = list()
		standardDevValues = list()
		xLabel = "X"
		yLabel = "Y"

		while (ts.atEnd() == False):
			s = ts.readLine().strip()

			if (s.count("(y = ")):
				lst = s.split("=")
				yLabel = lst[1].replace(")", "")

			if (s.count("(x = ")):
				lst = s.split("=")
				xLabel = lst[1].replace(")", "")

			if (s.count("Model:")):
				s = ts.readLine().strip()
				lst = s.split()
				s = lst[0]
				parameters = int(s)

				ts.readLine()
				if (name == "Roszman1"):
					ts.readLine()
					formula = ts.readLine().strip()
				else:
					formula = (ts.readLine() + ts.readLine() + ts.readLine()).strip()

				lst = formula.split(" = ")
				formula = lst[1].strip()
				formula = formula.replace("**", "^")
				formula = formula.replace("arctan", "atan")
				formula = formula.replace("[", "(")
				formula = formula.replace("]", ")")
				formula = formula.replace("  +  e", "")

			if (s.count("Starting")):
				ts.readLine()
				ts.readLine()
				for i in range (1, parameters + 1):
					s = ts.readLine().strip()
					lst = s.split(" = ")
					s = lst[1].strip()
					lst = s.split()
					initValues.append(float(lst[1]))
					certifiedValues.append(lst[2])
					standardDevValues.append(lst[3])

			if (s.count("Data:") and s.count("y") and s.endswith("x")):
				row = 0
				t = newTable(name, 300, 2)
				t.setColName(1, "x")
				t.setColName(2, "y")
				while (ts.atEnd() == False):
					row = row + 1
					s = ts.readLine().strip()
					lst = s.split()
					t.setText(1, row, lst[1])
					t.setText(2, row, lst[0])

				g = plot(t, t.colName(2), Layer.Scatter).activeLayer()
				g.setTitle("Data set: " + name + ".dat")
				g.setAxisTitle(Layer.Bottom, xLabel)
				g.setAxisTitle(Layer.Left, yLabel)

				f = NonLinearFit(g.curve(0))
				f.setObjectName(name)
				f.setFormula(formula)
				f.scaleErrors()
				for i in range (0, parameters):
					f.setInitialValue(i, initValues[i])

				f.fit()
				print("\nQtiPlot Results:\n")
				print("Table: " + t.objectName())
				print(f.legendInfo().replace("<b>", "").replace("</b>", ""))

				print("\nCertified Values:")

				paramNames = f.parameterNames()
				for i in range (0, parameters):
					print('%s = %s +/- %s' % (paramNames[i], certifiedValues[i], standardDevValues[i]))

				print("\nDifference with QtiPlot results:")
				results = f.results()
				for i in range (0, parameters):
					diff = fabs(results[i] - float(certifiedValues[i]))
					print('db%d = %6E' % (i+1, diff))
				print("\n")

				file.close()
				changeFolder(rootFolder())

print("Done parsing " + str(count) + " files.\n")
fsock.close()# close output results file
sys.stdout = saveout # restore initial sys.stdout

resNote = newNote("ResultsLog")
resNote.importASCII(resultsPath)
resNote.showMaximized()

saveProjectAs(dirPath + "/" + "StRD_NIST.qti")

Scope Changes

In recent versions the scope rules were changed to match standard Python:

If a script has any "global" declaration outside of a function, QtiPlot uses the old scope rules. Existing scripts should keep working, but for best results, update your scripts:

QtiPlot/Python API

The complete QtiPlot/Python API can be consulted here.

PyQt Class Reference

The complete PyQt class reference can be consulted here.